feat(themeCreator): add svelte theme creator

This commit is contained in:
sethburkart123
2024-09-20 10:37:26 +10:00
parent 548dcbf34e
commit 6267a77a71
12 changed files with 377 additions and 144 deletions
+2
View File
@@ -67,6 +67,7 @@
"@types/sortablejs": "^1.15.8",
"@types/uuid": "^9.0.8",
"@types/webextension-polyfill": "^0.10.7",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
"classnames": "^2.5.1",
"color": "^4.2.3",
@@ -75,6 +76,7 @@
"kolorist": "^1.8.0",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"million": "^3.1.11",
"motion": "^10.18.0",
"postcss": "^8.4.45",
"publish-browser-extension": "^2.2.1",
-1
View File
@@ -37,7 +37,6 @@ import injectedCSS from '@/css/injected.scss?inline'
import documentLoadCSS from '@/css/documentload.scss?inline'
import renderSvelte from '@/svelte-interface/main'
import Settings from '@/svelte-interface/pages/settings.svelte'
import { renderStore } from './seqta/ui/renderStore'
import { settingsPopup } from './svelte-interface/hooks/SettingsPopup'
let SettingsClicked = false
+70 -27
View File
@@ -1,9 +1,9 @@
@charset "UTF-8";
@import url('https://fonts.googleapis.com/css?family=Rubik:300,400,500,600');
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
@import './injected/sidebar-animation.scss';
@import './injected/theme.scss';
@import './injected/transparency.scss';
@import "./injected/sidebar-animation.scss";
@import "./injected/theme.scss";
@import "./injected/transparency.scss";
:root {
background: var(--better-main) !important;
@@ -16,7 +16,12 @@
}
body,
.legacy-root input, .legacy-root textarea, .legacy-root button, .legacy-root select, .legacy-root option, .legacy-root .input,
.legacy-root input,
.legacy-root textarea,
.legacy-root button,
.legacy-root select,
.legacy-root option,
.legacy-root .input,
html {
font-family: Rubik, sans-serif !important;
}
@@ -35,7 +40,8 @@ html {
height: 100%;
visibility: visible !important;
}
#themeCreatorIframe {
#themeCreator {
position: fixed;
right: 0;
height: 100%;
@@ -195,7 +201,8 @@ html {
background: unset;
}
.legacy-root button:active, .legacy-root a:active:not(.cke_combo_button) {
.legacy-root button:active,
.legacy-root a:active:not(.cke_combo_button) {
background-image: unset !important;
}
@@ -205,7 +212,8 @@ html {
}
.dark .dashboard section {
input, select {
input,
select {
background: rgba(255, 255, 255, 0.1);
}
}
@@ -227,7 +235,7 @@ html {
input,
select {
border: transparent;
background: rgba(0, 0, 0, .1);
background: rgba(0, 0, 0, 0.1);
color: var(--text-primary);
}
@@ -269,7 +277,7 @@ html {
color: var(--text-primary);
}
ul.magicDelete > li:hover {
background: rgba(0, 0, 0, .1);
background: rgba(0, 0, 0, 0.1);
}
.dashlet-notes > .editor {
background: unset;
@@ -292,7 +300,8 @@ ul.magicDelete > li.deleting {
height: 100%;
}
#media-container video, #media-container img {
#media-container video,
#media-container img {
width: 100%;
height: 100%;
object-fit: cover;
@@ -372,14 +381,16 @@ ul.magicDelete > li.deleting {
position: relative;
&::before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 3px;
background: var(--item-colour, transparent);
transition: width 100ms, transform 0.3s ease;
transition:
width 100ms,
transform 0.3s ease;
border-radius: 8px 0 0 8px;
}
@@ -481,7 +492,7 @@ ol:has(.MessageList__avatar___2wxyb svg) {
.quickbar .actions [title="Choose a colour"] > svg {
scale: 0.9;
}
.quickbar[data-yiq='light'] .actions {
.quickbar[data-yiq="light"] .actions {
color: white !important;
}
.singleSelect > li {
@@ -1328,7 +1339,9 @@ div > ol:has(.uiFileHandlerWrapper) {
height: 25px;
width: 24px;
}
.notifications__notifications___3mmLY > button > .notifications__bubble___1EkSQ {
.notifications__notifications___3mmLY
> button
> .notifications__bubble___1EkSQ {
background: var(--better-alert-highlight);
width: 25px;
height: 25px;
@@ -1534,7 +1547,9 @@ iframe.userHTML {
.Collapsible__Collapsible___3O8P3 > .Collapsible__header___-Afvq {
background: none;
}
.AssessmentList__AssessmentList___1GdCl > .AssessmentList__searchFilter___3N70o + .AssessmentList__items___3LcmQ {
.AssessmentList__AssessmentList___1GdCl
> .AssessmentList__searchFilter___3N70o
+ .AssessmentList__items___3LcmQ {
color: var(--text-primary);
}
.Thermoscore__Thermoscore___2tWMi {
@@ -1630,10 +1645,13 @@ ul {
> .SelectedAssessment__clearBtn___21D85 {
background: var(--better-main);
}
.SelectedAssessment__SelectedAssessment___3Bu5D > .SelectedAssessment__meta___1gq_y {
.SelectedAssessment__SelectedAssessment___3Bu5D
> .SelectedAssessment__meta___1gq_y {
border-bottom: 1px solid var(--better-main);
}
.TabSet__TabSet___Vo-SZ > ol.TabSet__tabs___1RRZk > li.TabSet__selected___1psfF {
.TabSet__TabSet___Vo-SZ
> ol.TabSet__tabs___1RRZk
> li.TabSet__selected___1psfF {
border-bottom-color: var(--better-main);
}
.TabSet__TabSet___Vo-SZ > ol.TabSet__tabs___1RRZk {
@@ -1758,7 +1776,15 @@ div.entry.class[style*="left: 46.5%"] {
div.entry.class[style*="width: 46.5%"] {
width: 50% !important;
}
.timetablepage .dailycal > .content > .wrapper > .days > tbody > tr > td > .entriesWrapper {
.timetablepage
.dailycal
> .content
> .wrapper
> .days
> tbody
> tr
> td
> .entriesWrapper {
min-width: 0;
width: auto !important;
}
@@ -1952,7 +1978,6 @@ div.bar.flat {
.cke_toolbox > .cke_toolbar .cke_button_on {
background-color: #3d3d3e !important;
}
}
.legacy-root input.singleSelect:focus {
background: var(--auto-background);
@@ -1996,7 +2021,15 @@ body {
.forumView .assessment {
background: var(--better-main);
}
.dailycal > .content > .wrapper > .days > tbody > tr > td > .entriesWrapper > .entry {
.dailycal
> .content
> .wrapper
> .days
> tbody
> tr
> td
> .entriesWrapper
> .entry {
padding: 3px;
}
.Viewer__Viewer___32BH- {
@@ -2035,7 +2068,9 @@ li.MessageList__unread___3imtO {
border-radius: 1600px;
}
.MessageList__MessageList___3DxoC > ol > li.MessageList__selected___1SJNz.MessageList__unread___3imtO {
.MessageList__MessageList___3DxoC
> ol
> li.MessageList__selected___1SJNz.MessageList__unread___3imtO {
box-shadow: none;
}
@@ -2055,7 +2090,9 @@ li.MessageList__unread___3imtO {
transition: width 0.1s;
}
.MessageList__MessageList___3DxoC > ol > li.MessageList__unread___3imtO::before {
.MessageList__MessageList___3DxoC
> ol
> li.MessageList__unread___3imtO::before {
width: 3px;
}
.connectedNotificationsWrapper > div > button {
@@ -2129,7 +2166,10 @@ li.MessageList__unread___3imtO {
cursor: pointer;
}
.dark .MessageList__MessageList___3DxoC > ol > li.MessageList__selected___1SJNz {
.dark
.MessageList__MessageList___3DxoC
> ol
> li.MessageList__selected___1SJNz {
background: var(--background-secondary);
}
@@ -2620,7 +2660,9 @@ li.MessageList__unread___3imtO {
width: 100%;
display: flex;
flex-direction: column;
box-shadow: inset 0px 6px 0 var(--item-colour, transparent), inset 0px 40px 50px -40px rgba(179, 179, 179, 0.9);
box-shadow:
inset 0px 6px 0 var(--item-colour, transparent),
inset 0px 40px 50px -40px rgba(179, 179, 179, 0.9);
transition: 200ms;
position: relative;
height: 15em;
@@ -2629,7 +2671,9 @@ li.MessageList__unread___3imtO {
font-family: Rubik, sans-serif;
}
.dark .day {
box-shadow: inset 0px 6px 0 var(--item-colour, transparent), inset 0px 40px 50px -40px rgba(0,0,0,0.9);
box-shadow:
inset 0px 6px 0 var(--item-colour, transparent),
inset 0px 40px 50px -40px rgba(0, 0, 0, 0.9);
}
.clickable {
cursor: pointer;
@@ -2946,7 +2990,6 @@ li.MessageList__unread___3imtO {
width: 90%;
border-radius: 16px;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
}
.whatsnewTextContainer {
display: flex;
+66 -57
View File
@@ -1,73 +1,79 @@
import browser from "webextension-polyfill";
import renderSvelte from "@/svelte-interface/main"
import themeCreator from "@/svelte-interface/pages/themeCreator.svelte"
import { unmount } from "svelte"
let themeCreatorSvelteApp: any = null
/**
* Open the Theme Creator sidebar, it is an embedded page loaded similar to the extension popup
* @param themeID - The ID of the theme to load in the Theme Creator
* @returns void
*/
export function OpenThemeCreator( themeID: string = '' ) {
CloseThemeCreator();
export function OpenThemeCreator(themeID: string = "") {
CloseThemeCreator()
const width = '310px';
const width = "310px"
const themeCreatorIframe: HTMLIFrameElement = document.createElement('iframe');
themeCreatorIframe.src = `${browser.runtime.getURL('interface/index.html')}${ themeID != '' ? `?themeID=${themeID}` : '' }#themeCreator`;
themeCreatorIframe.id = 'themeCreatorIframe';
themeCreatorIframe.setAttribute('allowTransparency', 'true');
themeCreatorIframe.setAttribute('excludeDarkCheck', 'true');
themeCreatorIframe.style.border = 'none';
themeCreatorIframe.style.width = width;
const themeCreatorDiv: HTMLDivElement = document.createElement("div")
themeCreatorDiv.id = "themeCreator"
themeCreatorDiv.style.width = width
const mainContent = document.querySelector('#container') as HTMLDivElement;
if (mainContent) mainContent.style.width = `calc(100% - ${width})`;
const shadow = themeCreatorDiv.attachShadow({ mode: "open" })
themeCreatorSvelteApp = renderSvelte(themeCreator, shadow, {
themeID: themeID,
})
const mainContent = document.querySelector("#container") as HTMLDivElement
if (mainContent) mainContent.style.width = `calc(100% - ${width})`
// close button
const closeButton = document.createElement('button');
closeButton.classList.add('themeCloseButton');
closeButton.textContent = '×';
closeButton.addEventListener('click', CloseThemeCreator);
document.body.appendChild(closeButton);
const closeButton = document.createElement("button")
closeButton.classList.add("themeCloseButton")
closeButton.textContent = "×"
closeButton.addEventListener("click", CloseThemeCreator)
document.body.appendChild(closeButton)
const resizeBar = document.createElement('div');
resizeBar.classList.add('resizeBar');
resizeBar.style.right = '307.5px';
const resizeBar = document.createElement("div")
resizeBar.classList.add("resizeBar")
resizeBar.style.right = "307.5px"
let isDragging = false;
let currentX: number;
let isDragging = false
let currentX: number
const mouseDownHandler = (e: MouseEvent) => {
isDragging = true;
currentX = e.clientX;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
document.body.style.userSelect = 'none';
themeCreatorIframe.style.pointerEvents = 'none'; // Disable pointer events on iframe during resize
};
isDragging = true
currentX = e.clientX
document.addEventListener("mousemove", mouseMoveHandler)
document.addEventListener("mouseup", mouseUpHandler)
document.body.style.userSelect = "none"
themeCreatorDiv.style.pointerEvents = "none"
}
const mouseMoveHandler = (e: MouseEvent) => {
if (!isDragging) return;
const dx = e.clientX - currentX;
currentX = e.clientX;
const newWidth = Math.min(Math.max(310, themeCreatorIframe.offsetWidth - dx), 600);
themeCreatorIframe.style.width = `${newWidth}px`;
mainContent.style.width = `calc(100% - ${newWidth}px)`;
resizeBar.style.right = `${newWidth - 2.5}px`;
};
if (!isDragging) return
const windowWidth = window.innerWidth
const newWidth = Math.min(Math.max(310, windowWidth - e.clientX), 600)
themeCreatorDiv.style.width = `${newWidth}px`
mainContent.style.width = `calc(100% - ${newWidth}px)`
resizeBar.style.right = `${newWidth - 2.5}px`
currentX = e.clientX
}
const mouseUpHandler = () => {
isDragging = false;
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
document.body.style.userSelect = '';
themeCreatorIframe.style.pointerEvents = 'auto';
};
isDragging = false
document.removeEventListener("mousemove", mouseMoveHandler)
document.removeEventListener("mouseup", mouseUpHandler)
document.body.style.userSelect = ""
themeCreatorDiv.style.pointerEvents = "auto"
}
resizeBar.addEventListener('mousedown', mouseDownHandler);
resizeBar.addEventListener('mouseover', () => resizeBar.style.opacity = '1');
resizeBar.addEventListener('mouseout', () => resizeBar.style.opacity = '0');
resizeBar.addEventListener("mousedown", mouseDownHandler)
resizeBar.addEventListener("mouseover", () => (resizeBar.style.opacity = "1"))
resizeBar.addEventListener("mouseout", () => (resizeBar.style.opacity = "0"))
document.body.appendChild(themeCreatorIframe);
document.body.appendChild(resizeBar);
document.body.appendChild(themeCreatorDiv)
document.body.appendChild(resizeBar)
}
/**
@@ -75,14 +81,17 @@ export function OpenThemeCreator( themeID: string = '' ) {
* @returns void
*/
export function CloseThemeCreator() {
const themeCreatorIframe = document.getElementById('themeCreatorIframe');
const closeButton = document.querySelector('.themeCloseButton') as HTMLButtonElement;
const resizeBar = document.querySelector('.resizeBar') as HTMLDivElement;
const themeCreator = document.getElementById("themeCreator")
const closeButton = document.querySelector(
".themeCloseButton",
) as HTMLButtonElement
const resizeBar = document.querySelector(".resizeBar") as HTMLDivElement
if (themeCreatorIframe) themeCreatorIframe.remove();
if (closeButton) closeButton.remove();
if (resizeBar) resizeBar.remove();
if (themeCreatorSvelteApp) unmount(themeCreatorSvelteApp)
if (themeCreator) themeCreator.remove()
if (closeButton) closeButton.remove()
if (resizeBar) resizeBar.remove()
const mainContent = document.querySelector('#container') as HTMLDivElement;
if (mainContent) mainContent.style.width = '100%';
const mainContent = document.querySelector("#container") as HTMLDivElement
if (mainContent) mainContent.style.width = "100%"
}
@@ -0,0 +1,4 @@
<script lang="ts">
</script>
<div>Code Editor Here</div>
@@ -1,8 +1,8 @@
.dark {
div:has(> #rbgcp-wrapper) {
background: transparent !important;
}
.dark {
#rbgcp-wrapper {
div[style="padding-top: 11px; position: relative;"] div {
color: white !important;
@@ -31,11 +31,14 @@
#rbgcp-radial-btn,
#rbgcp-linear-btn {
&[style*="background: white;"] {
background-color: #28282B !important;
background-color: #28282b !important;
}
svg {
path, g, polyline, circle {
path,
g,
polyline,
circle {
stroke: white !important;
fill: transparent !important;
}
@@ -5,14 +5,17 @@
import { animate, spring } from 'motion';
import { delay } from '@/seqta/utils/delay.ts'
const { hidePicker } = $props<{
hidePicker: () => void
const { hidePicker, standalone = false } = $props<{
hidePicker?: () => void,
standalone?: boolean
}>();
let background: HTMLDivElement;
let content: HTMLDivElement;
const closePicker = async () => {
if (standalone) return;
animate(
content,
{ scale: [1, 0.4], opacity: [1, 0] },
@@ -25,12 +28,13 @@
{ easing: [0.4, 0, 0.2, 1] }
);
await delay(400);
hidePicker();
}
onMount(() => {
if (standalone) return;
animate(
background,
{ opacity: [0, 1] },
@@ -43,11 +47,17 @@
{ easing: spring({ stiffness: 400, damping: 30 }) }
);
document.addEventListener('keydown', (e) => {
const handleEscapeKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
closePicker();
}
});
};
document.addEventListener('keydown', handleEscapeKey);
return () => {
document.removeEventListener('keydown', handleEscapeKey);
};
});
function handleBackgroundClick(event: MouseEvent) {
@@ -57,6 +67,11 @@
}
</script>
{#if standalone}
<div class="h-auto rounded-xl overflow-clip">
<ReactAdapter el={ColourPicker} />
</div>
{:else}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
bind:this={background}
@@ -71,3 +86,4 @@
<ReactAdapter el={ColourPicker} />
</div>
</div>
{/if}
@@ -0,0 +1,3 @@
<script lang="ts"></script>
<div class='w-full h-0.5 my-4 bg-zinc-200 dark:bg-zinc-700'></div>
+13 -12
View File
@@ -1,23 +1,24 @@
import styles from './index.css?inline';
import { mount } from 'svelte';
import type { ComponentType } from 'svelte';
import styles from "./index.css?inline"
import { mount } from "svelte"
import type { ComponentType } from "svelte"
export default function renderSvelte(
Component: ComponentType | any,
mountPoint: ShadowRoot | HTMLElement,
props: Record<string, any> = {}
props: Record<string, any> = {},
) {
const app = mount(Component, {
target: mountPoint,
props: {
standalone: false,
...props
}
});
...props,
},
})
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.innerHTML = styles;
mountPoint.appendChild(style);
return app;
const style = document.createElement("style")
style.setAttribute("type", "text/css")
style.innerHTML = styles
mountPoint.appendChild(style)
return app
}
@@ -0,0 +1,160 @@
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
import { onMount } from 'svelte';
import type { CustomTheme } from '@/types/CustomThemes'
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
import { getTheme } from '@/seqta/ui/themes/getTheme'
import Divider from '@/svelte-interface/components/themeCreator/divider.svelte'
import Switch from '@/svelte-interface/components/Switch.svelte'
import Button from '@/svelte-interface/components/Button.svelte'
import Slider from '@/svelte-interface/components/Slider.svelte'
import ColourPicker from '../components/ColourPicker.svelte'
import CodeEditor from '../components/CodeEditor.svelte'
const { themeID } = $props<{ themeID: string }>()
let theme = $state<CustomTheme>({
id: uuidv4(),
name: '',
description: '',
defaultColour: 'blue',
CanChangeColour: true,
allowBackgrounds: true,
CustomCSS: '',
CustomImages: [],
coverImage: null,
isEditable: true,
hideThemeName: false,
forceDark: false
})
onMount(async () => {
console.log(themeID)
if (themeID) {
const tempTheme = await getTheme(themeID)
if (tempTheme) theme = tempTheme
}
});
type SettingType = 'switch' | 'button' | 'slider' | 'colourPicker' | 'select' | 'codeEditor';
type SwitchProps = { state: boolean; onChange: (value: boolean) => void };
type ButtonProps = { onClick: () => void; text: string };
type SliderProps = { state: number; onChange: (value: number) => void; min?: number; max?: number };
type ColourPickerProps = { color: string; onChange: (color: string) => void };
type SelectProps = { options: Array<{ value: string; label: string }>; value: string; onChange: (value: string) => void };
type CodeEditorProps = { value: string; onChange: (value: string) => void };
type ComponentProps = SwitchProps | ButtonProps | SliderProps | ColourPickerProps | SelectProps | CodeEditorProps;
type SettingItem = {
type: SettingType;
title: string;
description: string;
direction?: 'horizontal' | 'vertical';
props: ComponentProps;
};
</script>
{#snippet settingItem(item: SettingItem)}
<div class="flex justify-between {item.direction === 'vertical' ? 'flex-col items-start gap-2' : 'items-center'} py-3">
<div class="pr-4">
<h2 class="text-sm font-bold">{item.title}</h2>
<p class="text-xs">{item.description}</p>
</div>
<div>
{#if item.type === 'switch'}
<Switch {...(item.props as SwitchProps)} />
{:else if item.type === 'button'}
<Button {...(item.props as ButtonProps)} />
{:else if item.type === 'slider'}
<Slider {...(item.props as SliderProps)} />
{:else if item.type === 'colourPicker'}
<ColourPicker standalone={true} {...(item.props)} />
{:else if item.type === 'codeEditor'}
<CodeEditor {...(item.props as CodeEditorProps)} />
{/if}
</div>
</div>
{/snippet}
<div class='h-screen overflow-y-scroll {$settingsState.DarkMode && "dark"} '>
<div class='w-full min-h-screen bg-zinc-100 dark:bg-zinc-800 flex flex-col p-2 dark:text-white'>
<h1 class='text-xl font-semibold'>Theme Creator</h1>
<a href='https://betterseqta.gitbook.io/betterseqta-docs' target='_blank' class='text-sm font-light text-zinc-500 dark:text-zinc-400'>
<span class='no-underline font-IconFamily pr-0.5'>{'\ueb44'}</span>
<span class='underline'>
Need help? Check out the docs!
</span>
</a>
<Divider />
<div>
<div class='pb-2 text-sm'>Theme Name</div>
<input
id='themeName'
type='text'
placeholder='What is your theme called?'
bind:value={theme.name}
class='w-full p-2 mb-4 transition-all duration-300 rounded-lg focus:outline-none ring-0 focus:ring-1 ring-zinc-100 dark:ring-zinc-700 dark:bg-zinc-900 dark:text-white' />
</div>
<div>
<div class='pb-2 text-sm'>Description <span class='italic font-light opacity-80'>(optional)</span></div>
<textarea
id='themeDescription'
placeholder="Don't worry, this one's optional!"
bind:value={theme.description}
class='w-full p-2 rounded-lg focus:outline-none ring-0 focus:ring-1 ring-zinc-100 dark:ring-zinc-700 dark:bg-zinc-900 dark:text-white'></textarea>
</div>
<Divider />
{#each [
{
type: 'switch',
title: 'Hide Theme Name',
description: 'Useful when your cover image contains text',
props: {
state: theme.hideThemeName,
onChange: (value) => theme.hideThemeName = value
}
},
{
type: 'switch',
title: 'Force Theme',
description: 'Force users to use either dark or light mode',
props: {
state: theme.forceDark !== undefined,
onChange: (value) => theme.forceDark = value ? false : undefined
}
},
{
type: 'colourPicker',
title: 'Default Theme Colour',
description: 'Set the default color for your theme',
direction: 'vertical',
props: {
color: theme.defaultColour,
onChange: (color) => theme.defaultColour = color
}
},
{
type: 'codeEditor',
title: 'Custom CSS',
description: 'Add custom CSS to your theme',
props: {
value: theme.CustomCSS,
onChange: (value) => theme.CustomCSS = value
}
}
] as SettingItem[] as setting}
{@render settingItem(setting)}
{/each}
</div>
</div>
@@ -1,4 +1,3 @@
import type { SettingsState } from './AppProps';
export interface SettingsList {
title: string;
id: number;
@@ -6,9 +5,3 @@ export interface SettingsList {
Component: any; /* TODO: Give this a type */
props?: any;
}
export interface SettingsProps {
settingsState: SettingsState;
setSettingsState: React.Dispatch<React.SetStateAction<SettingsState>>;
}
+3 -3
View File
@@ -4,8 +4,8 @@ import { join, resolve } from 'path';
import { base64Loader } from './lib/base64loader';
import type { BuildTarget } from './lib/types';
import react from '@vitejs/plugin-react-swc';
//import million from "million/compiler";
import react from '@vitejs/plugin-react';
import million from "million/compiler";
//import MillionLint from '@million/lint';
import { svelte } from '@sveltejs/vite-plugin-svelte'
@@ -31,7 +31,7 @@ export default defineConfig({
svelte({
emitCss: false
}),
//million.vite({ auto: true }),
million.vite({ auto: true }),
//MillionLint.vite(), /* enable for testing and debugging performance */
crx({
manifest: targets.find(t => t.browser === mode.toLowerCase())?.manifest ?? chrome.manifest,