paperclip-theme 0.2.0 → 0.2.1

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/ui/index.js CHANGED
@@ -9,6 +9,26 @@ var MAX_VISIBLE_PRESETS = 8;
9
9
  var CARD_WIDTH = 192;
10
10
  var CARD_GAP = 10;
11
11
  var SWATCH_TOKENS = ["--background", "--primary", "--accent", "--chart-1", "--destructive"];
12
+ var FONT_OPTIONS_SANS = [
13
+ { label: "System Default", value: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" },
14
+ { label: "Inter", value: "'Inter', sans-serif" },
15
+ { label: "Geist", value: "'Geist', sans-serif" },
16
+ { label: "DM Sans", value: "'DM Sans', sans-serif" },
17
+ { label: "Manrope", value: "'Manrope', sans-serif" },
18
+ { label: "Plus Jakarta Sans", value: "'Plus Jakarta Sans', sans-serif" },
19
+ { label: "IBM Plex Sans", value: "'IBM Plex Sans', sans-serif" },
20
+ { label: "Nunito", value: "'Nunito', sans-serif" },
21
+ { label: "Custom", value: "__custom__" }
22
+ ];
23
+ var FONT_OPTIONS_MONO = [
24
+ { label: "System Default", value: "'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace" },
25
+ { label: "Geist Mono", value: "'Geist Mono', monospace" },
26
+ { label: "JetBrains Mono", value: "'JetBrains Mono', monospace" },
27
+ { label: "Fira Code", value: "'Fira Code', monospace" },
28
+ { label: "IBM Plex Mono", value: "'IBM Plex Mono', monospace" },
29
+ { label: "Cascadia Code", value: "'Cascadia Code', monospace" },
30
+ { label: "Custom", value: "__custom__" }
31
+ ];
12
32
  var STYLE_ELEMENT_ID = "blazo-theme-overrides";
13
33
  function injectThemeCSS(theme) {
14
34
  let el = document.getElementById(STYLE_ELEMENT_ID);
@@ -336,6 +356,43 @@ var styles = {
336
356
  background: "var(--accent)",
337
357
  flexShrink: 0
338
358
  }),
359
+ stickyBar: {
360
+ position: "sticky",
361
+ top: 0,
362
+ zIndex: 50,
363
+ display: "flex",
364
+ alignItems: "center",
365
+ justifyContent: "space-between",
366
+ gap: 12,
367
+ padding: "10px 16px",
368
+ background: "var(--card)",
369
+ borderBottom: "1px solid var(--border)",
370
+ borderRadius: 8,
371
+ marginBottom: -8
372
+ },
373
+ stickyThemeInfo: {
374
+ display: "flex",
375
+ flexDirection: "column",
376
+ gap: 1,
377
+ minWidth: 0
378
+ },
379
+ stickyThemeName: {
380
+ fontSize: 13,
381
+ fontWeight: 600,
382
+ color: "var(--foreground)",
383
+ whiteSpace: "nowrap",
384
+ overflow: "hidden",
385
+ textOverflow: "ellipsis"
386
+ },
387
+ stickyStatus: {
388
+ fontSize: 11,
389
+ color: "var(--muted-foreground)"
390
+ },
391
+ stickyBtns: {
392
+ display: "flex",
393
+ gap: 8,
394
+ flexShrink: 0
395
+ },
339
396
  actions: {
340
397
  display: "flex",
341
398
  gap: 10,
@@ -371,6 +428,41 @@ var styles = {
371
428
  alignSelf: "center",
372
429
  transition: "opacity 300ms ease"
373
430
  },
431
+ fontRow: {
432
+ display: "flex",
433
+ flexDirection: "column",
434
+ gap: 4,
435
+ padding: "6px 0"
436
+ },
437
+ fontLabel: {
438
+ fontSize: 13,
439
+ fontWeight: 400,
440
+ color: "var(--foreground)"
441
+ },
442
+ fontSelect: {
443
+ padding: "7px 10px",
444
+ borderRadius: 6,
445
+ border: "1.5px solid var(--border)",
446
+ background: "var(--muted)",
447
+ color: "var(--foreground)",
448
+ fontSize: 13,
449
+ cursor: "pointer",
450
+ outline: "none",
451
+ width: "100%"
452
+ },
453
+ fontCustomInput: {
454
+ marginTop: 6,
455
+ padding: "7px 10px",
456
+ borderRadius: 6,
457
+ border: "1.5px solid var(--border)",
458
+ background: "var(--muted)",
459
+ color: "var(--foreground)",
460
+ fontSize: 12,
461
+ fontFamily: "var(--font-mono, monospace)",
462
+ outline: "none",
463
+ width: "100%",
464
+ boxSizing: "border-box"
465
+ },
374
466
  overlay: {
375
467
  position: "fixed",
376
468
  inset: 0,
@@ -601,6 +693,56 @@ function TokenRow({
601
693
  /* @__PURE__ */ jsx("span", { style: styles.tokenValue, children: value })
602
694
  ] });
603
695
  }
696
+ function FontRow({
697
+ label,
698
+ token,
699
+ value,
700
+ options,
701
+ onChange
702
+ }) {
703
+ const knownOption = options.find((o) => o.value !== "__custom__" && o.value === value);
704
+ const isCustom = !knownOption && value !== "";
705
+ const selectValue = isCustom ? "__custom__" : value || options[0].value;
706
+ const [showCustom, setShowCustom] = useState(isCustom);
707
+ return /* @__PURE__ */ jsxs("div", { style: styles.fontRow, children: [
708
+ /* @__PURE__ */ jsx("span", { style: styles.fontLabel, children: label }),
709
+ /* @__PURE__ */ jsx(
710
+ "select",
711
+ {
712
+ style: styles.fontSelect,
713
+ value: selectValue,
714
+ onChange: (e) => {
715
+ if (e.target.value === "__custom__") {
716
+ setShowCustom(true);
717
+ } else {
718
+ setShowCustom(false);
719
+ onChange(token, e.target.value);
720
+ }
721
+ },
722
+ children: options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
723
+ }
724
+ ),
725
+ showCustom && /* @__PURE__ */ jsx(
726
+ "input",
727
+ {
728
+ type: "text",
729
+ placeholder: "e.g. 'Roboto', sans-serif",
730
+ defaultValue: isCustom ? value : "",
731
+ style: styles.fontCustomInput,
732
+ onBlur: (e) => {
733
+ const v = e.target.value.trim();
734
+ if (v) onChange(token, v);
735
+ },
736
+ onKeyDown: (e) => {
737
+ if (e.key === "Enter") {
738
+ const v = e.target.value.trim();
739
+ if (v) onChange(token, v);
740
+ }
741
+ }
742
+ }
743
+ )
744
+ ] });
745
+ }
604
746
  function ThemeSettingsPage() {
605
747
  const activeThemeResult = usePluginData("active-theme");
606
748
  const presetsResult = usePluginData("presets");
@@ -735,6 +877,44 @@ function ThemeSettingsPage() {
735
877
  );
736
878
  const radiusNum = parseFloat(localTheme?.radius ?? "0") || 0;
737
879
  return /* @__PURE__ */ jsxs("div", { style: styles.root, children: [
880
+ /* @__PURE__ */ jsxs("div", { style: styles.stickyBar, children: [
881
+ /* @__PURE__ */ jsxs("div", { style: styles.stickyThemeInfo, children: [
882
+ /* @__PURE__ */ jsx("span", { style: styles.stickyThemeName, children: localTheme ? localTheme.name : "No theme selected" }),
883
+ /* @__PURE__ */ jsx("span", { style: styles.stickyStatus, children: saving ? "Saving\u2026" : savedAt ? `Saved at ${savedAt}` : hasUnsaved ? "Unsaved changes" : "Saved" })
884
+ ] }),
885
+ /* @__PURE__ */ jsxs("div", { style: styles.stickyBtns, children: [
886
+ /* @__PURE__ */ jsx(
887
+ "button",
888
+ {
889
+ type: "button",
890
+ style: {
891
+ ...styles.btnSecondary,
892
+ padding: "7px 14px",
893
+ fontSize: 12
894
+ },
895
+ onClick: handleReset,
896
+ disabled: saving,
897
+ children: "Reset"
898
+ }
899
+ ),
900
+ /* @__PURE__ */ jsx(
901
+ "button",
902
+ {
903
+ type: "button",
904
+ style: {
905
+ ...styles.btnPrimary,
906
+ padding: "7px 14px",
907
+ fontSize: 12,
908
+ opacity: saving || !hasUnsaved ? 0.45 : 1,
909
+ pointerEvents: saving || !hasUnsaved ? "none" : "auto"
910
+ },
911
+ onClick: handleSave,
912
+ disabled: saving || !hasUnsaved,
913
+ children: "Save Theme"
914
+ }
915
+ )
916
+ ] })
917
+ ] }),
738
918
  /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
739
919
  /* @__PURE__ */ jsx("h2", { style: styles.title, children: "Theme" }),
740
920
  /* @__PURE__ */ jsx("p", { style: styles.subtitle, children: "Choose a preset or fine-tune individual design tokens. Changes preview instantly." })
@@ -799,6 +979,30 @@ function ThemeSettingsPage() {
799
979
  ))
800
980
  ] }, group.label))
