paperclip-theme 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +45 -41
- package/dist/constants.js.map +1 -1
- package/dist/ui/index.js +82 -0
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +107 -5
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +47 -41
- package/src/ui/index.tsx +92 -0
- package/src/worker.ts +145 -4
package/dist/ui/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ui/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport {\n usePluginData,\n usePluginAction,\n} from \"@paperclipai/plugin-sdk/ui\";\n\n/* \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface ThemeConfig {\n id: string;\n name: string;\n description: string;\n author: string;\n isDark: boolean;\n tokens: Record<string, string>;\n radius: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/* \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst MAX_VISIBLE_PRESETS = 8;\nconst CARD_WIDTH = 192;\nconst CARD_GAP = 10;\nconst SWATCH_TOKENS = [\"--background\", \"--primary\", \"--accent\", \"--chart-1\", \"--destructive\"];\n\n/* \u2500\u2500\u2500 CSS injection engine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst STYLE_ELEMENT_ID = \"blazo-theme-overrides\";\n\nfunction injectThemeCSS(theme: ThemeConfig): void {\n let el = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!el) {\n el = document.createElement(\"style\");\n el.id = STYLE_ELEMENT_ID;\n document.head.appendChild(el);\n }\n\n const entries = Object.entries(theme.tokens);\n const radiusEntry = theme.radius ? ` --radius: ${theme.radius};` : \"\";\n\n const lines = entries.map(([key, value]) => ` ${key}: ${value};`);\n if (radiusEntry) lines.push(radiusEntry);\n\n const darkToggle = theme.isDark ? \"dark\" : \"light\";\n el.textContent = `:root { ${lines.join(\" \")} }\\n`;\n\n const htmlEl = document.documentElement;\n htmlEl.classList.toggle(\"dark\", theme.isDark);\n htmlEl.classList.toggle(\"light\", !theme.isDark);\n htmlEl.style.colorScheme = darkToggle;\n}\n\nfunction clearThemeCSS(): void {\n const el = document.getElementById(STYLE_ELEMENT_ID);\n if (el) el.remove();\n}\n\n/* \u2500\u2500\u2500 Token groups for the editor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface TokenGroup {\n label: string;\n description: string;\n tokens: string[];\n}\n\nconst TOKEN_GROUPS: TokenGroup[] = [\n {\n label: \"Surfaces\",\n description: \"Background and card colors\",\n tokens: [\"--background\", \"--card\", \"--muted\", \"--accent\"],\n },\n {\n label: \"Text\",\n description: \"Foreground and label colors\",\n tokens: [\"--foreground\", \"--card-foreground\", \"--muted-foreground\", \"--accent-foreground\"],\n },\n {\n label: \"Interactive\",\n description: \"Buttons, links, and primary actions\",\n tokens: [\"--primary\", \"--primary-foreground\"],\n },\n {\n label: \"Feedback\",\n description: \"Errors and destructive actions\",\n tokens: [\"--destructive\", \"--destructive-foreground\"],\n },\n {\n label: \"Structure\",\n description: \"Borders and focus rings\",\n tokens: [\"--border\", \"--input\", \"--ring\"],\n },\n {\n label: \"Charts\",\n description: \"Dashboard visualization colors\",\n tokens: [\"--chart-1\", \"--chart-2\", \"--chart-3\", \"--chart-4\", \"--chart-5\"],\n },\n];\n\n/* \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction tokenDisplayName(token: string): string {\n return token\n .replace(/^--/, \"\")\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\nfunction oklchToHex(oklchStr: string): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const ctx2d = canvas.getContext(\"2d\");\n if (!ctx2d) return \"#000000\";\n ctx2d.fillStyle = oklchStr;\n ctx2d.fillRect(0, 0, 1, 1);\n const [r, g, b] = ctx2d.getImageData(0, 0, 1, 1).data;\n return `#${(r ?? 0).toString(16).padStart(2, \"0\")}${(g ?? 0).toString(16).padStart(2, \"0\")}${(b ?? 0).toString(16).padStart(2, \"0\")}`;\n}\n\nfunction hexToOklch(hex: string): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const ctx2d = canvas.getContext(\"2d\");\n if (!ctx2d) return \"oklch(0% 0 0)\";\n ctx2d.fillStyle = hex;\n ctx2d.fillRect(0, 0, 1, 1);\n const [r, g, b] = ctx2d.getImageData(0, 0, 1, 1).data;\n const rLin = srgbToLinear((r ?? 0) / 255);\n const gLin = srgbToLinear((g ?? 0) / 255);\n const bLin = srgbToLinear((b ?? 0) / 255);\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n const L = 0.2104542553 * lRoot + 0.7936177850 * mRoot - 0.0040720468 * sRoot;\n const a = 1.9779984951 * lRoot - 2.4285922050 * mRoot + 0.4505937099 * sRoot;\n const bOklab = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.8086757660 * sRoot;\n const C = Math.sqrt(a * a + bOklab * bOklab);\n let H = (Math.atan2(bOklab, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return `oklch(${(L * 100).toFixed(1)}% ${C.toFixed(3)} ${H.toFixed(1)})`;\n}\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\n/* \u2500\u2500\u2500 Style definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst styles = {\n root: {\n fontFamily: \"var(--font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif)\",\n color: \"var(--foreground)\",\n maxWidth: 720,\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 32,\n },\n header: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 6,\n },\n title: {\n fontSize: 20,\n fontWeight: 600,\n letterSpacing: \"-0.01em\",\n margin: 0,\n color: \"var(--foreground)\",\n },\n subtitle: {\n fontSize: 13,\n color: \"var(--muted-foreground)\",\n margin: 0,\n lineHeight: 1.5,\n },\n section: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 16,\n },\n sectionHeader: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n margin: 0,\n },\n sectionLabel: {\n fontSize: 13,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.04em\",\n color: \"var(--muted-foreground)\",\n margin: 0,\n },\n seeAllBtn: {\n fontSize: 12,\n fontWeight: 500,\n color: \"var(--primary)\",\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n padding: \"2px 0\",\n outline: \"none\",\n transition: \"opacity 150ms ease\",\n },\n presetsScroller: {\n overflowX: \"auto\" as const,\n overflowY: \"hidden\" as const,\n WebkitOverflowScrolling: \"touch\" as const,\n scrollbarWidth: \"thin\" as const,\n paddingBottom: 4,\n },\n presetsTrack: {\n display: \"grid\",\n gridTemplateRows: \"1fr 1fr\",\n gridAutoFlow: \"column\" as const,\n gridAutoColumns: `${CARD_WIDTH}px`,\n gap: CARD_GAP,\n },\n presetCard: (isActive: boolean) => ({\n position: \"relative\" as const,\n width: CARD_WIDTH,\n padding: \"12px 14px\",\n borderRadius: 8,\n border: `1.5px solid ${isActive ? \"var(--primary)\" : \"var(--border)\"}`,\n background: isActive ? \"var(--accent)\" : \"transparent\",\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 5,\n outline: \"none\",\n boxSizing: \"border-box\" as const,\n textAlign: \"left\" as const,\n }),\n presetName: {\n fontSize: 13,\n fontWeight: 500,\n color: \"var(--foreground)\",\n margin: 0,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n textOverflow: \"ellipsis\" as const,\n },\n presetDesc: {\n fontSize: 11,\n color: \"var(--muted-foreground)\",\n margin: 0,\n lineHeight: 1.35,\n display: \"-webkit-box\",\n WebkitLineClamp: 2,\n WebkitBoxOrient: \"vertical\" as const,\n overflow: \"hidden\" as const,\n },\n presetMeta: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n marginTop: 2,\n },\n presetColors: {\n display: \"flex\",\n gap: 3,\n },\n presetSwatch: (color: string) => ({\n width: 14,\n height: 14,\n borderRadius: 3,\n background: color,\n border: \"1px solid rgba(128,128,128,0.25)\",\n flexShrink: 0,\n }),\n presetModeBadge: (isDark: boolean) => ({\n fontSize: 9,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.06em\",\n color: \"var(--muted-foreground)\",\n background: \"var(--muted)\",\n padding: \"1px 5px\",\n borderRadius: 3,\n lineHeight: \"16px\",\n }),\n activeBadge: {\n position: \"absolute\" as const,\n top: 7,\n right: 8,\n fontSize: 9,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.06em\",\n color: \"var(--primary-foreground)\",\n background: \"var(--primary)\",\n padding: \"1px 6px\",\n borderRadius: 999,\n lineHeight: \"16px\",\n },\n tokenGroup: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 10,\n padding: \"16px 0\",\n borderBottom: \"1px solid var(--border)\",\n },\n tokenGroupHeader: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 2,\n },\n tokenGroupLabel: {\n fontSize: 14,\n fontWeight: 500,\n color: \"var(--foreground)\",\n margin: 0,\n },\n tokenGroupDesc: {\n fontSize: 12,\n color: \"var(--muted-foreground)\",\n margin: 0,\n },\n tokenRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: 12,\n padding: \"4px 0\",\n },\n colorInput: {\n width: 32,\n height: 32,\n border: \"1.5px solid var(--border)\",\n borderRadius: 6,\n padding: 0,\n cursor: \"pointer\",\n background: \"none\",\n flexShrink: 0,\n outline: \"none\",\n },\n tokenLabel: {\n fontSize: 13,\n fontWeight: 400,\n color: \"var(--foreground)\",\n flex: 1,\n minWidth: 0,\n },\n tokenValue: {\n fontSize: 11,\n fontFamily: \"var(--font-mono, 'SF Mono', Consolas, monospace)\",\n color: \"var(--muted-foreground)\",\n textAlign: \"right\" as const,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n textOverflow: \"ellipsis\" as const,\n maxWidth: 180,\n },\n radiusSection: {\n display: \"flex\",\n alignItems: \"center\",\n gap: 16,\n padding: \"8px 0\",\n },\n rangeInput: {\n flex: 1,\n accentColor: \"var(--primary)\",\n cursor: \"pointer\",\n },\n radiusValue: {\n fontSize: 13,\n fontFamily: \"var(--font-mono, 'SF Mono', Consolas, monospace)\",\n color: \"var(--muted-foreground)\",\n minWidth: 48,\n textAlign: \"right\" as const,\n },\n radiusPreview: (r: string) => ({\n width: 32,\n height: 32,\n borderRadius: r,\n border: \"2px solid var(--primary)\",\n background: \"var(--accent)\",\n flexShrink: 0,\n }),\n actions: {\n display: \"flex\",\n gap: 10,\n paddingTop: 8,\n },\n btnPrimary: {\n padding: \"9px 20px\",\n borderRadius: 6,\n border: \"none\",\n background: \"var(--primary)\",\n color: \"var(--primary-foreground)\",\n fontSize: 13,\n fontWeight: 500,\n cursor: \"pointer\",\n transition: \"opacity 150ms ease\",\n outline: \"none\",\n },\n btnSecondary: {\n padding: \"9px 20px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"transparent\",\n color: \"var(--foreground)\",\n fontSize: 13,\n fontWeight: 500,\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n outline: \"none\",\n },\n saved: {\n fontSize: 12,\n color: \"var(--muted-foreground)\",\n alignSelf: \"center\" as const,\n transition: \"opacity 300ms ease\",\n },\n overlay: {\n position: \"fixed\" as const,\n inset: 0,\n background: \"rgba(0,0,0,0.55)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 9999,\n backdropFilter: \"blur(4px)\",\n },\n modal: {\n background: \"var(--card)\",\n border: \"1px solid var(--border)\",\n borderRadius: 12,\n width: \"min(640px, calc(100vw - 48px))\",\n maxHeight: \"min(560px, calc(100vh - 80px))\",\n display: \"flex\",\n flexDirection: \"column\" as const,\n boxShadow: \"0 16px 48px rgba(0,0,0,0.25)\",\n overflow: \"hidden\" as const,\n },\n modalHeader: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"16px 20px 0 20px\",\n },\n modalTitle: {\n fontSize: 16,\n fontWeight: 600,\n margin: 0,\n color: \"var(--foreground)\",\n },\n modalCloseBtn: {\n width: 28,\n height: 28,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"none\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n color: \"var(--muted-foreground)\",\n fontSize: 18,\n lineHeight: 1,\n outline: \"none\",\n transition: \"background 150ms ease\",\n },\n modalSearch: {\n margin: \"12px 20px\",\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"var(--muted)\",\n color: \"var(--foreground)\",\n fontSize: 13,\n outline: \"none\",\n width: \"calc(100% - 40px)\",\n boxSizing: \"border-box\" as const,\n transition: \"border-color 150ms ease\",\n },\n modalCount: {\n fontSize: 11,\n color: \"var(--muted-foreground)\",\n padding: \"0 20px 8px 20px\",\n margin: 0,\n },\n modalGrid: {\n flex: 1,\n overflowY: \"auto\" as const,\n padding: \"0 20px 20px 20px\",\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fill, minmax(180px, 1fr))\",\n gap: 10,\n alignContent: \"start\",\n },\n};\n\n/* \u2500\u2500\u2500 Preset Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction PresetCard({\n preset,\n isActive,\n onSelect,\n compact,\n}: {\n preset: ThemeConfig;\n isActive: boolean;\n onSelect: () => void;\n compact?: boolean;\n}) {\n const cardStyle = compact\n ? {\n ...styles.presetCard(isActive),\n width: \"auto\" as const,\n }\n : styles.presetCard(isActive);\n\n return (\n <button\n type=\"button\"\n style={cardStyle}\n onClick={onSelect}\n onMouseEnter={(e) => {\n if (!isActive) (e.currentTarget.style.borderColor = \"var(--muted-foreground)\");\n }}\n onMouseLeave={(e) => {\n if (!isActive) (e.currentTarget.style.borderColor = \"var(--border)\");\n }}\n >\n {isActive && <span style={styles.activeBadge}>Active</span>}\n <p style={styles.presetName}>{preset.name}</p>\n <p style={styles.presetDesc}>{preset.description}</p>\n <div style={styles.presetMeta}>\n <div style={styles.presetColors}>\n {SWATCH_TOKENS.map((t) => (\n <div key={t} style={styles.presetSwatch(preset.tokens[t] ?? \"#333\")} />\n ))}\n </div>\n <span style={styles.presetModeBadge(preset.isDark)}>\n {preset.isDark ? \"Dark\" : \"Light\"}\n </span>\n </div>\n </button>\n );\n}\n\n/* \u2500\u2500\u2500 \"See All\" Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction PresetModal({\n presets,\n activeId,\n onSelect,\n onClose,\n}: {\n presets: ThemeConfig[];\n activeId: string | undefined;\n onSelect: (preset: ThemeConfig) => void;\n onClose: () => void;\n}) {\n const [search, setSearch] = useState(\"\");\n const searchRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n searchRef.current?.focus();\n }, []);\n\n useEffect(() => {\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKey);\n return () => document.removeEventListener(\"keydown\", handleKey);\n }, [onClose]);\n\n const filtered = useMemo(() => {\n if (!search.trim()) return presets;\n const q = search.toLowerCase().trim();\n return presets.filter(\n (p) =>\n p.name.toLowerCase().includes(q) ||\n p.description.toLowerCase().includes(q) ||\n p.author.toLowerCase().includes(q) ||\n (p.isDark ? \"dark\" : \"light\").includes(q),\n );\n }, [presets, search]);\n\n return (\n <div\n style={styles.overlay}\n onClick={(e) => {\n if (e.target === e.currentTarget) onClose();\n }}\n >\n <div style={styles.modal}>\n <div style={styles.modalHeader}>\n <h3 style={styles.modalTitle}>All Themes</h3>\n <button\n type=\"button\"\n style={styles.modalCloseBtn}\n onClick={onClose}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = \"var(--muted)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = \"none\";\n }}\n >\n ✕\n </button>\n </div>\n <input\n ref={searchRef}\n type=\"text\"\n placeholder=\"Search themes...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n style={styles.modalSearch}\n onFocus={(e) => {\n e.currentTarget.style.borderColor = \"var(--primary)\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.borderColor = \"var(--border)\";\n }}\n />\n <p style={styles.modalCount}>\n {filtered.length} of {presets.length} themes\n </p>\n <div style={styles.modalGrid}>\n {filtered.map((preset) => (\n <PresetCard\n key={preset.id}\n preset={preset}\n isActive={activeId === preset.id}\n onSelect={() => {\n onSelect(preset);\n onClose();\n }}\n compact\n />\n ))}\n {filtered.length === 0 && (\n <p style={{ ...styles.subtitle, gridColumn: \"1 / -1\", textAlign: \"center\" as const, padding: \"32px 0\" }}>\n No themes match your search.\n </p>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/* \u2500\u2500\u2500 Token Editor Row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction TokenRow({\n token,\n value,\n onChange,\n}: {\n token: string;\n value: string;\n onChange: (token: string, value: string) => void;\n}) {\n const hex = oklchToHex(value);\n return (\n <div style={styles.tokenRow}>\n <input\n type=\"color\"\n style={styles.colorInput}\n value={hex}\n onChange={(e) => {\n const newOklch = hexToOklch(e.target.value);\n onChange(token, newOklch);\n }}\n title={tokenDisplayName(token)}\n />\n <span style={styles.tokenLabel}>{tokenDisplayName(token)}</span>\n <span style={styles.tokenValue}>{value}</span>\n </div>\n );\n}\n\n/* \u2500\u2500\u2500 Main Settings Page Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport function ThemeSettingsPage() {\n const activeThemeResult = usePluginData<ThemeConfig | null>(\"active-theme\");\n const presetsResult = usePluginData<ThemeConfig[]>(\"presets\");\n const applyTheme = usePluginAction(\"apply-theme\");\n const resetTheme = usePluginAction(\"reset-theme\");\n\n const [localTheme, setLocalTheme] = useState<ThemeConfig | null>(null);\n const [saving, setSaving] = useState(false);\n const [savedAt, setSavedAt] = useState<string | null>(null);\n const [hasUnsaved, setHasUnsaved] = useState(false);\n const [showModal, setShowModal] = useState(false);\n const initialLoadDone = useRef(false);\n\n const presets: ThemeConfig[] = presetsResult.data ?? [];\n const serverTheme: ThemeConfig | null = activeThemeResult.data ?? null;\n\n const visiblePresets = presets.slice(0, MAX_VISIBLE_PRESETS);\n const hasMore = presets.length > MAX_VISIBLE_PRESETS;\n\n useEffect(() => {\n if (initialLoadDone.current) return;\n if (serverTheme) {\n setLocalTheme(serverTheme);\n injectThemeCSS(serverTheme);\n initialLoadDone.current = true;\n } else if (presets.length > 0 && activeThemeResult.data === null) {\n initialLoadDone.current = true;\n }\n }, [serverTheme, presets, activeThemeResult.data]);\n\n const selectPreset = useCallback(\n (preset: ThemeConfig) => {\n const next = { ...preset, updatedAt: new Date().toISOString() };\n setLocalTheme(next);\n injectThemeCSS(next);\n setHasUnsaved(true);\n setSavedAt(null);\n },\n [],\n );\n\n const updateToken = useCallback(\n (token: string, value: string) => {\n setLocalTheme((prev) => {\n if (!prev) return prev;\n const next = {\n ...prev,\n tokens: { ...prev.tokens, [token]: value },\n updatedAt: new Date().toISOString(),\n };\n injectThemeCSS(next);\n return next;\n });\n setHasUnsaved(true);\n setSavedAt(null);\n },\n [],\n );\n\n const updateRadius = useCallback((value: string) => {\n setLocalTheme((prev) => {\n if (!prev) return prev;\n const next = { ...prev, radius: value, updatedAt: new Date().toISOString() };\n injectThemeCSS(next);\n return next;\n });\n setHasUnsaved(true);\n setSavedAt(null);\n }, []);\n\n const handleSave = useCallback(async () => {\n if (!localTheme) return;\n setSaving(true);\n try {\n await applyTheme(localTheme);\n setHasUnsaved(false);\n setSavedAt(new Date().toLocaleTimeString());\n } catch (err) {\n console.error(\"Failed to save theme:\", err);\n } finally {\n setSaving(false);\n }\n }, [localTheme, applyTheme]);\n\n const handleReset = useCallback(async () => {\n setSaving(true);\n try {\n const result = await resetTheme({});\n const restored = result as unknown as ThemeConfig;\n setLocalTheme(restored);\n injectThemeCSS(restored);\n setHasUnsaved(false);\n setSavedAt(null);\n } catch (err) {\n console.error(\"Failed to reset theme:\", err);\n } finally {\n setSaving(false);\n }\n }, [resetTheme]);\n\n const radiusNum = parseFloat(localTheme?.radius ?? \"0\") || 0;\n\n return (\n <div style={styles.root}>\n {/* Header */}\n <div style={styles.header}>\n <h2 style={styles.title}>Theme</h2>\n <p style={styles.subtitle}>\n Choose a preset or fine-tune individual design tokens. Changes preview instantly.\n </p>\n </div>\n\n {/* Presets \u2014 2-row horizontal scroll */}\n <div style={styles.section}>\n <div style={styles.sectionHeader}>\n <p style={styles.sectionLabel}>Presets</p>\n {hasMore && (\n <button\n type=\"button\"\n style={styles.seeAllBtn}\n onClick={() => setShowModal(true)}\n onMouseEnter={(e) => { e.currentTarget.style.opacity = \"0.7\"; }}\n onMouseLeave={(e) => { e.currentTarget.style.opacity = \"1\"; }}\n >\n See all {presets.length} themes\n </button>\n )}\n </div>\n <div style={styles.presetsScroller}>\n <div style={styles.presetsTrack}>\n {visiblePresets.map((preset) => (\n <PresetCard\n key={preset.id}\n preset={preset}\n isActive={localTheme?.id === preset.id}\n onSelect={() => selectPreset(preset)}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* \"See All\" modal */}\n {showModal && (\n <PresetModal\n presets={presets}\n activeId={localTheme?.id}\n onSelect={selectPreset}\n onClose={() => setShowModal(false)}\n />\n )}\n\n {/* Token Editor */}\n {localTheme && (\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Design Tokens</p>\n {TOKEN_GROUPS.map((group) => (\n <div key={group.label} style={styles.tokenGroup}>\n <div style={styles.tokenGroupHeader}>\n <p style={styles.tokenGroupLabel}>{group.label}</p>\n <p style={styles.tokenGroupDesc}>{group.description}</p>\n </div>\n {group.tokens.map((token) => (\n <TokenRow\n key={token}\n token={token}\n value={localTheme.tokens[token] ?? \"\"}\n onChange={updateToken}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n\n {/* Radius */}\n {localTheme && (\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Border Radius</p>\n <div style={styles.radiusSection}>\n <div style={styles.radiusPreview(localTheme.radius || \"0\")} />\n <input\n type=\"range\"\n min=\"0\"\n max=\"1.5\"\n step=\"0.125\"\n value={radiusNum}\n onChange={(e) => updateRadius(`${e.target.value}rem`)}\n style={styles.rangeInput}\n />\n <span style={styles.radiusValue}>{localTheme.radius || \"0\"}</span>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div style={styles.actions}>\n <button\n type=\"button\"\n style={{\n ...styles.btnPrimary,\n opacity: saving || !hasUnsaved ? 0.5 : 1,\n pointerEvents: saving || !hasUnsaved ? \"none\" : \"auto\",\n }}\n onClick={handleSave}\n disabled={saving || !hasUnsaved}\n >\n {saving ? \"Saving\\u2026\" : \"Save Theme\"}\n </button>\n <button\n type=\"button\"\n style={styles.btnSecondary}\n onClick={handleReset}\n disabled={saving}\n >\n Reset to Default\n </button>\n {savedAt && (\n <span style={styles.saved}>Saved at {savedAt}</span>\n )}\n {hasUnsaved && !savedAt && (\n <span style={{ ...styles.saved, color: \"var(--chart-1)\" }}>Unsaved changes</span>\n )}\n </div>\n </div>\n );\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport {\n usePluginData,\n usePluginAction,\n} from \"@paperclipai/plugin-sdk/ui\";\n\n/* \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface ThemeConfig {\n id: string;\n name: string;\n description: string;\n author: string;\n isDark: boolean;\n tokens: Record<string, string>;\n radius: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/* \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst MAX_VISIBLE_PRESETS = 8;\nconst CARD_WIDTH = 192;\nconst CARD_GAP = 10;\nconst SWATCH_TOKENS = [\"--background\", \"--primary\", \"--accent\", \"--chart-1\", \"--destructive\"];\n\n/* \u2500\u2500\u2500 CSS injection engine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst STYLE_ELEMENT_ID = \"blazo-theme-overrides\";\n\nfunction injectThemeCSS(theme: ThemeConfig): void {\n let el = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!el) {\n el = document.createElement(\"style\");\n el.id = STYLE_ELEMENT_ID;\n document.head.appendChild(el);\n }\n\n const entries = Object.entries(theme.tokens);\n const radiusEntry = theme.radius ? ` --radius: ${theme.radius};` : \"\";\n\n const lines = entries.map(([key, value]) => ` ${key}: ${value};`);\n if (radiusEntry) lines.push(radiusEntry);\n\n const darkToggle = theme.isDark ? \"dark\" : \"light\";\n el.textContent = `:root { ${lines.join(\" \")} }\\n`;\n\n const htmlEl = document.documentElement;\n htmlEl.classList.toggle(\"dark\", theme.isDark);\n htmlEl.classList.toggle(\"light\", !theme.isDark);\n htmlEl.style.colorScheme = darkToggle;\n}\n\nfunction clearThemeCSS(): void {\n const el = document.getElementById(STYLE_ELEMENT_ID);\n if (el) el.remove();\n}\n\n/* \u2500\u2500\u2500 Token groups for the editor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface TokenGroup {\n label: string;\n description: string;\n tokens: string[];\n}\n\nconst TOKEN_GROUPS: TokenGroup[] = [\n {\n label: \"Surfaces\",\n description: \"Background and card colors\",\n tokens: [\"--background\", \"--card\", \"--muted\", \"--accent\"],\n },\n {\n label: \"Text\",\n description: \"Foreground and label colors\",\n tokens: [\"--foreground\", \"--card-foreground\", \"--muted-foreground\", \"--accent-foreground\"],\n },\n {\n label: \"Interactive\",\n description: \"Buttons, links, and primary actions\",\n tokens: [\"--primary\", \"--primary-foreground\"],\n },\n {\n label: \"Feedback\",\n description: \"Errors and destructive actions\",\n tokens: [\"--destructive\", \"--destructive-foreground\"],\n },\n {\n label: \"Structure\",\n description: \"Borders and focus rings\",\n tokens: [\"--border\", \"--input\", \"--ring\"],\n },\n {\n label: \"Charts\",\n description: \"Dashboard visualization colors\",\n tokens: [\"--chart-1\", \"--chart-2\", \"--chart-3\", \"--chart-4\", \"--chart-5\"],\n },\n];\n\n/* \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction tokenDisplayName(token: string): string {\n return token\n .replace(/^--/, \"\")\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\nfunction oklchToHex(oklchStr: string): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const ctx2d = canvas.getContext(\"2d\");\n if (!ctx2d) return \"#000000\";\n ctx2d.fillStyle = oklchStr;\n ctx2d.fillRect(0, 0, 1, 1);\n const [r, g, b] = ctx2d.getImageData(0, 0, 1, 1).data;\n return `#${(r ?? 0).toString(16).padStart(2, \"0\")}${(g ?? 0).toString(16).padStart(2, \"0\")}${(b ?? 0).toString(16).padStart(2, \"0\")}`;\n}\n\nfunction hexToOklch(hex: string): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const ctx2d = canvas.getContext(\"2d\");\n if (!ctx2d) return \"oklch(0% 0 0)\";\n ctx2d.fillStyle = hex;\n ctx2d.fillRect(0, 0, 1, 1);\n const [r, g, b] = ctx2d.getImageData(0, 0, 1, 1).data;\n const rLin = srgbToLinear((r ?? 0) / 255);\n const gLin = srgbToLinear((g ?? 0) / 255);\n const bLin = srgbToLinear((b ?? 0) / 255);\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n const L = 0.2104542553 * lRoot + 0.7936177850 * mRoot - 0.0040720468 * sRoot;\n const a = 1.9779984951 * lRoot - 2.4285922050 * mRoot + 0.4505937099 * sRoot;\n const bOklab = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.8086757660 * sRoot;\n const C = Math.sqrt(a * a + bOklab * bOklab);\n let H = (Math.atan2(bOklab, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return `oklch(${(L * 100).toFixed(1)}% ${C.toFixed(3)} ${H.toFixed(1)})`;\n}\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\n/* \u2500\u2500\u2500 Style definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst styles = {\n root: {\n fontFamily: \"var(--font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif)\",\n color: \"var(--foreground)\",\n maxWidth: 720,\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 32,\n },\n header: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 6,\n },\n title: {\n fontSize: 20,\n fontWeight: 600,\n letterSpacing: \"-0.01em\",\n margin: 0,\n color: \"var(--foreground)\",\n },\n subtitle: {\n fontSize: 13,\n color: \"var(--muted-foreground)\",\n margin: 0,\n lineHeight: 1.5,\n },\n section: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 16,\n },\n sectionHeader: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n margin: 0,\n },\n sectionLabel: {\n fontSize: 13,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.04em\",\n color: \"var(--muted-foreground)\",\n margin: 0,\n },\n seeAllBtn: {\n fontSize: 12,\n fontWeight: 500,\n color: \"var(--primary)\",\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n padding: \"2px 0\",\n outline: \"none\",\n transition: \"opacity 150ms ease\",\n },\n presetsScroller: {\n overflowX: \"auto\" as const,\n overflowY: \"hidden\" as const,\n WebkitOverflowScrolling: \"touch\" as const,\n scrollbarWidth: \"thin\" as const,\n paddingBottom: 4,\n },\n presetsTrack: {\n display: \"grid\",\n gridTemplateRows: \"1fr 1fr\",\n gridAutoFlow: \"column\" as const,\n gridAutoColumns: `${CARD_WIDTH}px`,\n gap: CARD_GAP,\n },\n presetCard: (isActive: boolean) => ({\n position: \"relative\" as const,\n width: CARD_WIDTH,\n padding: \"12px 14px\",\n borderRadius: 8,\n border: `1.5px solid ${isActive ? \"var(--primary)\" : \"var(--border)\"}`,\n background: isActive ? \"var(--accent)\" : \"transparent\",\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 5,\n outline: \"none\",\n boxSizing: \"border-box\" as const,\n textAlign: \"left\" as const,\n }),\n presetName: {\n fontSize: 13,\n fontWeight: 500,\n color: \"var(--foreground)\",\n margin: 0,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n textOverflow: \"ellipsis\" as const,\n },\n presetDesc: {\n fontSize: 11,\n color: \"var(--muted-foreground)\",\n margin: 0,\n lineHeight: 1.35,\n display: \"-webkit-box\",\n WebkitLineClamp: 2,\n WebkitBoxOrient: \"vertical\" as const,\n overflow: \"hidden\" as const,\n },\n presetMeta: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n marginTop: 2,\n },\n presetColors: {\n display: \"flex\",\n gap: 3,\n },\n presetSwatch: (color: string) => ({\n width: 14,\n height: 14,\n borderRadius: 3,\n background: color,\n border: \"1px solid rgba(128,128,128,0.25)\",\n flexShrink: 0,\n }),\n presetModeBadge: (isDark: boolean) => ({\n fontSize: 9,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.06em\",\n color: \"var(--muted-foreground)\",\n background: \"var(--muted)\",\n padding: \"1px 5px\",\n borderRadius: 3,\n lineHeight: \"16px\",\n }),\n activeBadge: {\n position: \"absolute\" as const,\n top: 7,\n right: 8,\n fontSize: 9,\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.06em\",\n color: \"var(--primary-foreground)\",\n background: \"var(--primary)\",\n padding: \"1px 6px\",\n borderRadius: 999,\n lineHeight: \"16px\",\n },\n tokenGroup: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 10,\n padding: \"16px 0\",\n borderBottom: \"1px solid var(--border)\",\n },\n tokenGroupHeader: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 2,\n },\n tokenGroupLabel: {\n fontSize: 14,\n fontWeight: 500,\n color: \"var(--foreground)\",\n margin: 0,\n },\n tokenGroupDesc: {\n fontSize: 12,\n color: \"var(--muted-foreground)\",\n margin: 0,\n },\n tokenRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: 12,\n padding: \"4px 0\",\n },\n colorInput: {\n width: 32,\n height: 32,\n border: \"1.5px solid var(--border)\",\n borderRadius: 6,\n padding: 0,\n cursor: \"pointer\",\n background: \"none\",\n flexShrink: 0,\n outline: \"none\",\n },\n tokenLabel: {\n fontSize: 13,\n fontWeight: 400,\n color: \"var(--foreground)\",\n flex: 1,\n minWidth: 0,\n },\n tokenValue: {\n fontSize: 11,\n fontFamily: \"var(--font-mono, 'SF Mono', Consolas, monospace)\",\n color: \"var(--muted-foreground)\",\n textAlign: \"right\" as const,\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n textOverflow: \"ellipsis\" as const,\n maxWidth: 180,\n },\n radiusSection: {\n display: \"flex\",\n alignItems: \"center\",\n gap: 16,\n padding: \"8px 0\",\n },\n rangeInput: {\n flex: 1,\n accentColor: \"var(--primary)\",\n cursor: \"pointer\",\n },\n radiusValue: {\n fontSize: 13,\n fontFamily: \"var(--font-mono, 'SF Mono', Consolas, monospace)\",\n color: \"var(--muted-foreground)\",\n minWidth: 48,\n textAlign: \"right\" as const,\n },\n radiusPreview: (r: string) => ({\n width: 32,\n height: 32,\n borderRadius: r,\n border: \"2px solid var(--primary)\",\n background: \"var(--accent)\",\n flexShrink: 0,\n }),\n actions: {\n display: \"flex\",\n gap: 10,\n paddingTop: 8,\n },\n btnPrimary: {\n padding: \"9px 20px\",\n borderRadius: 6,\n border: \"none\",\n background: \"var(--primary)\",\n color: \"var(--primary-foreground)\",\n fontSize: 13,\n fontWeight: 500,\n cursor: \"pointer\",\n transition: \"opacity 150ms ease\",\n outline: \"none\",\n },\n btnSecondary: {\n padding: \"9px 20px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"transparent\",\n color: \"var(--foreground)\",\n fontSize: 13,\n fontWeight: 500,\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n outline: \"none\",\n },\n saved: {\n fontSize: 12,\n color: \"var(--muted-foreground)\",\n alignSelf: \"center\" as const,\n transition: \"opacity 300ms ease\",\n },\n overlay: {\n position: \"fixed\" as const,\n inset: 0,\n background: \"rgba(0,0,0,0.55)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 9999,\n backdropFilter: \"blur(4px)\",\n },\n modal: {\n background: \"var(--card)\",\n border: \"1px solid var(--border)\",\n borderRadius: 12,\n width: \"min(640px, calc(100vw - 48px))\",\n maxHeight: \"min(560px, calc(100vh - 80px))\",\n display: \"flex\",\n flexDirection: \"column\" as const,\n boxShadow: \"0 16px 48px rgba(0,0,0,0.25)\",\n overflow: \"hidden\" as const,\n },\n modalHeader: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"16px 20px 0 20px\",\n },\n modalTitle: {\n fontSize: 16,\n fontWeight: 600,\n margin: 0,\n color: \"var(--foreground)\",\n },\n modalCloseBtn: {\n width: 28,\n height: 28,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"none\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n color: \"var(--muted-foreground)\",\n fontSize: 18,\n lineHeight: 1,\n outline: \"none\",\n transition: \"background 150ms ease\",\n },\n modalSearch: {\n margin: \"12px 20px\",\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"var(--muted)\",\n color: \"var(--foreground)\",\n fontSize: 13,\n outline: \"none\",\n width: \"calc(100% - 40px)\",\n boxSizing: \"border-box\" as const,\n transition: \"border-color 150ms ease\",\n },\n modalCount: {\n fontSize: 11,\n color: \"var(--muted-foreground)\",\n padding: \"0 20px 8px 20px\",\n margin: 0,\n },\n modalGrid: {\n flex: 1,\n overflowY: \"auto\" as const,\n padding: \"0 20px 20px 20px\",\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fill, minmax(180px, 1fr))\",\n gap: 10,\n alignContent: \"start\",\n },\n};\n\n/* \u2500\u2500\u2500 Preset Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction PresetCard({\n preset,\n isActive,\n onSelect,\n compact,\n}: {\n preset: ThemeConfig;\n isActive: boolean;\n onSelect: () => void;\n compact?: boolean;\n}) {\n const cardStyle = compact\n ? {\n ...styles.presetCard(isActive),\n width: \"auto\" as const,\n }\n : styles.presetCard(isActive);\n\n return (\n <button\n type=\"button\"\n style={cardStyle}\n onClick={onSelect}\n onMouseEnter={(e) => {\n if (!isActive) (e.currentTarget.style.borderColor = \"var(--muted-foreground)\");\n }}\n onMouseLeave={(e) => {\n if (!isActive) (e.currentTarget.style.borderColor = \"var(--border)\");\n }}\n >\n {isActive && <span style={styles.activeBadge}>Active</span>}\n <p style={styles.presetName}>{preset.name}</p>\n <p style={styles.presetDesc}>{preset.description}</p>\n <div style={styles.presetMeta}>\n <div style={styles.presetColors}>\n {SWATCH_TOKENS.map((t) => (\n <div key={t} style={styles.presetSwatch(preset.tokens[t] ?? \"#333\")} />\n ))}\n </div>\n <span style={styles.presetModeBadge(preset.isDark)}>\n {preset.isDark ? \"Dark\" : \"Light\"}\n </span>\n </div>\n </button>\n );\n}\n\n/* \u2500\u2500\u2500 \"See All\" Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction PresetModal({\n presets,\n activeId,\n onSelect,\n onClose,\n}: {\n presets: ThemeConfig[];\n activeId: string | undefined;\n onSelect: (preset: ThemeConfig) => void;\n onClose: () => void;\n}) {\n const [search, setSearch] = useState(\"\");\n const searchRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n searchRef.current?.focus();\n }, []);\n\n useEffect(() => {\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKey);\n return () => document.removeEventListener(\"keydown\", handleKey);\n }, [onClose]);\n\n const filtered = useMemo(() => {\n if (!search.trim()) return presets;\n const q = search.toLowerCase().trim();\n return presets.filter(\n (p) =>\n p.name.toLowerCase().includes(q) ||\n p.description.toLowerCase().includes(q) ||\n p.author.toLowerCase().includes(q) ||\n (p.isDark ? \"dark\" : \"light\").includes(q),\n );\n }, [presets, search]);\n\n return (\n <div\n style={styles.overlay}\n onClick={(e) => {\n if (e.target === e.currentTarget) onClose();\n }}\n >\n <div style={styles.modal}>\n <div style={styles.modalHeader}>\n <h3 style={styles.modalTitle}>All Themes</h3>\n <button\n type=\"button\"\n style={styles.modalCloseBtn}\n onClick={onClose}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = \"var(--muted)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = \"none\";\n }}\n >\n ✕\n </button>\n </div>\n <input\n ref={searchRef}\n type=\"text\"\n placeholder=\"Search themes...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n style={styles.modalSearch}\n onFocus={(e) => {\n e.currentTarget.style.borderColor = \"var(--primary)\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.borderColor = \"var(--border)\";\n }}\n />\n <p style={styles.modalCount}>\n {filtered.length} of {presets.length} themes\n </p>\n <div style={styles.modalGrid}>\n {filtered.map((preset) => (\n <PresetCard\n key={preset.id}\n preset={preset}\n isActive={activeId === preset.id}\n onSelect={() => {\n onSelect(preset);\n onClose();\n }}\n compact\n />\n ))}\n {filtered.length === 0 && (\n <p style={{ ...styles.subtitle, gridColumn: \"1 / -1\", textAlign: \"center\" as const, padding: \"32px 0\" }}>\n No themes match your search.\n </p>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/* \u2500\u2500\u2500 Token Editor Row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction TokenRow({\n token,\n value,\n onChange,\n}: {\n token: string;\n value: string;\n onChange: (token: string, value: string) => void;\n}) {\n const hex = oklchToHex(value);\n return (\n <div style={styles.tokenRow}>\n <input\n type=\"color\"\n style={styles.colorInput}\n value={hex}\n onChange={(e) => {\n const newOklch = hexToOklch(e.target.value);\n onChange(token, newOklch);\n }}\n title={tokenDisplayName(token)}\n />\n <span style={styles.tokenLabel}>{tokenDisplayName(token)}</span>\n <span style={styles.tokenValue}>{value}</span>\n </div>\n );\n}\n\n/* \u2500\u2500\u2500 Main Settings Page Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport function ThemeSettingsPage() {\n const activeThemeResult = usePluginData<ThemeConfig | null>(\"active-theme\");\n const presetsResult = usePluginData<ThemeConfig[]>(\"presets\");\n const applyTheme = usePluginAction(\"apply-theme\");\n const resetTheme = usePluginAction(\"reset-theme\");\n const importThemeAction = usePluginAction(\"import-theme\");\n\n const [localTheme, setLocalTheme] = useState<ThemeConfig | null>(null);\n const [saving, setSaving] = useState(false);\n const [savedAt, setSavedAt] = useState<string | null>(null);\n const [hasUnsaved, setHasUnsaved] = useState(false);\n const [showModal, setShowModal] = useState(false);\n const [importError, setImportError] = useState<string | null>(null);\n const initialLoadDone = useRef(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const presets: ThemeConfig[] = presetsResult.data ?? [];\n const serverTheme: ThemeConfig | null = activeThemeResult.data ?? null;\n\n const visiblePresets = presets.slice(0, MAX_VISIBLE_PRESETS);\n const hasMore = presets.length > MAX_VISIBLE_PRESETS;\n\n useEffect(() => {\n if (initialLoadDone.current) return;\n if (serverTheme) {\n setLocalTheme(serverTheme);\n injectThemeCSS(serverTheme);\n initialLoadDone.current = true;\n } else if (presets.length > 0 && activeThemeResult.data === null) {\n initialLoadDone.current = true;\n }\n }, [serverTheme, presets, activeThemeResult.data]);\n\n const selectPreset = useCallback(\n (preset: ThemeConfig) => {\n const next = { ...preset, updatedAt: new Date().toISOString() };\n setLocalTheme(next);\n injectThemeCSS(next);\n setHasUnsaved(true);\n setSavedAt(null);\n },\n [],\n );\n\n const updateToken = useCallback(\n (token: string, value: string) => {\n setLocalTheme((prev) => {\n if (!prev) return prev;\n const next = {\n ...prev,\n tokens: { ...prev.tokens, [token]: value },\n updatedAt: new Date().toISOString(),\n };\n injectThemeCSS(next);\n return next;\n });\n setHasUnsaved(true);\n setSavedAt(null);\n },\n [],\n );\n\n const updateRadius = useCallback((value: string) => {\n setLocalTheme((prev) => {\n if (!prev) return prev;\n const next = { ...prev, radius: value, updatedAt: new Date().toISOString() };\n injectThemeCSS(next);\n return next;\n });\n setHasUnsaved(true);\n setSavedAt(null);\n }, []);\n\n const handleSave = useCallback(async () => {\n if (!localTheme) return;\n setSaving(true);\n try {\n await applyTheme(localTheme);\n setHasUnsaved(false);\n setSavedAt(new Date().toLocaleTimeString());\n } catch (err) {\n console.error(\"Failed to save theme:\", err);\n } finally {\n setSaving(false);\n }\n }, [localTheme, applyTheme]);\n\n const handleReset = useCallback(async () => {\n setSaving(true);\n try {\n const result = await resetTheme({});\n const restored = result as unknown as ThemeConfig;\n setLocalTheme(restored);\n injectThemeCSS(restored);\n setHasUnsaved(false);\n setSavedAt(null);\n } catch (err) {\n console.error(\"Failed to reset theme:\", err);\n } finally {\n setSaving(false);\n }\n }, [resetTheme]);\n\n const handleExport = useCallback(() => {\n if (!localTheme) return;\n const payload = JSON.stringify(localTheme, null, 2);\n const blob = new Blob([payload], { type: \"application/json\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `${localTheme.id}.theme.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }, [localTheme]);\n\n const handleImportFile = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n setImportError(null);\n const file = e.target.files?.[0];\n if (!file) return;\n try {\n const text = await file.text();\n const parsed: unknown = JSON.parse(text);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as Record<string, unknown>).id !== \"string\" ||\n typeof (parsed as Record<string, unknown>).name !== \"string\" ||\n typeof (parsed as Record<string, unknown>).tokens !== \"object\" ||\n typeof (parsed as Record<string, unknown>).isDark !== \"boolean\"\n ) {\n setImportError(\"Invalid theme file: must contain id, name, tokens, and isDark fields.\");\n return;\n }\n const result = await importThemeAction(parsed);\n const imported = result as unknown as ThemeConfig;\n setLocalTheme(imported);\n injectThemeCSS(imported);\n setHasUnsaved(true);\n setSavedAt(null);\n } catch (err) {\n setImportError(`Import failed: ${String(err)}`);\n } finally {\n if (fileInputRef.current) fileInputRef.current.value = \"\";\n }\n },\n [importThemeAction],\n );\n\n const radiusNum = parseFloat(localTheme?.radius ?? \"0\") || 0;\n\n return (\n <div style={styles.root}>\n {/* Header */}\n <div style={styles.header}>\n <h2 style={styles.title}>Theme</h2>\n <p style={styles.subtitle}>\n Choose a preset or fine-tune individual design tokens. Changes preview instantly.\n </p>\n </div>\n\n {/* Presets \u2014 2-row horizontal scroll */}\n <div style={styles.section}>\n <div style={styles.sectionHeader}>\n <p style={styles.sectionLabel}>Presets</p>\n {hasMore && (\n <button\n type=\"button\"\n style={styles.seeAllBtn}\n onClick={() => setShowModal(true)}\n onMouseEnter={(e) => { e.currentTarget.style.opacity = \"0.7\"; }}\n onMouseLeave={(e) => { e.currentTarget.style.opacity = \"1\"; }}\n >\n See all {presets.length} themes\n </button>\n )}\n </div>\n <div style={styles.presetsScroller}>\n <div style={styles.presetsTrack}>\n {visiblePresets.map((preset) => (\n <PresetCard\n key={preset.id}\n preset={preset}\n isActive={localTheme?.id === preset.id}\n onSelect={() => selectPreset(preset)}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* \"See All\" modal */}\n {showModal && (\n <PresetModal\n presets={presets}\n activeId={localTheme?.id}\n onSelect={selectPreset}\n onClose={() => setShowModal(false)}\n />\n )}\n\n {/* Token Editor */}\n {localTheme && (\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Design Tokens</p>\n {TOKEN_GROUPS.map((group) => (\n <div key={group.label} style={styles.tokenGroup}>\n <div style={styles.tokenGroupHeader}>\n <p style={styles.tokenGroupLabel}>{group.label}</p>\n <p style={styles.tokenGroupDesc}>{group.description}</p>\n </div>\n {group.tokens.map((token) => (\n <TokenRow\n key={token}\n token={token}\n value={localTheme.tokens[token] ?? \"\"}\n onChange={updateToken}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n\n {/* Radius */}\n {localTheme && (\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Border Radius</p>\n <div style={styles.radiusSection}>\n <div style={styles.radiusPreview(localTheme.radius || \"0\")} />\n <input\n type=\"range\"\n min=\"0\"\n max=\"1.5\"\n step=\"0.125\"\n value={radiusNum}\n onChange={(e) => updateRadius(`${e.target.value}rem`)}\n style={styles.rangeInput}\n />\n <span style={styles.radiusValue}>{localTheme.radius || \"0\"}</span>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div style={styles.actions}>\n <button\n type=\"button\"\n style={{\n ...styles.btnPrimary,\n opacity: saving || !hasUnsaved ? 0.5 : 1,\n pointerEvents: saving || !hasUnsaved ? \"none\" : \"auto\",\n }}\n onClick={handleSave}\n disabled={saving || !hasUnsaved}\n >\n {saving ? \"Saving\\u2026\" : \"Save Theme\"}\n </button>\n <button\n type=\"button\"\n style={styles.btnSecondary}\n onClick={handleReset}\n disabled={saving}\n >\n Reset to Default\n </button>\n {savedAt && (\n <span style={styles.saved}>Saved at {savedAt}</span>\n )}\n {hasUnsaved && !savedAt && (\n <span style={{ ...styles.saved, color: \"var(--chart-1)\" }}>Unsaved changes</span>\n )}\n </div>\n\n {/* Import / Export */}\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Share</p>\n <p style={{ ...styles.subtitle, marginTop: -8 }}>\n Export your current theme as a JSON file to share with others, or import a community theme.\n </p>\n <div style={styles.actions}>\n <button\n type=\"button\"\n style={{\n ...styles.btnSecondary,\n opacity: localTheme ? 1 : 0.5,\n pointerEvents: localTheme ? \"auto\" : \"none\",\n }}\n onClick={handleExport}\n disabled={!localTheme}\n >\n Export Theme\n </button>\n <button\n type=\"button\"\n style={styles.btnSecondary}\n onClick={() => fileInputRef.current?.click()}\n >\n Import Theme\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".json,application/json\"\n style={{ display: \"none\" }}\n onChange={handleImportFile}\n />\n </div>\n {importError && (\n <p style={{ fontSize: 12, color: \"var(--destructive)\", margin: 0 }}>\n {importError}\n </p>\n )}\n </div>\n </div>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;AAwHjC,QAAA,MAAM,MAAM,EAAE,eAoHZ,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
package/dist/worker.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { definePlugin, runWorker, } from "@paperclipai/plugin-sdk";
|
|
2
|
-
import { STATE_KEYS, DATA_ENDPOINTS, ACTION_NAMES, THEME_PRESETS, } from "./constants.js";
|
|
2
|
+
import { STATE_KEYS, DATA_ENDPOINTS, ACTION_NAMES, THEME_PRESETS, REMOTE_REGISTRY_URL, } from "./constants.js";
|
|
3
3
|
let currentContext = null;
|
|
4
|
+
/** In-memory cache of remote themes, refreshed on startup. */
|
|
5
|
+
let remoteThemes = [];
|
|
6
|
+
/* ─── State helpers ──────────────────────────────────────────────── */
|
|
4
7
|
async function readTheme(ctx) {
|
|
5
8
|
return (await ctx.state.get({
|
|
6
9
|
scopeKind: "instance",
|
|
@@ -10,21 +13,85 @@ async function readTheme(ctx) {
|
|
|
10
13
|
async function writeTheme(ctx, theme) {
|
|
11
14
|
await ctx.state.set({ scopeKind: "instance", stateKey: STATE_KEYS.activeTheme }, theme);
|
|
12
15
|
}
|
|
16
|
+
async function readUserThemes(ctx) {
|
|
17
|
+
const stored = await ctx.state.get({
|
|
18
|
+
scopeKind: "instance",
|
|
19
|
+
stateKey: STATE_KEYS.userThemes,
|
|
20
|
+
});
|
|
21
|
+
if (!Array.isArray(stored))
|
|
22
|
+
return [];
|
|
23
|
+
return stored;
|
|
24
|
+
}
|
|
25
|
+
async function writeUserThemes(ctx, themes) {
|
|
26
|
+
await ctx.state.set({ scopeKind: "instance", stateKey: STATE_KEYS.userThemes }, themes);
|
|
27
|
+
}
|
|
28
|
+
function isValidTheme(t) {
|
|
29
|
+
if (typeof t !== "object" || t === null)
|
|
30
|
+
return false;
|
|
31
|
+
const obj = t;
|
|
32
|
+
return (typeof obj.id === "string" &&
|
|
33
|
+
typeof obj.name === "string" &&
|
|
34
|
+
typeof obj.tokens === "object" &&
|
|
35
|
+
obj.tokens !== null &&
|
|
36
|
+
typeof obj.isDark === "boolean");
|
|
37
|
+
}
|
|
38
|
+
async function fetchRemoteThemes(logger) {
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(REMOTE_REGISTRY_URL);
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
logger.warn(`Remote theme registry returned ${response.status}, using bundled fallback`);
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const payload = await response.json();
|
|
46
|
+
if (typeof payload !== "object" ||
|
|
47
|
+
payload === null ||
|
|
48
|
+
!Array.isArray(payload.themes)) {
|
|
49
|
+
logger.warn("Remote theme registry has invalid shape, using bundled fallback");
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const themes = payload.themes.filter(isValidTheme);
|
|
53
|
+
logger.info(`Fetched ${themes.length} themes from remote registry`);
|
|
54
|
+
return themes;
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
logger.warn(`Failed to fetch remote theme registry: ${String(err)}`);
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/* ─── Merge logic: remote wins over bundled, user themes appended ── */
|
|
62
|
+
function mergePresets(bundled, remote, user) {
|
|
63
|
+
const merged = new Map();
|
|
64
|
+
for (const t of bundled)
|
|
65
|
+
merged.set(t.id, t);
|
|
66
|
+
for (const t of remote)
|
|
67
|
+
merged.set(t.id, t);
|
|
68
|
+
const result = Array.from(merged.values());
|
|
69
|
+
for (const t of user) {
|
|
70
|
+
if (!merged.has(t.id)) {
|
|
71
|
+
result.push(t);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
/* ─── Plugin definition ──────────────────────────────────────────── */
|
|
13
77
|
const plugin = definePlugin({
|
|
14
78
|
async setup(ctx) {
|
|
15
79
|
currentContext = ctx;
|
|
80
|
+
remoteThemes = await fetchRemoteThemes(ctx.logger);
|
|
16
81
|
ctx.data.register(DATA_ENDPOINTS.activeTheme, async () => {
|
|
17
82
|
return await readTheme(ctx);
|
|
18
83
|
});
|
|
19
84
|
ctx.data.register(DATA_ENDPOINTS.presets, async () => {
|
|
20
|
-
|
|
85
|
+
const userThemes = await readUserThemes(ctx);
|
|
86
|
+
return mergePresets(THEME_PRESETS, remoteThemes, userThemes);
|
|
21
87
|
});
|
|
22
88
|
ctx.actions.register(ACTION_NAMES.applyTheme, async (params) => {
|
|
23
89
|
const incoming = params;
|
|
24
90
|
if (!incoming.id) {
|
|
25
91
|
throw new Error("Theme id is required");
|
|
26
92
|
}
|
|
27
|
-
const
|
|
93
|
+
const allPresets = mergePresets(THEME_PRESETS, remoteThemes, await readUserThemes(ctx));
|
|
94
|
+
const preset = allPresets.find((p) => p.id === incoming.id);
|
|
28
95
|
const theme = preset
|
|
29
96
|
? {
|
|
30
97
|
...preset,
|
|
@@ -47,23 +114,58 @@ const plugin = definePlugin({
|
|
|
47
114
|
return theme;
|
|
48
115
|
});
|
|
49
116
|
ctx.actions.register(ACTION_NAMES.resetTheme, async () => {
|
|
50
|
-
const
|
|
117
|
+
const allPresets = mergePresets(THEME_PRESETS, remoteThemes, await readUserThemes(ctx));
|
|
118
|
+
const defaultTheme = allPresets[0];
|
|
51
119
|
if (!defaultTheme) {
|
|
52
120
|
throw new Error("No default preset found");
|
|
53
121
|
}
|
|
54
122
|
await writeTheme(ctx, defaultTheme);
|
|
55
123
|
return defaultTheme;
|
|
56
124
|
});
|
|
125
|
+
ctx.actions.register(ACTION_NAMES.importTheme, async (params) => {
|
|
126
|
+
const incoming = params;
|
|
127
|
+
if (!isValidTheme(incoming)) {
|
|
128
|
+
throw new Error("Invalid theme: must include id (string), name (string), tokens (object), isDark (boolean)");
|
|
129
|
+
}
|
|
130
|
+
const theme = {
|
|
131
|
+
id: incoming.id,
|
|
132
|
+
name: incoming.name,
|
|
133
|
+
description: incoming.description ?? "",
|
|
134
|
+
author: incoming.author ?? "Community",
|
|
135
|
+
isDark: incoming.isDark,
|
|
136
|
+
tokens: incoming.tokens,
|
|
137
|
+
radius: incoming.radius ?? "0",
|
|
138
|
+
createdAt: incoming.createdAt ?? new Date().toISOString(),
|
|
139
|
+
updatedAt: new Date().toISOString(),
|
|
140
|
+
};
|
|
141
|
+
const existing = await readUserThemes(ctx);
|
|
142
|
+
const filtered = existing.filter((t) => t.id !== theme.id);
|
|
143
|
+
filtered.push(theme);
|
|
144
|
+
await writeUserThemes(ctx, filtered);
|
|
145
|
+
return theme;
|
|
146
|
+
});
|
|
147
|
+
ctx.actions.register(ACTION_NAMES.removeUserTheme, async (params) => {
|
|
148
|
+
const { id } = params;
|
|
149
|
+
if (!id)
|
|
150
|
+
throw new Error("Theme id is required");
|
|
151
|
+
const existing = await readUserThemes(ctx);
|
|
152
|
+
const filtered = existing.filter((t) => t.id !== id);
|
|
153
|
+
await writeUserThemes(ctx, filtered);
|
|
154
|
+
return { removed: id };
|
|
155
|
+
});
|
|
57
156
|
},
|
|
58
157
|
async onHealth() {
|
|
59
158
|
const ctx = currentContext;
|
|
60
159
|
const activeTheme = ctx ? await readTheme(ctx) : null;
|
|
160
|
+
const userThemes = ctx ? await readUserThemes(ctx) : [];
|
|
61
161
|
return {
|
|
62
162
|
status: "ok",
|
|
63
163
|
message: "Theme Customizer ready",
|
|
64
164
|
details: {
|
|
65
165
|
activePreset: activeTheme?.name ?? "Default (unset)",
|
|
66
|
-
|
|
166
|
+
bundledPresets: THEME_PRESETS.length,
|
|
167
|
+
remotePresets: remoteThemes.length,
|
|
168
|
+
userImported: userThemes.length,
|
|
67
169
|
},
|
|
68
170
|
};
|
|
69
171
|
},
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,SAAS,GAIV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,UAAU,EACV,cAAc,EACd,YAAY,EACZ,aAAa,
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|