From d1acd043f1a211038b5901a85045a804abf2b296 Mon Sep 17 00:00:00 2001 From: Keith Hudnall Date: Mon, 23 Mar 2026 22:05:07 -0700 Subject: [PATCH 1/5] EL-349 Added sample plugin ui. Added dev ui module to quickly iterate. Added run configurations. --- .gitignore | 6 +- .idea/encodings.xml | 2 + .idea/runConfigurations/run_element.xml | 16 + .idea/runConfigurations/run_superuser_ui.xml | 12 + .idea/runConfigurations/run_user_ui.xml | 12 + debug/src/main/java/run.java | 25 +- .../mygame/HelloWorldApplication.java | 8 +- element/src/main/static/index.html | 11 + element/src/main/ui/index.html | 11 + .../src/main/ui/superuser/plugin.bundle.js | 8 + element/src/main/ui/superuser/plugin.json | 11 + element/src/main/ui/user/plugin.bundle.js | 8 + element/src/main/ui/user/plugin.json | 11 + pom.xml | 3 +- ui/package-lock.json | 1849 +++++++++++++++++ ui/package.json | 19 + ui/pom.xml | 57 + ui/src/superuser/ExamplePlugin.tsx | 16 + ui/src/superuser/dev-entry.tsx | 9 + ui/src/superuser/index.html | 12 + ui/src/superuser/plugin-entry.ts | 9 + ui/src/user/ExamplePlugin.tsx | 16 + ui/src/user/dev-entry.tsx | 9 + ui/src/user/index.html | 12 + ui/src/user/plugin-entry.ts | 9 + ui/tsconfig.json | 14 + ui/vite.base.config.ts | 44 + ui/vite.superuser.config.ts | 2 + ui/vite.user.config.ts | 2 + 29 files changed, 2219 insertions(+), 4 deletions(-) create mode 100644 .idea/runConfigurations/run_element.xml create mode 100644 .idea/runConfigurations/run_superuser_ui.xml create mode 100644 .idea/runConfigurations/run_user_ui.xml create mode 100644 element/src/main/static/index.html create mode 100644 element/src/main/ui/index.html create mode 100644 element/src/main/ui/superuser/plugin.bundle.js create mode 100644 element/src/main/ui/superuser/plugin.json create mode 100644 element/src/main/ui/user/plugin.bundle.js create mode 100644 element/src/main/ui/user/plugin.json create mode 100644 ui/package-lock.json create mode 100644 ui/package.json create mode 100644 ui/pom.xml create mode 100644 ui/src/superuser/ExamplePlugin.tsx create mode 100644 ui/src/superuser/dev-entry.tsx create mode 100644 ui/src/superuser/index.html create mode 100644 ui/src/superuser/plugin-entry.ts create mode 100644 ui/src/user/ExamplePlugin.tsx create mode 100644 ui/src/user/dev-entry.tsx create mode 100644 ui/src/user/index.html create mode 100644 ui/src/user/plugin-entry.ts create mode 100644 ui/tsconfig.json create mode 100644 ui/vite.base.config.ts create mode 100644 ui/vite.superuser.config.ts create mode 100644 ui/vite.user.config.ts diff --git a/.gitignore b/.gitignore index c28f963..9540bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,11 @@ build/ ### Mac OS ### .DS_Store -## Directories created by Namazu Elemetns +## Directories created by Namazu Elements /cdn-repos/** /script-repos/** +## Frontend build tooling +/ui/node_modules/ +/ui/.node/ + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 146497a..f2aca1b 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -9,5 +9,7 @@ + + \ No newline at end of file diff --git a/.idea/runConfigurations/run_element.xml b/.idea/runConfigurations/run_element.xml new file mode 100644 index 0000000..81fe90d --- /dev/null +++ b/.idea/runConfigurations/run_element.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/run_superuser_ui.xml b/.idea/runConfigurations/run_superuser_ui.xml new file mode 100644 index 0000000..928db4d --- /dev/null +++ b/.idea/runConfigurations/run_superuser_ui.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/ui/src/superuser/plugin-entry.ts b/ui/src/superuser/plugin-entry.ts new file mode 100644 index 0000000..a112c44 --- /dev/null +++ b/ui/src/superuser/plugin-entry.ts @@ -0,0 +1,9 @@ +import { ExamplePlugin } from './ExamplePlugin' + +declare const window: Window & { + __elementsPlugins?: { + register(route: string, component: unknown): void + } +} + +window.__elementsPlugins?.register('example-element', ExamplePlugin) diff --git a/ui/src/user/ExamplePlugin.tsx b/ui/src/user/ExamplePlugin.tsx new file mode 100644 index 0000000..69794d3 --- /dev/null +++ b/ui/src/user/ExamplePlugin.tsx @@ -0,0 +1,16 @@ +import React from 'react' + +export function ExamplePlugin() { + return ( +
+