801
981
  ] }),
982
+ localTheme && /* @__PURE__ */ jsxs("div", { style: styles.section, children: [
983
+ /* @__PURE__ */ jsx("p", { style: styles.sectionLabel, children: "Typography" }),
984
+ /* @__PURE__ */ jsx("p", { style: { ...styles.subtitle, marginTop: -8 }, children: "Choose fonts for the UI. Select a preset or enter a custom CSS font stack." }),
985
+ /* @__PURE__ */ jsx(
986
+ FontRow,
987
+ {
988
+ label: "Sans-serif (UI font)",
989
+ token: "--font-sans",
990
+ value: localTheme.tokens["--font-sans"] ?? "",
991
+ options: FONT_OPTIONS_SANS,
992
+ onChange: updateToken
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx(
996
+ FontRow,
997
+ {
998
+ label: "Monospace (code font)",
999
+ token: "--font-mono",
1000
+ value: localTheme.tokens["--font-mono"] ?? "",
1001
+ options: FONT_OPTIONS_MONO,
1002
+ onChange: updateToken
1003
+ }
1004
+ )
1005
+ ] }),
802
1006
  localTheme && /* @__PURE__ */ jsxs("div", { style: styles.section, children: [
803
1007
  /* @__PURE__ */ jsx("p", { style: styles.sectionLabel, children: "Border Radius" }),
804
1008
  /* @__PURE__ */ jsxs("div", { style: styles.radiusSection, children: [
@@ -818,37 +1022,6 @@ function ThemeSettingsPage() {
818
1022
  /* @__PURE__ */ jsx("span", { style: styles.radiusValue, children: localTheme.radius || "0" })
819
1023
  ] })
820
1024
  ] }),
821
- /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
822
- /* @__PURE__ */ jsx(
823
- "button",
824
- {
825
- type: "button",
826
- style: {
827
- ...styles.btnPrimary,
828
- opacity: saving || !hasUnsaved ? 0.5 : 1,
829
- pointerEvents: saving || !hasUnsaved ? "none" : "auto"
830
- },
831
- onClick: handleSave,
832
- disabled: saving || !hasUnsaved,
833
- children: saving ? "Saving\u2026" : "Save Theme"
834
- }
835
- ),
836
- /* @__PURE__ */ jsx(
837
- "button",
838
- {
839
- type: "button",
840
- style: styles.btnSecondary,
841
- onClick: handleReset,
842
- disabled: saving,
843
- children: "Reset to Default"
844
- }
845
- ),
846
- savedAt && /* @__PURE__ */ jsxs("span", { style: styles.saved, children: [
847
- "Saved at ",
848
- savedAt
849
- ] }),
850
- hasUnsaved && !savedAt && /* @__PURE__ */ jsx("span", { style: { ...styles.saved, color: "var(--chart-1)" }, children: "Unsaved changes" })
851
- ] }),
852
1025
  /* @__PURE__ */ jsxs("div", { style: styles.section, children: [
853
1026
  /* @__PURE__ */ jsx("p", { style: styles.sectionLabel, children: "Share" }),
854
1027
  /* @__PURE__ */ jsx("p", { style: { ...styles.subtitle, marginTop: -8 }, children: "Export your current theme as a JSON file to share with others, or import a community theme." }),
@@ -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 &#x2715;\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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",
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\nconst FONT_OPTIONS_SANS = [\n { label: \"System Default\", value: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\" },\n { label: \"Inter\", value: \"'Inter', sans-serif\" },\n { label: \"Geist\", value: \"'Geist', sans-serif\" },\n { label: \"DM Sans\", value: \"'DM Sans', sans-serif\" },\n { label: \"Manrope\", value: \"'Manrope', sans-serif\" },\n { label: \"Plus Jakarta Sans\", value: \"'Plus Jakarta Sans', sans-serif\" },\n { label: \"IBM Plex Sans\", value: \"'IBM Plex Sans', sans-serif\" },\n { label: \"Nunito\", value: \"'Nunito', sans-serif\" },\n { label: \"Custom\", value: \"__custom__\" },\n];\n\nconst FONT_OPTIONS_MONO = [\n { label: \"System Default\", value: \"'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace\" },\n { label: \"Geist Mono\", value: \"'Geist Mono', monospace\" },\n { label: \"JetBrains Mono\", value: \"'JetBrains Mono', monospace\" },\n { label: \"Fira Code\", value: \"'Fira Code', monospace\" },\n { label: \"IBM Plex Mono\", value: \"'IBM Plex Mono', monospace\" },\n { label: \"Cascadia Code\", value: \"'Cascadia Code', monospace\" },\n { label: \"Custom\", value: \"__custom__\" },\n];\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 stickyBar: {\n position: \"sticky\" as const,\n top: 0,\n zIndex: 50,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 12,\n padding: \"10px 16px\",\n background: \"var(--card)\",\n borderBottom: \"1px solid var(--border)\",\n borderRadius: 8,\n marginBottom: -8,\n },\n stickyThemeInfo: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 1,\n minWidth: 0,\n },\n stickyThemeName: {\n fontSize: 13,\n fontWeight: 600,\n color: \"var(--foreground)\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\" as const,\n textOverflow: \"ellipsis\" as const,\n },\n stickyStatus: {\n fontSize: 11,\n color: \"var(--muted-foreground)\",\n },\n stickyBtns: {\n display: \"flex\",\n gap: 8,\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 fontRow: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: 4,\n padding: \"6px 0\",\n },\n fontLabel: {\n fontSize: 13,\n fontWeight: 400,\n color: \"var(--foreground)\",\n },\n fontSelect: {\n padding: \"7px 10px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"var(--muted)\",\n color: \"var(--foreground)\",\n fontSize: 13,\n cursor: \"pointer\",\n outline: \"none\",\n width: \"100%\",\n },\n fontCustomInput: {\n marginTop: 6,\n padding: \"7px 10px\",\n borderRadius: 6,\n border: \"1.5px solid var(--border)\",\n background: \"var(--muted)\",\n color: \"var(--foreground)\",\n fontSize: 12,\n fontFamily: \"var(--font-mono, monospace)\",\n outline: \"none\",\n width: \"100%\",\n boxSizing: \"border-box\" as const,\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 &#x2715;\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 Font 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction FontRow({\n label,\n token,\n value,\n options,\n onChange,\n}: {\n label: string;\n token: string;\n value: string;\n options: { label: string; value: string }[];\n onChange: (token: string, value: string) => void;\n}) {\n const knownOption = options.find((o) => o.value !== \"__custom__\" && o.value === value);\n const isCustom = !knownOption && value !== \"\";\n const selectValue = isCustom ? \"__custom__\" : (value || options[0]!.value);\n const [showCustom, setShowCustom] = useState(isCustom);\n\n return (\n <div style={styles.fontRow}>\n <span style={styles.fontLabel}>{label}</span>\n <select\n style={styles.fontSelect}\n value={selectValue}\n onChange={(e) => {\n if (e.target.value === \"__custom__\") {\n setShowCustom(true);\n } else {\n setShowCustom(false);\n onChange(token, e.target.value);\n }\n }}\n >\n {options.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n {showCustom && (\n <input\n type=\"text\"\n placeholder=\"e.g. 'Roboto', sans-serif\"\n defaultValue={isCustom ? value : \"\"}\n style={styles.fontCustomInput}\n onBlur={(e) => {\n const v = e.target.value.trim();\n if (v) onChange(token, v);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n const v = (e.target as HTMLInputElement).value.trim();\n if (v) onChange(token, v);\n }\n }}\n />\n )}\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 {/* Sticky Save Bar */}\n <div style={styles.stickyBar}>\n <div style={styles.stickyThemeInfo}>\n <span style={styles.stickyThemeName}>\n {localTheme ? localTheme.name : \"No theme selected\"}\n </span>\n <span style={styles.stickyStatus}>\n {saving\n ? \"Saving\\u2026\"\n : savedAt\n ? `Saved at ${savedAt}`\n : hasUnsaved\n ? \"Unsaved changes\"\n : \"Saved\"}\n </span>\n </div>\n <div style={styles.stickyBtns}>\n <button\n type=\"button\"\n style={{\n ...styles.btnSecondary,\n padding: \"7px 14px\",\n fontSize: 12,\n }}\n onClick={handleReset}\n disabled={saving}\n >\n Reset\n </button>\n <button\n type=\"button\"\n style={{\n ...styles.btnPrimary,\n padding: \"7px 14px\",\n fontSize: 12,\n opacity: saving || !hasUnsaved ? 0.45 : 1,\n pointerEvents: saving || !hasUnsaved ? \"none\" : \"auto\",\n }}\n onClick={handleSave}\n disabled={saving || !hasUnsaved}\n >\n Save Theme\n </button>\n </div>\n </div>\n\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 {/* Typography */}\n {localTheme && (\n <div style={styles.section}>\n <p style={styles.sectionLabel}>Typography</p>\n <p style={{ ...styles.subtitle, marginTop: -8 }}>\n Choose fonts for the UI. Select a preset or enter a custom CSS font stack.\n </p>\n <FontRow\n label=\"Sans-serif (UI font)\"\n token=\"--font-sans\"\n value={localTheme.tokens[\"--font-sans\"] ?? \"\"}\n options={FONT_OPTIONS_SANS}\n onChange={updateToken}\n />\n <FontRow\n label=\"Monospace (code font)\"\n token=\"--font-mono\"\n value={localTheme.tokens[\"--font-mono\"] ?? \"\"}\n options={FONT_OPTIONS_MONO}\n onChange={updateToken}\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 {/* 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-theme",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "In-app theme customizer for Paperclip with live preview, presets, and shareable configs",
5
5
  "type": "module",
6
6
  "exports": {
package/src/ui/index.tsx CHANGED
@@ -25,6 +25,28 @@ const CARD_WIDTH = 192;
25
25
  const CARD_GAP = 10;
26
26
  const SWATCH_TOKENS = ["--background", "--primary", "--accent", "--chart-1", "--destructive"];
27
27
 
28
+ const FONT_OPTIONS_SANS = [
29
+ { label: "System Default", value: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" },
30
+ { label: "Inter", value: "'Inter', sans-serif" },
31
+ { label: "Geist", value: "'Geist', sans-serif" },
32
+ { label: "DM Sans", value: "'DM Sans', sans-serif" },
33
+ { label: "Manrope", value: "'Manrope', sans-serif" },
34
+ { label: "Plus Jakarta Sans", value: "'Plus Jakarta Sans', sans-serif" },
35
+ { label: "IBM Plex Sans", value: "'IBM Plex Sans', sans-serif" },
36
+ { label: "Nunito", value: "'Nunito', sans-serif" },
37
+ { label: "Custom", value: "__custom__" },
38
+ ];
39
+
40
+ const FONT_OPTIONS_MONO = [
41
+ { label: "System Default", value: "'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace" },
42
+ { label: "Geist Mono", value: "'Geist Mono', monospace" },
43
+ { label: "JetBrains Mono", value: "'JetBrains Mono', monospace" },
44
+ { label: "Fira Code", value: "'Fira Code', monospace" },
45
+ { label: "IBM Plex Mono", value: "'IBM Plex Mono', monospace" },
46
+ { label: "Cascadia Code", value: "'Cascadia Code', monospace" },
47
+ { label: "Custom", value: "__custom__" },
48
+ ];
49
+
28
50
  /* ─── CSS injection engine ───────────────────────────────────────── */
29
51
 
30
52
  const STYLE_ELEMENT_ID = "blazo-theme-overrides";
@@ -385,6 +407,43 @@ const styles = {
385
407
  background: "var(--accent)",
386
408
  flexShrink: 0,
387
409
  }),
410
+ stickyBar: {
411
+ position: "sticky" as const,
412
+ top: 0,
413
+ zIndex: 50,
414
+ display: "flex",
415
+ alignItems: "center",
416
+ justifyContent: "space-between",
417
+ gap: 12,
418
+ padding: "10px 16px",
419
+ background: "var(--card)",
420
+ borderBottom: "1px solid var(--border)",
421
+ borderRadius: 8,
422
+ marginBottom: -8,
423
+ },
424
+ stickyThemeInfo: {
425
+ display: "flex",
426
+ flexDirection: "column" as const,
427
+ gap: 1,
428
+ minWidth: 0,
429
+ },
430
+ stickyThemeName: {
431
+ fontSize: 13,
432
+ fontWeight: 600,
433
+ color: "var(--foreground)",
434
+ whiteSpace: "nowrap" as const,
435
+ overflow: "hidden" as const,
436
+ textOverflow: "ellipsis" as const,
437
+ },
438
+ stickyStatus: {
439
+ fontSize: 11,
440
+ color: "var(--muted-foreground)",
441
+ },
442
+ stickyBtns: {
443
+ display: "flex",
444
+ gap: 8,
445
+ flexShrink: 0,
446
+ },
388
447
  actions: {
389
448
  display: "flex",
390
449
  gap: 10,
@@ -420,6 +479,41 @@ const styles = {
420
479
  alignSelf: "center" as const,
421
480
  transition: "opacity 300ms ease",
422
481
  },
482
+ fontRow: {
483
+ display: "flex",
484
+ flexDirection: "column" as const,
485
+ gap: 4,
486
+ padding: "6px 0",
487
+ },
488
+ fontLabel: {
489
+ fontSize: 13,
490
+ fontWeight: 400,
491
+ color: "var(--foreground)",
492
+ },
493
+ fontSelect: {
494
+ padding: "7px 10px",
495
+ borderRadius: 6,
496
+ border: "1.5px solid var(--border)",
497
+ background: "var(--muted)",
498
+ color: "var(--foreground)",
499
+ fontSize: 13,
500
+ cursor: "pointer",
501
+ outline: "none",
502
+ width: "100%",
503
+ },
504
+ fontCustomInput: {
505
+ marginTop: 6,
506
+ padding: "7px 10px",
507
+ borderRadius: 6,
508
+ border: "1.5px solid var(--border)",
509
+ background: "var(--muted)",
510
+ color: "var(--foreground)",
511
+ fontSize: 12,
512
+ fontFamily: "var(--font-mono, monospace)",
513
+ outline: "none",
514
+ width: "100%",
515
+ boxSizing: "border-box" as const,
516
+ },
423
517
  overlay: {
424
518
  position: "fixed" as const,
425
519
  inset: 0,
@@ -683,6 +777,69 @@ function TokenRow({
683
777
  );
684
778
  }
685
779
 
780
+ /* ─── Font Row ───────────────────────────────────────────────────── */
781
+
782
+ function FontRow({
783
+ label,
784
+ token,
785
+ value,
786
+ options,
787
+ onChange,
788
+ }: {
789
+ label: string;
790
+ token: string;
791
+ value: string;
792
+ options: { label: string; value: string }[];
793
+ onChange: (token: string, value: string) => void;
794
+ }) {
795
+ const knownOption = options.find((o) => o.value !== "__custom__" && o.value === value);
796
+ const isCustom = !knownOption && value !== "";
797
+ const selectValue = isCustom ? "__custom__" : (value || options[0]!.value);
798
+ const [showCustom, setShowCustom] = useState(isCustom);
799
+
800
+ return (
801
+ <div style={styles.fontRow}>
802
+ <span style={styles.fontLabel}>{label}</span>
803
+ <select
804
+ style={styles.fontSelect}
805
+ value={selectValue}
806
+ onChange={(e) => {
807
+ if (e.target.value === "__custom__") {
808
+ setShowCustom(true);
809
+ } else {
810
+ setShowCustom(false);
811
+ onChange(token, e.target.value);
812
+ }
813
+ }}
814
+ >
815
+ {options.map((o) => (
816
+ <option key={o.value} value={o.value}>
817
+ {o.label}
818
+ </option>
819
+ ))}
820
+ </select>
821
+ {showCustom && (
822
+ <input
823
+ type="text"
824
+ placeholder="e.g. 'Roboto', sans-serif"
825
+ defaultValue={isCustom ? value : ""}
826
+ style={styles.fontCustomInput}
827
+ onBlur={(e) => {
828
+ const v = e.target.value.trim();
829
+ if (v) onChange(token, v);
830
+ }}
831
+ onKeyDown={(e) => {
832
+ if (e.key === "Enter") {
833
+ const v = (e.target as HTMLInputElement).value.trim();
834
+ if (v) onChange(token, v);
835
+ }
836
+ }}
837
+ />
838
+ )}
839
+ </div>
840
+ );
841
+ }
842
+
686
843
  /* ─── Main Settings Page Component ───────────────────────────────── */
687
844
 
688
845
  export function ThemeSettingsPage() {
@@ -840,6 +997,52 @@ export function ThemeSettingsPage() {
840
997
 
841
998
  return (
842
999
  <div style={styles.root}>
1000
+ {/* Sticky Save Bar */}
1001
+ <div style={styles.stickyBar}>
1002
+ <div style={styles.stickyThemeInfo}>
1003
+ <span style={styles.stickyThemeName}>
1004
+ {localTheme ? localTheme.name : "No theme selected"}
1005
+ </span>
1006
+ <span style={styles.stickyStatus}>
1007
+ {saving
1008
+ ? "Saving\u2026"
1009
+ : savedAt
1010
+ ? `Saved at ${savedAt}`
1011
+ : hasUnsaved
1012
+ ? "Unsaved changes"
1013
+ : "Saved"}
1014
+ </span>
1015
+ </div>
1016
+ <div style={styles.stickyBtns}>
1017
+ <button
1018
+ type="button"
1019
+ style={{
1020
+ ...styles.btnSecondary,
1021
+ padding: "7px 14px",
1022
+ fontSize: 12,
1023
+ }}
1024
+ onClick={handleReset}
1025
+ disabled={saving}
1026
+ >
1027
+ Reset
1028
+ </button>
1029
+ <button
1030
+ type="button"
1031
+ style={{
1032
+ ...styles.btnPrimary,
1033
+ padding: "7px 14px",
1034
+ fontSize: 12,
1035
+ opacity: saving || !hasUnsaved ? 0.45 : 1,
1036
+ pointerEvents: saving || !hasUnsaved ? "none" : "auto",
1037
+ }}
1038
+ onClick={handleSave}
1039
+ disabled={saving || !hasUnsaved}
1040
+ >
1041
+ Save Theme
1042
+ </button>
1043
+ </div>
1044
+ </div>
1045
+
843
1046
  {/* Header */}
844
1047
  <div style={styles.header}>
845
1048
  <h2 style={styles.title}>Theme</h2>
@@ -911,6 +1114,30 @@ export function ThemeSettingsPage() {
911
1114
  </div>
912
1115
  )}
913
1116
 
1117
+ {/* Typography */}
1118
+ {localTheme && (
1119
+ <div style={styles.section}>
1120
+ <p style={styles.sectionLabel}>Typography</p>
1121
+ <p style={{ ...styles.subtitle, marginTop: -8 }}>
1122
+ Choose fonts for the UI. Select a preset or enter a custom CSS font stack.
1123
+ </p>
1124
+ <FontRow
1125
+ label="Sans-serif (UI font)"
1126
+ token="--font-sans"
1127
+ value={localTheme.tokens["--font-sans"] ?? ""}
1128
+ options={FONT_OPTIONS_SANS}
1129
+ onChange={updateToken}
1130
+ />
1131
+ <FontRow
1132
+ label="Monospace (code font)"
1133
+ token="--font-mono"
1134
+ value={localTheme.tokens["--font-mono"] ?? ""}
1135
+ options={FONT_OPTIONS_MONO}
1136
+ onChange={updateToken}
1137
+ />
1138
+ </div>
1139
+ )}
1140
+
914
1141
  {/* Radius */}
915
1142
  {localTheme && (
916
1143
  <div style={styles.section}>
@@ -931,36 +1158,6 @@ export function ThemeSettingsPage() {
931
1158
  </div>
932
1159
  )}
933
1160
 
934
- {/* Actions */}
935
- <div style={styles.actions}>
936
- <button
937
- type="button"
938
- style={{
939
- ...styles.btnPrimary,
940
- opacity: saving || !hasUnsaved ? 0.5 : 1,
941
- pointerEvents: saving || !hasUnsaved ? "none" : "auto",
942
- }}
943
- onClick={handleSave}
944
- disabled={saving || !hasUnsaved}
945
- >
946
- {saving ? "Saving\u2026" : "Save Theme"}
947
- </button>
948
- <button
949
- type="button"
950
- style={styles.btnSecondary}
951
- onClick={handleReset}
952
- disabled={saving}
953
- >
954
- Reset to Default
955
- </button>
956
- {savedAt && (
957
- <span style={styles.saved}>Saved at {savedAt}</span>
958
- )}
959
- {hasUnsaved && !savedAt && (
960
- <span style={{ ...styles.saved, color: "var(--chart-1)" }}>Unsaved changes</span>
961
- )}
962
- </div>
963
-
964
1161
  {/* Import / Export */}
965
1162
  <div style={styles.section}>
966
1163
  <p style={styles.sectionLabel}>Share</p>