Example Element

+

+ This page is served from the Example Element’s user UI content directory. +

+
+ Installed Elements can inject custom user-facing UI by placing a plugin.json + and plugin.bundle.js in their ui/user/ content directory. +
+
+ ) +} diff --git a/ui/src/user/dev-entry.tsx b/ui/src/user/dev-entry.tsx new file mode 100644 index 0000000..4eca758 --- /dev/null +++ b/ui/src/user/dev-entry.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { ExamplePlugin } from './ExamplePlugin' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/ui/src/user/index.html b/ui/src/user/index.html new file mode 100644 index 0000000..5abfbee --- /dev/null +++ b/ui/src/user/index.html @@ -0,0 +1,12 @@ + + + + + + Example Element — User Plugin (dev) + + +
+ + + diff --git a/ui/src/user/plugin-entry.ts b/ui/src/user/plugin-entry.ts new file mode 100644 index 0000000..a112c44 --- /dev/null +++ b/ui/src/user/plugin-entry.ts @@ -0,0 +1,9 @@ +import { ExamplePlugin } from './ExamplePlugin' + +declare const window: Window & { + __elementsPlugins?: { + register(route: string, component: unknown): void + } +} + +window.__elementsPlugins?.register('example-element', ExamplePlugin) diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 0000000..0aa9342 --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react", + "jsxFactory": "React.createElement", + "jsxFragmentFactory": "React.Fragment", + "strict": true, + "skipLibCheck": true + }, + "include": ["src", "vite.base.config.ts", "vite.superuser.config.ts", "vite.user.config.ts"] +} diff --git a/ui/vite.base.config.ts b/ui/vite.base.config.ts new file mode 100644 index 0000000..b9b8acd --- /dev/null +++ b/ui/vite.base.config.ts @@ -0,0 +1,44 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export function createConfig(segment: string) { + return defineConfig(({ command }) => { + if (command === 'serve') { + // Standalone dev server — renders the component in isolation with HMR. + // Run via: npm run dev:superuser or npm run dev:user + return { + plugins: [react({ jsxRuntime: 'classic' })], + root: `src/${segment}`, + } + } + + // Library/IIFE build — writes plugin.bundle.js directly into + // ../element/src/main/ui/{segment}/ for packaging into the .elm artifact. + // Run via: npm run build + return { + esbuild: { + jsx: 'transform', + jsxFactory: 'React.createElement', + jsxFragment: 'React.Fragment', + }, + build: { + lib: { + entry: `src/${segment}/plugin-entry.ts`, + name: 'ElementPlugin', + formats: ['iife' as const], + fileName: () => 'plugin.bundle.js', + }, + outDir: `../element/src/main/ui/${segment}`, + emptyOutDir: false, + minify: false, + rollupOptions: { + external: ['react'], + output: { + // Rewrites `import React from 'react'` → `var React = window.React` in the IIFE. + globals: { react: 'window.React' }, + }, + }, + }, + } + }) +} diff --git a/ui/vite.superuser.config.ts b/ui/vite.superuser.config.ts new file mode 100644 index 0000000..3bea348 --- /dev/null +++ b/ui/vite.superuser.config.ts @@ -0,0 +1,2 @@ +import { createConfig } from './vite.base.config' +export default createConfig('superuser') diff --git a/ui/vite.user.config.ts b/ui/vite.user.config.ts new file mode 100644 index 0000000..6417df2 --- /dev/null +++ b/ui/vite.user.config.ts @@ -0,0 +1,2 @@ +import { createConfig } from './vite.base.config' +export default createConfig('user') From 3919df634fcb3c8b8a854758ea92d37b34db7d29 Mon Sep 17 00:00:00 2001 From: Keith Hudnall Date: Mon, 23 Mar 2026 22:11:17 -0700 Subject: [PATCH 2/5] EL-349 Updated main readme to link to new ui readme --- README.md | 8 +++ ui/README.md | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 ui/README.md diff --git a/README.md b/README.md index c19762a..005857f 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,14 @@ Try out GET http://localhost:8080/app/rest/example-element/helloworld > [!Note] > The `example-element` portion of the URL is determined by the `dev.getelements.elements.app.serve.prefix` value in the `dev.getelements.element.attributes.properties` file in the deployment project. +--- + +## Dashboard UI Plugins + +Elements can inject custom pages into the Elements dashboard by shipping a React component bundle alongside the Element's Java code. See **[ui/README.md](ui/README.md)** for the full guide. + +--- + # Further Reading - **[MORPHIA.md](MORPHIA.md)** — How to use the MongoDB/Morphia database layer inside a custom Element (Transactions, DAOs, `Datastore`, `@ElementTypeRequest`) diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000..3ba21ca --- /dev/null +++ b/ui/README.md @@ -0,0 +1,143 @@ +# Dashboard UI Plugins + +Elements can inject custom pages into the Elements dashboard by shipping a small React component bundle alongside the Element's Java code. The dashboard discovers and loads these bundles at runtime - no changes to the dashboard itself are required. + +## How it works + +When the dashboard starts, it checks each running Element container for a `plugin.json` manifest file. If found, it dynamically loads the declared JavaScript bundle and adds the component to the sidebar navigation. + +The manifest and bundle are placed in the Element's static UI content directory: + +``` +element/src/main/ui/ + superuser/ + plugin.json <- declares the sidebar entry and bundle location + plugin.bundle.js <- self-contained IIFE component bundle + user/ + plugin.json <- same structure, for user-facing dashboards (future) + plugin.bundle.js +``` + +These files are packaged into the `.elm` artifact at build time and served by the Elements runtime under `/app/ui/{element-prefix}/{segment}/`. + +## plugin.json + +```json +{ + "schema": "1", + "entries": [ + { + "label": "Example Element", + "icon": "Package", + "bundlePath": "plugin.bundle.js", + "route": "example-element" + } + ] +} +``` + +| Field | Description | +|---|---| +| `label` | Text shown in the dashboard sidebar | +| `icon` | A [Lucide](https://lucide.dev/icons/) icon name (e.g. `Package`, `Layers`, `Zap`) | +| `bundlePath` | Path to the bundle, relative to the manifest file | +| `route` | Unique key used in the dashboard URL (`/plugin/{route}`) | + +## Bundle format + +The bundle must be an IIFE (immediately-invoked function expression) that registers a React component with the dashboard's plugin registry. The host dashboard exposes `window.React` - the bundle must use this same instance to avoid React hook conflicts. + +```js +(function () { + var React = window.React; + + function MyPlugin() { + return React.createElement('div', { className: 'p-6' }, + React.createElement('h1', { className: 'text-2xl font-bold' }, 'My Plugin') + ); + } + + window.__elementsPlugins && window.__elementsPlugins.register('my-route', MyPlugin); +})(); +``` + +The component can use Tailwind utility classes - the dashboard's stylesheet is already loaded when the bundle runs. + +## User segmentation + +The `superuser/` directory serves components shown to administrators. A `user/` directory (same structure) will serve components in user-facing dashboards when that feature is released. Each segment is discovered and loaded independently, so the bundles do not interfere with each other. + +## Developing the UI + +This module contains a Vite-based TypeScript project for developing plugin components with JSX and full hot-module replacement, while still producing the correct IIFE output for embedding. + +### Requirements + +- [Node.js](https://nodejs.org/) 18+ (via [nvm](https://github.com/nvm-sh/nvm) recommended) + +### One-time setup + +```bash +cd ui +npm install +``` + +### Standalone development (fast iteration) + +Run a Vite dev server that renders the component in isolation with live reload: + +```bash +npm run dev:superuser # or: npm run dev:user +``` + +Open `http://localhost:5173` in a browser. Edit `src/superuser/ExamplePlugin.tsx` (or `src/user/ExamplePlugin.tsx`) and the browser updates instantly. + +### Building for integration + +When ready to test the component embedded in the actual dashboard, build the bundles: + +```bash +npm run build +``` + +This compiles both segments and writes `plugin.bundle.js` directly into `element/src/main/ui/superuser/` and `element/src/main/ui/user/`. No copy step is needed - the output lands exactly where the Maven build will pick it up. + +Then run the `debug` module to repackage the `.elm` and restart the server. + +### Source structure + +``` +ui/src/ + superuser/ + ExamplePlugin.tsx <- edit this for the superuser UI component + dev-entry.tsx <- mounts the component for standalone dev (do not ship) + plugin-entry.ts <- registers the component with the dashboard registry + index.html <- dev server entry point (do not ship) + user/ + ExamplePlugin.tsx <- edit this for the user UI component + dev-entry.tsx + plugin-entry.ts + index.html + shared/ <- optional: components used by both segments +``` + +`ExamplePlugin.tsx` is the only file you need to edit for most use cases. The other files wire it into the dev server and the plugin registry respectively. + +### Adding your own component + +1. Edit `src/superuser/ExamplePlugin.tsx` (and/or the `user/` equivalent) +2. Develop with `npm run dev:superuser` until satisfied +3. Run `npm run build` to write the bundle into `element/src/main/ui/superuser/` +4. Restart the debug server to pick up the new bundle + +Shared components (used by both segments) can be placed in `src/shared/` and imported with a relative path. + +### CI and release builds + +To build the UI as part of a Maven build (e.g. in CI), activate the `build-ui` profile from the project root: + +```bash +mvn install -Pbuild-ui +``` + +This profile is deliberately inactive by default so that normal `mvn install` does not touch `node_modules`. From 103942ef08ec919b9706beaea8391cbabc558b96 Mon Sep 17 00:00:00 2001 From: Keith Hudnall Date: Mon, 23 Mar 2026 22:28:13 -0700 Subject: [PATCH 3/5] EL-349 Added css to mimic the elements cms while developing --- .../src/main/ui/superuser/plugin.bundle.js | 2 +- ui/package-lock.json | 1000 ++++++++++++++++- ui/package.json | 5 + ui/postcss.config.ts | 6 + ui/src/dev.css | 115 ++ ui/src/superuser/dev-entry.tsx | 29 +- ui/src/user/dev-entry.tsx | 29 +- ui/tailwind.config.ts | 79 ++ ui/tsconfig.json | 2 +- 9 files changed, 1247 insertions(+), 20 deletions(-) create mode 100644 ui/postcss.config.ts create mode 100644 ui/src/dev.css create mode 100644 ui/tailwind.config.ts diff --git a/element/src/main/ui/superuser/plugin.bundle.js b/element/src/main/ui/superuser/plugin.bundle.js index cb284b7..c210973 100644 --- a/element/src/main/ui/superuser/plugin.bundle.js +++ b/element/src/main/ui/superuser/plugin.bundle.js @@ -2,7 +2,7 @@ "use strict"; var _a; function ExamplePlugin() { - return /* @__PURE__ */ React.createElement("div", { className: "p-6 max-w-2xl" }, /* @__PURE__ */ React.createElement("h1", { className: "text-2xl font-bold mb-2" }, "Example Element"), /* @__PURE__ */ React.createElement("p", { className: "text-muted-foreground mb-4" }, "This page is served from the Example Element’s superuser UI content directory."), /* @__PURE__ */ React.createElement("div", { className: "rounded-lg border p-4 text-sm text-muted-foreground" }, "Installed Elements can inject custom admin UI into the dashboard by placing a plugin.json and plugin.bundle.js in their ui/superuser/ content directory blah blah.")); + return /* @__PURE__ */ React.createElement("div", { className: "p-6 max-w-2xl" }, /* @__PURE__ */ React.createElement("h1", { className: "text-2xl font-bold mb-2" }, "Example Element"), /* @__PURE__ */ React.createElement("p", { className: "text-muted-foreground mb-4" }, "This page is served from the Example Element’s superuser UI content directory."), /* @__PURE__ */ React.createElement("div", { className: "rounded-lg border p-4 text-sm text-muted-foreground" }, "Installed Elements can inject custom admin UI into the dashboard by placing a plugin.json and plugin.bundle.js in their ui/superuser/ content directory.")); } (_a = window.__elementsPlugins) == null ? void 0 : _a.register("example-element", ExamplePlugin); })(window.React); diff --git a/ui/package-lock.json b/ui/package-lock.json index 3dedb1b..71fd9cd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,15 +8,33 @@ "name": "example-element-ui", "version": "1.0.0", "devDependencies": { + "@tailwindcss/typography": "^0.5.15", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.0", + "autoprefixer": "^10.4.0", + "postcss": "^8.4.0", "react": "^18.3.0", "react-dom": "^18.3.0", + "tailwindcss": "^3.4.0", + "tailwindcss-animate": "^1.0.7", "typescript": "^5.6.0", "vite": "^6.0.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -791,6 +809,44 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -1148,6 +1204,19 @@ "win32" ] }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1249,6 +1318,84 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.10.10", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", @@ -1262,6 +1409,32 @@ "node": ">=6.0.0" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -1296,6 +1469,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001781", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", @@ -1317,6 +1500,54 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1324,6 +1555,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -1349,6 +1593,20 @@ } } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.321", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", @@ -1408,6 +1666,46 @@ "node": ">=6" } }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1426,6 +1724,33 @@ } } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1441,6 +1766,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1451,6 +1786,104 @@ "node": ">=6.9.0" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1484,6 +1917,26 @@ "node": ">=6" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1507,26 +1960,75 @@ "yallist": "^3.0.2" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { @@ -1540,6 +2042,43 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1560,6 +2099,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss": { "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", @@ -1589,6 +2148,175 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -1626,6 +2354,74 @@ "node": ">=0.10.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.60.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", @@ -1671,6 +2467,30 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -1701,6 +2521,127 @@ "node": ">=0.10.0" } }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -1718,6 +2659,26 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1763,6 +2724,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", diff --git a/ui/package.json b/ui/package.json index fa0759e..e851148 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,11 +8,16 @@ "build": "vite build --config vite.superuser.config.ts && vite build --config vite.user.config.ts" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.15", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.0", + "autoprefixer": "^10.4.0", + "postcss": "^8.4.0", "react": "^18.3.0", "react-dom": "^18.3.0", + "tailwindcss": "^3.4.0", + "tailwindcss-animate": "^1.0.7", "typescript": "^5.6.0", "vite": "^6.0.0" } diff --git a/ui/postcss.config.ts b/ui/postcss.config.ts new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/ui/postcss.config.ts @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/ui/src/dev.css b/ui/src/dev.css new file mode 100644 index 0000000..025d193 --- /dev/null +++ b/ui/src/dev.css @@ -0,0 +1,115 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* LIGHT MODE */ +:root { + --button-outline: rgba(0,0,0, .10); + --badge-outline: rgba(0,0,0, .05); + + --opaque-button-border-intensity: -8; + + --elevate-1: rgba(0,0,0, .03); + --elevate-2: rgba(0,0,0, .08); + + --background: 220 15% 96%; + --foreground: 222 8% 9%; + --border: 220 13% 88%; + --card: 0 0% 100%; + --card-foreground: 222 8% 12%; + --card-border: 220 13% 91%; + --sidebar: 217 40% 94%; + --sidebar-foreground: 222 8% 15%; + --sidebar-border: 217 30% 88%; + --sidebar-primary: 217 91% 60%; + --sidebar-primary-foreground: 217 91% 98%; + --sidebar-accent: 220 13% 91%; + --sidebar-accent-foreground: 222 8% 25%; + --sidebar-ring: 217 91% 60%; + --popover: 220 13% 94%; + --popover-foreground: 222 8% 18%; + --popover-border: 220 13% 88%; + --primary: 217 91% 60%; + --primary-foreground: 217 91% 98%; + --secondary: 220 13% 88%; + --secondary-foreground: 222 8% 20%; + --muted: 220 10% 92%; + --muted-foreground: 222 8% 45%; + --accent: 330 80% 65%; + --accent-foreground: 0 0% 100%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 84% 98%; + --input: 222 8% 65%; + --ring: 217 91% 60%; + + --font-sans: Inter, sans-serif; + --font-serif: Georgia, serif; + --font-mono: JetBrains Mono, monospace; + --radius: .5rem; + + --sidebar-primary-border: hsl(var(--sidebar-primary)); + --sidebar-primary-border: hsl(from hsl(var(--sidebar-primary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --sidebar-accent-border: hsl(var(--sidebar-accent)); + --sidebar-accent-border: hsl(from hsl(var(--sidebar-accent)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --primary-border: hsl(var(--primary)); + --primary-border: hsl(from hsl(var(--primary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --secondary-border: hsl(var(--secondary)); + --secondary-border: hsl(from hsl(var(--secondary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --muted-border: hsl(var(--muted)); + --muted-border: hsl(from hsl(var(--muted)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --accent-border: hsl(var(--accent)); + --accent-border: hsl(from hsl(var(--accent)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); + --destructive-border: hsl(var(--destructive)); + --destructive-border: hsl(from hsl(var(--destructive)) h s calc(l + var(--opaque-button-border-intensity)) / alpha); +} + +/* DARK MODE */ +.dark { + --button-outline: rgba(255,255,255, .10); + --badge-outline: rgba(255,255,255, .05); + + --opaque-button-border-intensity: 9; + + --elevate-1: rgba(255,255,255, .04); + --elevate-2: rgba(255,255,255, .09); + + --background: 217 25% 10%; + --foreground: 217 20% 95%; + --border: 217 25% 18%; + --card: 217 25% 14%; + --card-foreground: 217 20% 90%; + --card-border: 217 25% 18%; + --sidebar: 217 30% 12%; + --sidebar-foreground: 217 20% 88%; + --sidebar-border: 217 30% 18%; + --sidebar-primary: 217 91% 60%; + --sidebar-primary-foreground: 217 91% 98%; + --sidebar-accent: 222 8% 15%; + --sidebar-accent-foreground: 222 8% 85%; + --sidebar-ring: 217 91% 60%; + --popover: 222 8% 14%; + --popover-foreground: 222 8% 92%; + --popover-border: 222 8% 18%; + --primary: 217 91% 60%; + --primary-foreground: 217 91% 98%; + --secondary: 222 8% 18%; + --secondary-foreground: 222 8% 90%; + --muted: 222 6% 16%; + --muted-foreground: 222 8% 65%; + --accent: 330 80% 60%; + --accent-foreground: 0 0% 100%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 84% 98%; + --input: 222 8% 25%; + --ring: 217 91% 60%; +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply font-sans antialiased bg-background text-foreground; + } +} diff --git a/ui/src/superuser/dev-entry.tsx b/ui/src/superuser/dev-entry.tsx index 4eca758..44eb1ef 100644 --- a/ui/src/superuser/dev-entry.tsx +++ b/ui/src/superuser/dev-entry.tsx @@ -1,9 +1,36 @@ import React from 'react' import ReactDOM from 'react-dom/client' import { ExamplePlugin } from './ExamplePlugin' +import '../dev.css' + +function DevShell() { + const [dark, setDark] = React.useState( + () => window.matchMedia('(prefers-color-scheme: dark)').matches + ) + + React.useEffect(() => { + document.documentElement.classList.toggle('dark', dark) + }, [dark]) + + return ( +
+ {import.meta.env.DEV && ( +
+ +
+ )} + +
+ ) +} ReactDOM.createRoot(document.getElementById('root')!).render( - + ) diff --git a/ui/src/user/dev-entry.tsx b/ui/src/user/dev-entry.tsx index 4eca758..44eb1ef 100644 --- a/ui/src/user/dev-entry.tsx +++ b/ui/src/user/dev-entry.tsx @@ -1,9 +1,36 @@ import React from 'react' import ReactDOM from 'react-dom/client' import { ExamplePlugin } from './ExamplePlugin' +import '../dev.css' + +function DevShell() { + const [dark, setDark] = React.useState( + () => window.matchMedia('(prefers-color-scheme: dark)').matches + ) + + React.useEffect(() => { + document.documentElement.classList.toggle('dark', dark) + }, [dark]) + + return ( +
+ {import.meta.env.DEV && ( +
+ +
+ )} + +
+ ) +} ReactDOM.createRoot(document.getElementById('root')!).render( - + ) diff --git a/ui/tailwind.config.ts b/ui/tailwind.config.ts new file mode 100644 index 0000000..5d17e6a --- /dev/null +++ b/ui/tailwind.config.ts @@ -0,0 +1,79 @@ +import type { Config } from "tailwindcss"; + +export default { + darkMode: ["class"], + content: ["./src/**/*.{ts,tsx}"], + theme: { + extend: { + borderRadius: { + lg: ".5625rem", + md: ".375rem", + sm: ".1875rem", + }, + colors: { + background: "hsl(var(--background) / )", + foreground: "hsl(var(--foreground) / )", + border: "hsl(var(--border) / )", + input: "hsl(var(--input) / )", + card: { + DEFAULT: "hsl(var(--card) / )", + foreground: "hsl(var(--card-foreground) / )", + border: "hsl(var(--card-border) / )", + }, + popover: { + DEFAULT: "hsl(var(--popover) / )", + foreground: "hsl(var(--popover-foreground) / )", + border: "hsl(var(--popover-border) / )", + }, + primary: { + DEFAULT: "hsl(var(--primary) / )", + foreground: "hsl(var(--primary-foreground) / )", + border: "var(--primary-border)", + }, + secondary: { + DEFAULT: "hsl(var(--secondary) / )", + foreground: "hsl(var(--secondary-foreground) / )", + border: "var(--secondary-border)", + }, + muted: { + DEFAULT: "hsl(var(--muted) / )", + foreground: "hsl(var(--muted-foreground) / )", + border: "var(--muted-border)", + }, + accent: { + DEFAULT: "hsl(var(--accent) / )", + foreground: "hsl(var(--accent-foreground) / )", + border: "var(--accent-border)", + }, + destructive: { + DEFAULT: "hsl(var(--destructive) / )", + foreground: "hsl(var(--destructive-foreground) / )", + border: "var(--destructive-border)", + }, + ring: "hsl(var(--ring) / )", + sidebar: { + ring: "hsl(var(--sidebar-ring) / )", + DEFAULT: "hsl(var(--sidebar) / )", + foreground: "hsl(var(--sidebar-foreground) / )", + border: "hsl(var(--sidebar-border) / )", + }, + "sidebar-primary": { + DEFAULT: "hsl(var(--sidebar-primary) / )", + foreground: "hsl(var(--sidebar-primary-foreground) / )", + border: "var(--sidebar-primary-border)", + }, + "sidebar-accent": { + DEFAULT: "hsl(var(--sidebar-accent) / )", + foreground: "hsl(var(--sidebar-accent-foreground) / )", + border: "var(--sidebar-accent-border)", + }, + }, + fontFamily: { + sans: ["var(--font-sans)"], + serif: ["var(--font-serif)"], + mono: ["var(--font-mono)"], + }, + }, + }, + plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], +} satisfies Config; diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 0aa9342..c5a1f70 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -10,5 +10,5 @@ "strict": true, "skipLibCheck": true }, - "include": ["src", "vite.base.config.ts", "vite.superuser.config.ts", "vite.user.config.ts"] + "include": ["src", "vite.base.config.ts", "vite.superuser.config.ts", "vite.user.config.ts", "tailwind.config.ts", "postcss.config.ts"] } From 4ac1c1e1efe96eac5418258253cb8e0ad5fc47bc Mon Sep 17 00:00:00 2001 From: Keith Hudnall Date: Wed, 25 Mar 2026 09:54:29 -0700 Subject: [PATCH 4/5] EL-349 Updated claude reference, added build ui run config --- .idea/runConfigurations/build_ui.xml | 12 +++ CLAUDE.md | 109 ++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 .idea/runConfigurations/build_ui.xml diff --git a/.idea/runConfigurations/build_ui.xml b/.idea/runConfigurations/build_ui.xml new file mode 100644 index 0000000..6ac303f --- /dev/null +++ b/.idea/runConfigurations/build_ui.xml @@ -0,0 +1,12 @@ + + + + + +