@motion-proto/live-tokens 0.1.0 → 0.3.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/README.md +160 -21
- package/dist-plugin/index.cjs +823 -336
- package/dist-plugin/index.d.cts +9 -7
- package/dist-plugin/index.d.ts +9 -7
- package/dist-plugin/index.js +822 -335
- package/package.json +51 -23
- package/src/assets/newspaper.webp +0 -0
- package/src/assets/offering.webp +0 -0
- package/src/component-editor/BadgeEditor.svelte +170 -0
- package/src/component-editor/CalloutEditor.svelte +103 -0
- package/src/component-editor/CardEditor.svelte +184 -0
- package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
- package/src/component-editor/CornerBadgeEditor.svelte +207 -0
- package/src/component-editor/DialogEditor.svelte +172 -0
- package/src/component-editor/ImageEditor.svelte +72 -0
- package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
- package/src/component-editor/NotificationEditor.svelte +160 -0
- package/src/component-editor/ProgressBarEditor.svelte +124 -0
- package/src/component-editor/RadioButtonEditor.svelte +140 -0
- package/src/component-editor/SectionDividerEditor.svelte +263 -0
- package/src/component-editor/SegmentedControlEditor.svelte +154 -0
- package/src/component-editor/StandardButtonsEditor.svelte +178 -0
- package/src/component-editor/TabBarEditor.svelte +137 -0
- package/src/component-editor/TableEditor.svelte +128 -0
- package/src/component-editor/TooltipEditor.svelte +122 -0
- package/src/component-editor/editorTokens.test.ts +93 -0
- package/src/component-editor/groupKeySlots.test.ts +67 -0
- package/src/component-editor/groupKeySnapshot.test.ts +52 -0
- package/src/component-editor/index.ts +5 -0
- package/src/component-editor/registry.ts +246 -0
- package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
- package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
- package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
- package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
- package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
- package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
- package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
- package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
- package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
- package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
- package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
- package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
- package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
- package/src/component-editor/scaffolding/componentSources.ts +9 -0
- package/src/component-editor/scaffolding/defaultSections.ts +16 -0
- package/src/component-editor/scaffolding/editorContext.ts +44 -0
- package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
- package/src/component-editor/scaffolding/siblings.ts +33 -0
- package/src/component-editor/scaffolding/types.ts +39 -0
- package/src/components/Badge.svelte +231 -42
- package/src/components/Button.svelte +324 -124
- package/src/components/Callout.svelte +145 -0
- package/src/components/Card.svelte +123 -25
- package/src/components/CollapsibleSection.svelte +213 -35
- package/src/components/CornerBadge.svelte +224 -0
- package/src/components/Dialog.svelte +137 -114
- package/src/components/Image.svelte +43 -0
- package/src/components/InlineEditActions.svelte +74 -14
- package/src/components/Notification.svelte +184 -163
- package/src/components/ProgressBar.svelte +216 -22
- package/src/components/RadioButton.svelte +110 -40
- package/src/components/SectionDivider.svelte +428 -74
- package/src/components/SegmentedControl.svelte +203 -0
- package/src/components/TabBar.svelte +146 -21
- package/src/components/Table.svelte +102 -0
- package/src/components/Tooltip.svelte +45 -19
- package/src/components/types.ts +51 -0
- package/src/data/google-fonts.json +75 -0
- package/src/lib/ColumnsOverlay.svelte +20 -7
- package/src/lib/LiveEditorOverlay.svelte +265 -82
- package/src/lib/columnsOverlay.ts +21 -17
- package/src/lib/componentConfig.test.ts +204 -0
- package/src/lib/componentConfigKeys.ts +19 -0
- package/src/lib/componentConfigService.ts +88 -0
- package/src/lib/copyPopover.ts +30 -0
- package/src/lib/cssVarSync.ts +59 -7
- package/src/lib/editorConfigStore.ts +0 -10
- package/src/lib/editorCore.ts +402 -0
- package/src/lib/editorKeybindings.ts +52 -0
- package/src/lib/editorPersistence.ts +106 -0
- package/src/lib/editorRenderer.ts +74 -0
- package/src/lib/editorStore.test.ts +328 -0
- package/src/lib/editorStore.ts +412 -0
- package/src/lib/editorTypes.ts +100 -0
- package/src/lib/editorViewStore.ts +55 -0
- package/src/lib/files/versionedFileResource.ts +140 -0
- package/src/lib/fontLoader.ts +130 -0
- package/src/lib/fontMigration.ts +140 -0
- package/src/lib/fontParse.ts +168 -0
- package/src/lib/index.ts +48 -31
- package/src/lib/lazyConfig.test.ts +54 -0
- package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
- package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
- package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
- package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
- package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
- package/src/lib/migrations/index.ts +93 -0
- package/src/lib/migrations/migrations.test.ts +341 -0
- package/src/lib/navLinkTypes.ts +1 -0
- package/src/lib/overlayState.ts +3 -0
- package/src/lib/paletteDerivation.ts +300 -0
- package/src/lib/parentRouteStore.ts +42 -0
- package/src/lib/parsers/globalRootBlock.ts +32 -0
- package/src/lib/presetService.ts +94 -0
- package/src/lib/router.ts +49 -0
- package/src/lib/scrollSection.ts +45 -0
- package/src/lib/slices/columns.ts +59 -0
- package/src/lib/slices/components.ts +362 -0
- package/src/lib/slices/domainVars.ts +15 -0
- package/src/lib/slices/fonts.ts +30 -0
- package/src/lib/slices/gradients.ts +153 -0
- package/src/lib/slices/overlays.ts +132 -0
- package/src/lib/slices/palettes.ts +26 -0
- package/src/lib/slices/shadows.ts +123 -0
- package/src/lib/storage.ts +88 -0
- package/src/lib/themeInit.ts +74 -0
- package/src/lib/themeService.ts +101 -0
- package/src/lib/themeTypes.ts +146 -0
- package/src/lib/tokenRegistry.ts +148 -0
- package/src/pages/ComponentEditorPage.svelte +384 -0
- package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
- package/src/pages/Editor.svelte +98 -0
- package/src/pages/Editor.svelte.d.ts +2 -0
- package/src/pages/EditorShell.svelte +348 -0
- package/src/styles/_padding.scss +34 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
- package/src/styles/fonts.css +22 -10
- package/src/styles/form-controls.css +14 -16
- package/src/styles/tokens.css +1322 -0
- package/src/styles/ui-editor.css +126 -0
- package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
- package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
- package/src/ui/EditorViewSwitcher.svelte +180 -0
- package/src/ui/FontStackEditor.svelte +360 -0
- package/src/ui/GradientEditor.svelte +461 -0
- package/src/ui/GradientStopPicker.svelte +74 -0
- package/src/ui/PaletteEditor.svelte +1590 -0
- package/src/ui/PaletteEditor.test.ts +108 -0
- package/src/ui/PresetFileManager.svelte +567 -0
- package/src/ui/ProjectFontsSection.svelte +645 -0
- package/src/{showcase → ui}/SurfacesTab.svelte +39 -41
- package/src/{showcase → ui}/TextTab.svelte +27 -29
- package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
- package/src/ui/Toggle.svelte +108 -0
- package/src/ui/UICopyPopover.svelte +78 -0
- package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
- package/src/ui/UIFontFamilySelector.svelte +309 -0
- package/src/ui/UIFontSizeSelector.svelte +165 -0
- package/src/ui/UIFontWeightSelector.svelte +52 -0
- package/src/ui/UILineHeightSelector.svelte +47 -0
- package/src/ui/UILinkToggle.svelte +60 -0
- package/src/ui/UIOptionItem.svelte +74 -0
- package/src/ui/UIOptionList.svelte +27 -0
- package/src/ui/UIPaddingSelector.svelte +661 -0
- package/src/ui/UIPaletteSelector.svelte +1084 -0
- package/src/ui/UIRadio.svelte +72 -0
- package/src/ui/UIRadioGroup.svelte +59 -0
- package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
- package/src/ui/UITokenSelector.svelte +509 -0
- package/src/ui/UIVariantSelector.svelte +145 -0
- package/src/ui/VariablesTab.svelte +252 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/keepInViewport.ts +84 -0
- package/src/ui/palette/GradientStopEditor.svelte +482 -0
- package/src/ui/palette/OverridesPanel.svelte +526 -0
- package/src/ui/palette/PaletteBase.svelte +165 -0
- package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
- package/src/ui/palette/paletteEditorState.ts +89 -0
- package/src/ui/sections/ColumnsSection.svelte +273 -0
- package/src/ui/sections/GradientsSection.svelte +147 -0
- package/src/ui/sections/OverlaysSection.svelte +670 -0
- package/src/ui/sections/ShadowsSection.svelte +1250 -0
- package/src/ui/sections/TokenScaleTable.svelte +332 -0
- package/src/ui/sections/tokenScales.ts +81 -0
- package/src/ui/variantScales.ts +108 -0
- package/src/components/DetailNav.svelte +0 -78
- package/src/components/Toggle.svelte +0 -86
- package/src/lib/pageSource.ts +0 -6
- package/src/lib/tokenInit.ts +0 -29
- package/src/lib/tokenService.ts +0 -144
- package/src/lib/tokenTypes.ts +0 -45
- package/src/pages/Admin.svelte +0 -100
- package/src/pages/ShowcasePage.svelte +0 -146
- package/src/showcase/BackupBrowser.svelte +0 -617
- package/src/showcase/ComponentsTab.svelte +0 -107
- package/src/showcase/PaletteEditor.svelte +0 -2579
- package/src/showcase/PaletteSelector.svelte +0 -627
- package/src/showcase/TokenMap.svelte +0 -54
- package/src/showcase/VariablesTab.svelte +0 -2657
- package/src/showcase/VisualsTab.svelte +0 -233
- package/src/showcase/demos/BadgeDemo.svelte +0 -58
- package/src/showcase/demos/CardDemo.svelte +0 -52
- package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -194
- package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -56
- package/src/showcase/demos/DialogDemo.svelte +0 -42
- package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -27
- package/src/showcase/demos/NotificationDemo.svelte +0 -149
- package/src/showcase/demos/ProgressBarDemo.svelte +0 -56
- package/src/showcase/demos/RadioButtonDemo.svelte +0 -58
- package/src/showcase/demos/SectionDividerDemo.svelte +0 -79
- package/src/showcase/demos/StandardButtonsDemo.svelte +0 -457
- package/src/showcase/demos/TabBarDemo.svelte +0 -60
- package/src/showcase/demos/TooltipDemo.svelte +0 -54
- package/src/showcase/editor.css +0 -93
- package/src/showcase/index.ts +0 -17
- package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
- package/src/styles/fonts/Domine/OFL.txt +0 -97
- package/src/styles/fonts/Domine/README.txt +0 -66
- /package/src/{showcase → ui}/curveEngine.ts +0 -0
package/dist-plugin/index.cjs
CHANGED
|
@@ -30,84 +30,153 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/vite-plugin/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
33
|
+
themeFileApi: () => themeFileApi
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
|
-
// src/vite-plugin/
|
|
37
|
+
// src/vite-plugin/themeFileApi.ts
|
|
38
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
39
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
40
|
+
|
|
41
|
+
// src/lib/parsers/globalRootBlock.ts
|
|
42
|
+
function extractGlobalRootBody(source) {
|
|
43
|
+
const re = /:global\(:root\)\s*\{([^}]*)\}/g;
|
|
44
|
+
const bodies = [];
|
|
45
|
+
let m;
|
|
46
|
+
while ((m = re.exec(source)) !== null) {
|
|
47
|
+
bodies.push(m[1]);
|
|
48
|
+
}
|
|
49
|
+
return bodies.join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/lib/files/versionedFileResource.ts
|
|
53
|
+
function sanitizeFileName(name) {
|
|
54
|
+
return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9\-_]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unnamed";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/vite-plugin/files/versionedFileResource.ts
|
|
38
58
|
var import_fs = __toESM(require("fs"), 1);
|
|
39
59
|
var import_path = __toESM(require("path"), 1);
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
function ensureTokensDir() {
|
|
49
|
-
if (!import_fs.default.existsSync(TOKENS_DIR)) {
|
|
50
|
-
import_fs.default.mkdirSync(TOKENS_DIR, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
if (!import_fs.default.existsSync(import_path.default.join(TOKENS_DIR, "default.json"))) {
|
|
53
|
-
const defaultTokens = {
|
|
54
|
-
name: "Default",
|
|
55
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
56
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
57
|
-
editorConfigs: {},
|
|
58
|
-
cssVariables: {}
|
|
59
|
-
};
|
|
60
|
-
import_fs.default.writeFileSync(import_path.default.join(TOKENS_DIR, "default.json"), JSON.stringify(defaultTokens, null, 2));
|
|
61
|
-
}
|
|
62
|
-
if (!import_fs.default.existsSync(ACTIVE_FILE)) {
|
|
63
|
-
import_fs.default.writeFileSync(ACTIVE_FILE, JSON.stringify({ activeFile: "default" }));
|
|
64
|
-
}
|
|
65
|
-
if (!import_fs.default.existsSync(PRODUCTION_FILE)) {
|
|
66
|
-
const activeFile = getActiveFileName();
|
|
67
|
-
import_fs.default.writeFileSync(PRODUCTION_FILE, JSON.stringify({ productionFile: activeFile }));
|
|
60
|
+
function versionedFileResourceServer(opts) {
|
|
61
|
+
const dir = opts.dir;
|
|
62
|
+
const activePath = import_path.default.join(dir, "_active.json");
|
|
63
|
+
const productionPath = import_path.default.join(dir, "_production.json");
|
|
64
|
+
const defaultName = opts.defaultName ?? "default";
|
|
65
|
+
function ensureDir() {
|
|
66
|
+
if (!import_fs.default.existsSync(dir)) {
|
|
67
|
+
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
function
|
|
71
|
-
if (!import_fs.default.existsSync(
|
|
72
|
-
|
|
73
|
-
import_fs.default.mkdirSync(TOKENS_BACKUP_DIR, { recursive: true });
|
|
70
|
+
function ensureMeta() {
|
|
71
|
+
if (!import_fs.default.existsSync(activePath)) {
|
|
72
|
+
import_fs.default.writeFileSync(activePath, JSON.stringify({ activeFile: defaultName }));
|
|
74
73
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
import_fs.default.copyFileSync(filePath, backupPath);
|
|
78
|
-
const allBackups = import_fs.default.readdirSync(TOKENS_BACKUP_DIR).filter((f) => f.startsWith(`${fileName}_`) && f.endsWith(".json")).sort().reverse();
|
|
79
|
-
for (const old of allBackups.slice(10)) {
|
|
80
|
-
import_fs.default.unlinkSync(import_path.default.join(TOKENS_BACKUP_DIR, old));
|
|
74
|
+
if (!import_fs.default.existsSync(productionPath)) {
|
|
75
|
+
import_fs.default.writeFileSync(productionPath, JSON.stringify({ productionFile: defaultName }));
|
|
81
76
|
}
|
|
82
77
|
}
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
if (!import_fs.default.existsSync(CSS_BACKUP_DIR)) {
|
|
86
|
-
import_fs.default.mkdirSync(CSS_BACKUP_DIR, { recursive: true });
|
|
87
|
-
}
|
|
88
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
89
|
-
const backupPath = import_path.default.join(CSS_BACKUP_DIR, `variables_${timestamp}.css`);
|
|
90
|
-
import_fs.default.copyFileSync(CSS_PATH, backupPath);
|
|
91
|
-
const allBackups = import_fs.default.readdirSync(CSS_BACKUP_DIR).filter((f) => f.startsWith("variables_") && f.endsWith(".css")).sort().reverse();
|
|
92
|
-
for (const old of allBackups.slice(10)) {
|
|
93
|
-
import_fs.default.unlinkSync(import_path.default.join(CSS_BACKUP_DIR, old));
|
|
94
|
-
}
|
|
78
|
+
function filePath(name) {
|
|
79
|
+
return import_path.default.join(dir, `${name}.json`);
|
|
95
80
|
}
|
|
96
|
-
function
|
|
81
|
+
function getActiveName() {
|
|
97
82
|
try {
|
|
98
|
-
const data = JSON.parse(import_fs.default.readFileSync(
|
|
99
|
-
return data.activeFile ||
|
|
83
|
+
const data = JSON.parse(import_fs.default.readFileSync(activePath, "utf-8"));
|
|
84
|
+
return data.activeFile || defaultName;
|
|
100
85
|
} catch {
|
|
101
|
-
return
|
|
86
|
+
return defaultName;
|
|
102
87
|
}
|
|
103
88
|
}
|
|
104
|
-
function
|
|
89
|
+
function getProductionName() {
|
|
105
90
|
try {
|
|
106
|
-
const data = JSON.parse(import_fs.default.readFileSync(
|
|
107
|
-
return data.productionFile ||
|
|
91
|
+
const data = JSON.parse(import_fs.default.readFileSync(productionPath, "utf-8"));
|
|
92
|
+
return data.productionFile || defaultName;
|
|
108
93
|
} catch {
|
|
109
|
-
return
|
|
94
|
+
return defaultName;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function setActiveName(name) {
|
|
98
|
+
import_fs.default.writeFileSync(activePath, JSON.stringify({ activeFile: name }));
|
|
99
|
+
}
|
|
100
|
+
function setProductionName(name) {
|
|
101
|
+
import_fs.default.writeFileSync(productionPath, JSON.stringify({ productionFile: name }));
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
dir,
|
|
105
|
+
activePath,
|
|
106
|
+
productionPath,
|
|
107
|
+
ensureDir,
|
|
108
|
+
ensureMeta,
|
|
109
|
+
filePath,
|
|
110
|
+
getActiveName,
|
|
111
|
+
getProductionName,
|
|
112
|
+
setActiveName,
|
|
113
|
+
setProductionName
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/vite-plugin/files/routeTable.ts
|
|
118
|
+
async function dispatch(req, res, routes) {
|
|
119
|
+
const url = req.url || "";
|
|
120
|
+
const method = req.method || "GET";
|
|
121
|
+
for (const route of routes) {
|
|
122
|
+
if (route.method !== method) continue;
|
|
123
|
+
let params = [];
|
|
124
|
+
if (typeof route.pattern === "string") {
|
|
125
|
+
if (url !== route.pattern) continue;
|
|
126
|
+
} else {
|
|
127
|
+
const m = url.match(route.pattern);
|
|
128
|
+
if (!m) continue;
|
|
129
|
+
params = m.slice(1);
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
await route.handler({ url, params, req, res });
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (!res.writableEnded) {
|
|
135
|
+
res.statusCode = 500;
|
|
136
|
+
res.setHeader("Content-Type", "application/json");
|
|
137
|
+
res.end(JSON.stringify({ error: err?.message ?? "Internal error" }));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/vite-plugin/themeFileApi.ts
|
|
146
|
+
function themeFileApi(opts) {
|
|
147
|
+
const THEMES_DIR = import_path2.default.resolve(opts.themesDir);
|
|
148
|
+
const CSS_PATH = import_path2.default.resolve(opts.tokensCssPath);
|
|
149
|
+
const FONTS_CSS_PATH = opts.fontsCssPath ? import_path2.default.resolve(opts.fontsCssPath) : import_path2.default.join(import_path2.default.dirname(CSS_PATH), "fonts.css");
|
|
150
|
+
const API_BASE = opts.apiBase ?? "/api";
|
|
151
|
+
const COMPONENT_CONFIGS_DIR = opts.componentConfigsDir ? import_path2.default.resolve(opts.componentConfigsDir) : import_path2.default.resolve("component-configs");
|
|
152
|
+
const COMPONENTS_SRC_DIR = opts.componentsSrcDir ? import_path2.default.resolve(opts.componentsSrcDir) : import_path2.default.resolve("src/components");
|
|
153
|
+
const PRESETS_DIR = opts.presetsDir ? import_path2.default.resolve(opts.presetsDir) : import_path2.default.resolve("presets");
|
|
154
|
+
const themesResource = versionedFileResourceServer({
|
|
155
|
+
dir: THEMES_DIR
|
|
156
|
+
});
|
|
157
|
+
const componentResourceCache = /* @__PURE__ */ new Map();
|
|
158
|
+
function componentResource(comp) {
|
|
159
|
+
let r = componentResourceCache.get(comp);
|
|
160
|
+
if (!r) {
|
|
161
|
+
r = versionedFileResourceServer({ dir: import_path2.default.join(COMPONENT_CONFIGS_DIR, comp) });
|
|
162
|
+
componentResourceCache.set(comp, r);
|
|
163
|
+
}
|
|
164
|
+
return r;
|
|
165
|
+
}
|
|
166
|
+
const presetsResource = versionedFileResourceServer({ dir: PRESETS_DIR });
|
|
167
|
+
function ensureThemesDir() {
|
|
168
|
+
themesResource.ensureDir();
|
|
169
|
+
if (!import_fs2.default.existsSync(import_path2.default.join(THEMES_DIR, "default.json"))) {
|
|
170
|
+
const defaultTheme = {
|
|
171
|
+
name: "Default",
|
|
172
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
173
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
174
|
+
editorConfigs: {},
|
|
175
|
+
cssVariables: {}
|
|
176
|
+
};
|
|
177
|
+
import_fs2.default.writeFileSync(import_path2.default.join(THEMES_DIR, "default.json"), JSON.stringify(defaultTheme, null, 2));
|
|
110
178
|
}
|
|
179
|
+
themesResource.ensureMeta();
|
|
111
180
|
}
|
|
112
181
|
function readBody(req) {
|
|
113
182
|
return new Promise((resolve, reject) => {
|
|
@@ -122,16 +191,48 @@ function tokenFileApi(opts) {
|
|
|
122
191
|
res.setHeader("Content-Type", "application/json");
|
|
123
192
|
res.end(JSON.stringify(data));
|
|
124
193
|
}
|
|
125
|
-
|
|
126
|
-
|
|
194
|
+
const SYSTEM_CASCADES_SSR = {
|
|
195
|
+
"system-ui-sans": 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
196
|
+
"system-ui-serif": 'Georgia, "Times New Roman", serif',
|
|
197
|
+
"system-ui-mono": 'ui-monospace, "SF Mono", Menlo, Consolas, monospace'
|
|
198
|
+
};
|
|
199
|
+
function resolveFontStacks(themeData) {
|
|
200
|
+
const stacks = themeData.fontStacks;
|
|
201
|
+
const sources = themeData.fontSources;
|
|
202
|
+
if (!stacks || stacks.length === 0) return {};
|
|
203
|
+
const familyById = /* @__PURE__ */ new Map();
|
|
204
|
+
for (const src of sources ?? []) {
|
|
205
|
+
for (const f of src.families) familyById.set(f.id, f.cssName);
|
|
206
|
+
}
|
|
207
|
+
const out = {};
|
|
208
|
+
for (const stack of stacks) {
|
|
209
|
+
const parts = [];
|
|
210
|
+
for (const slot of stack.slots) {
|
|
211
|
+
if (slot.kind === "project") {
|
|
212
|
+
const css = familyById.get(slot.familyId);
|
|
213
|
+
if (css) parts.push(css);
|
|
214
|
+
} else if (slot.kind === "system") {
|
|
215
|
+
const cascade = SYSTEM_CASCADES_SSR[slot.preset];
|
|
216
|
+
if (cascade) parts.push(cascade);
|
|
217
|
+
} else if (slot.kind === "generic") {
|
|
218
|
+
parts.push(slot.value);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (parts.length > 0) out[stack.variable] = parts.join(", ");
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
127
224
|
}
|
|
128
225
|
function syncTokensToCss(fileName) {
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
131
|
-
const
|
|
132
|
-
const cssVars =
|
|
226
|
+
const themePath = import_path2.default.join(THEMES_DIR, `${fileName}.json`);
|
|
227
|
+
if (!import_fs2.default.existsSync(themePath)) return;
|
|
228
|
+
const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
|
|
229
|
+
const cssVars = { ...themeData.cssVariables || {} };
|
|
230
|
+
const resolvedFontVars = resolveFontStacks(themeData);
|
|
231
|
+
for (const [name, value] of Object.entries(resolvedFontVars)) {
|
|
232
|
+
cssVars[name] = value;
|
|
233
|
+
}
|
|
133
234
|
if (Object.keys(cssVars).length === 0) return;
|
|
134
|
-
const cssContent =
|
|
235
|
+
const cssContent = import_fs2.default.readFileSync(CSS_PATH, "utf-8");
|
|
135
236
|
const remaining = new Set(Object.keys(cssVars));
|
|
136
237
|
const updatedContent = cssContent.replace(
|
|
137
238
|
/^(\s*)(--[\w-]+):\s*(.+);/gm,
|
|
@@ -155,290 +256,676 @@ ${newVars}
|
|
|
155
256
|
}$1`
|
|
156
257
|
);
|
|
157
258
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.log(`[syncTokensToCss] Wrote ${Object.keys(cssVars).length} variables from "${fileName}" into variables.css`);
|
|
259
|
+
import_fs2.default.writeFileSync(CSS_PATH, finalContent);
|
|
260
|
+
console.log(`[syncTokensToCss] Wrote ${Object.keys(cssVars).length} variables from "${fileName}" into tokens.css`);
|
|
161
261
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
262
|
+
function syncFontsToCss(fileName) {
|
|
263
|
+
const themePath = import_path2.default.join(THEMES_DIR, `${fileName}.json`);
|
|
264
|
+
if (!import_fs2.default.existsSync(themePath)) return;
|
|
265
|
+
const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
|
|
266
|
+
const sources = themeData.fontSources;
|
|
267
|
+
if (!sources) return;
|
|
268
|
+
const lines = [];
|
|
269
|
+
lines.push("/* Generated from the production theme by syncFontsToCss. Do not edit. */");
|
|
270
|
+
lines.push("/* Both fonts.css and fonts/ are in dist/, so relative paths work at runtime. */");
|
|
271
|
+
lines.push("");
|
|
272
|
+
for (const source of sources) {
|
|
273
|
+
const familyNames = source.families.map((f) => f.name).join(", ");
|
|
274
|
+
const label = source.label ? `${source.label} \u2014 ${familyNames}` : familyNames;
|
|
275
|
+
lines.push(`/* ${label} */`);
|
|
276
|
+
if (source.kind === "font-face") {
|
|
277
|
+
if (source.cssText) lines.push(source.cssText);
|
|
278
|
+
} else if (source.url) {
|
|
279
|
+
lines.push(`@import url('${source.url}');`);
|
|
280
|
+
}
|
|
281
|
+
lines.push("");
|
|
282
|
+
}
|
|
283
|
+
const content = lines.join("\n");
|
|
284
|
+
if (!import_fs2.default.existsSync(import_path2.default.dirname(FONTS_CSS_PATH))) {
|
|
285
|
+
import_fs2.default.mkdirSync(import_path2.default.dirname(FONTS_CSS_PATH), { recursive: true });
|
|
286
|
+
}
|
|
287
|
+
import_fs2.default.writeFileSync(FONTS_CSS_PATH, content);
|
|
288
|
+
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${import_path2.default.basename(FONTS_CSS_PATH)}`);
|
|
289
|
+
}
|
|
290
|
+
function extractAliasDeclarations(body) {
|
|
291
|
+
const aliases = {};
|
|
292
|
+
const re = /(--[a-z0-9-]+)\s*:\s*var\((--[a-z0-9-]+)\)\s*;/gi;
|
|
293
|
+
let m;
|
|
294
|
+
while ((m = re.exec(body)) !== null) aliases[m[1]] = m[2];
|
|
295
|
+
return aliases;
|
|
296
|
+
}
|
|
297
|
+
function componentNameFromFile(filePath) {
|
|
298
|
+
return import_path2.default.basename(filePath, ".svelte").toLowerCase();
|
|
299
|
+
}
|
|
300
|
+
function listComponentSourcePaths() {
|
|
301
|
+
if (!import_fs2.default.existsSync(COMPONENTS_SRC_DIR)) return [];
|
|
302
|
+
return import_fs2.default.readdirSync(COMPONENTS_SRC_DIR).filter((f) => f.endsWith(".svelte")).map((f) => import_path2.default.join(COMPONENTS_SRC_DIR, f));
|
|
303
|
+
}
|
|
304
|
+
function listComponentNames() {
|
|
305
|
+
return listComponentSourcePaths().map(componentNameFromFile);
|
|
306
|
+
}
|
|
307
|
+
function generateDefaultConfig(comp, sourcePath) {
|
|
308
|
+
if (!import_fs2.default.existsSync(sourcePath)) return;
|
|
309
|
+
const r = componentResource(comp);
|
|
310
|
+
r.ensureDir();
|
|
311
|
+
const defaultPath = r.filePath("default");
|
|
312
|
+
const sourceStat = import_fs2.default.statSync(sourcePath);
|
|
313
|
+
if (import_fs2.default.existsSync(defaultPath)) {
|
|
314
|
+
const defaultStat = import_fs2.default.statSync(defaultPath);
|
|
315
|
+
if (defaultStat.mtimeMs >= sourceStat.mtimeMs) return;
|
|
316
|
+
}
|
|
317
|
+
const source = import_fs2.default.readFileSync(sourcePath, "utf-8");
|
|
318
|
+
const body = extractGlobalRootBody(source);
|
|
319
|
+
const aliases = extractAliasDeclarations(body);
|
|
320
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
321
|
+
let createdAt = now;
|
|
322
|
+
let existingUpdatedAt;
|
|
323
|
+
let existingAliases;
|
|
324
|
+
if (import_fs2.default.existsSync(defaultPath)) {
|
|
325
|
+
try {
|
|
326
|
+
const existing = JSON.parse(import_fs2.default.readFileSync(defaultPath, "utf-8"));
|
|
327
|
+
if (existing.createdAt) createdAt = existing.createdAt;
|
|
328
|
+
if (typeof existing.updatedAt === "string") existingUpdatedAt = existing.updatedAt;
|
|
329
|
+
if (existing.aliases && typeof existing.aliases === "object") {
|
|
330
|
+
existingAliases = existing.aliases;
|
|
196
331
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
|
|
336
|
+
const defaultConfig = {
|
|
337
|
+
name: "default",
|
|
338
|
+
component: comp,
|
|
339
|
+
createdAt,
|
|
340
|
+
updatedAt: aliasesUnchanged && existingUpdatedAt ? existingUpdatedAt : now,
|
|
341
|
+
aliases
|
|
342
|
+
};
|
|
343
|
+
if (aliasesUnchanged && existingUpdatedAt) {
|
|
344
|
+
const t = /* @__PURE__ */ new Date();
|
|
345
|
+
import_fs2.default.utimesSync(defaultPath, t, t);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
import_fs2.default.writeFileSync(defaultPath, JSON.stringify(defaultConfig, null, 2));
|
|
349
|
+
}
|
|
350
|
+
function ensureComponentConfigsDir() {
|
|
351
|
+
if (!import_fs2.default.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
352
|
+
import_fs2.default.mkdirSync(COMPONENT_CONFIGS_DIR, { recursive: true });
|
|
353
|
+
}
|
|
354
|
+
for (const sourcePath of listComponentSourcePaths()) {
|
|
355
|
+
const comp = componentNameFromFile(sourcePath);
|
|
356
|
+
const r = componentResource(comp);
|
|
357
|
+
r.ensureDir();
|
|
358
|
+
generateDefaultConfig(comp, sourcePath);
|
|
359
|
+
r.ensureMeta();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function ensurePresetsDir() {
|
|
363
|
+
presetsResource.ensureDir();
|
|
364
|
+
const defaultPath = import_path2.default.join(PRESETS_DIR, "default.json");
|
|
365
|
+
if (!import_fs2.default.existsSync(defaultPath)) {
|
|
366
|
+
const componentConfigs = {};
|
|
367
|
+
for (const comp of listComponentNames()) {
|
|
368
|
+
componentConfigs[comp] = componentResource(comp).getActiveName();
|
|
369
|
+
}
|
|
370
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
371
|
+
const defaultPreset = {
|
|
372
|
+
name: "Default",
|
|
373
|
+
createdAt: now,
|
|
374
|
+
updatedAt: now,
|
|
375
|
+
theme: themesResource.getActiveName(),
|
|
376
|
+
componentConfigs
|
|
377
|
+
};
|
|
378
|
+
import_fs2.default.writeFileSync(defaultPath, JSON.stringify(defaultPreset, null, 2));
|
|
379
|
+
}
|
|
380
|
+
presetsResource.ensureMeta();
|
|
381
|
+
}
|
|
382
|
+
function readComponentConfig(comp, name) {
|
|
383
|
+
const filePath = componentResource(comp).filePath(name);
|
|
384
|
+
if (!import_fs2.default.existsSync(filePath)) return null;
|
|
385
|
+
try {
|
|
386
|
+
return JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
387
|
+
} catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const COMPONENT_OVERRIDES_START = "/* component-aliases:start */";
|
|
392
|
+
const COMPONENT_OVERRIDES_END = "/* component-aliases:end */";
|
|
393
|
+
function syncComponentsToCss() {
|
|
394
|
+
if (!import_fs2.default.existsSync(CSS_PATH)) return;
|
|
395
|
+
if (!import_fs2.default.existsSync(COMPONENT_CONFIGS_DIR)) return;
|
|
396
|
+
const lines = [];
|
|
397
|
+
const components = listComponentNames();
|
|
398
|
+
for (const comp of components) {
|
|
399
|
+
const prod = componentResource(comp).getProductionName();
|
|
400
|
+
if (prod === "default") continue;
|
|
401
|
+
const prodCfg = readComponentConfig(comp, prod);
|
|
402
|
+
const defaultCfg = readComponentConfig(comp, "default");
|
|
403
|
+
if (!prodCfg || !defaultCfg) continue;
|
|
404
|
+
const overrides = [];
|
|
405
|
+
for (const [varName, semanticName] of Object.entries(prodCfg.aliases ?? {})) {
|
|
406
|
+
if ((defaultCfg.aliases ?? {})[varName] !== semanticName) {
|
|
407
|
+
overrides.push([varName, semanticName]);
|
|
212
408
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
409
|
+
}
|
|
410
|
+
if (overrides.length === 0) continue;
|
|
411
|
+
lines.push(` /* ${comp} (${prod}) */`);
|
|
412
|
+
for (const [varName, semanticName] of overrides) {
|
|
413
|
+
lines.push(` ${varName}: var(${semanticName});`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const block = lines.length > 0 ? `
|
|
417
|
+
|
|
418
|
+
${COMPONENT_OVERRIDES_START}
|
|
419
|
+
:root:root {
|
|
420
|
+
${lines.join("\n")}
|
|
421
|
+
}
|
|
422
|
+
${COMPONENT_OVERRIDES_END}
|
|
423
|
+
` : "";
|
|
424
|
+
let cssContent = import_fs2.default.readFileSync(CSS_PATH, "utf-8");
|
|
425
|
+
const startIdx = cssContent.indexOf(COMPONENT_OVERRIDES_START);
|
|
426
|
+
const endIdx = cssContent.indexOf(COMPONENT_OVERRIDES_END);
|
|
427
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
428
|
+
let stripStart = startIdx;
|
|
429
|
+
while (stripStart > 0 && cssContent[stripStart - 1] === "\n") stripStart--;
|
|
430
|
+
const after = cssContent.slice(endIdx + COMPONENT_OVERRIDES_END.length);
|
|
431
|
+
cssContent = cssContent.slice(0, stripStart) + (block || "\n") + after.replace(/^\n+/, "");
|
|
432
|
+
} else if (block) {
|
|
433
|
+
cssContent = cssContent.replace(/\n*$/, "") + block;
|
|
434
|
+
}
|
|
435
|
+
import_fs2.default.writeFileSync(CSS_PATH, cssContent);
|
|
436
|
+
console.log(
|
|
437
|
+
`[syncComponentsToCss] Wrote ${lines.filter((l) => !l.trim().startsWith("/*")).length} alias override(s) to tokens.css`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
const escapedBase = API_BASE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
441
|
+
const THEMES_ROUTE = `${API_BASE}/themes`;
|
|
442
|
+
const THEMES_ACTIVE_ROUTE = `${API_BASE}/themes/active`;
|
|
443
|
+
const THEMES_PRODUCTION_ROUTE = `${API_BASE}/themes/production`;
|
|
444
|
+
const COMPONENT_CONFIGS_ROUTE = `${API_BASE}/component-configs`;
|
|
445
|
+
const PRESETS_ROUTE = `${API_BASE}/presets`;
|
|
446
|
+
const PRESETS_ACTIVE_ROUTE = `${API_BASE}/presets/active`;
|
|
447
|
+
const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
|
|
448
|
+
const COMP_LIST_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)$`);
|
|
449
|
+
const COMP_ACTIVE_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/active$`);
|
|
450
|
+
const COMP_PRODUCTION_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/production$`);
|
|
451
|
+
const COMP_BY_NAME_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/([a-z0-9\\-_]+)$`);
|
|
452
|
+
const PRESET_APPLY_REGEX = new RegExp(`^${escapedBase}/presets/([a-z0-9\\-_]+)/apply$`);
|
|
453
|
+
const PRESET_BY_NAME_REGEX = new RegExp(`^${escapedBase}/presets/([a-z0-9\\-_]+)$`);
|
|
454
|
+
async function handleListThemes(_ctx) {
|
|
455
|
+
const activeFile = themesResource.getActiveName();
|
|
456
|
+
const files = import_fs2.default.readdirSync(THEMES_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
|
|
457
|
+
const filePath = import_path2.default.join(THEMES_DIR, f);
|
|
458
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
459
|
+
const fileName = f.replace(".json", "");
|
|
460
|
+
return {
|
|
461
|
+
name: data.name || fileName,
|
|
462
|
+
fileName,
|
|
463
|
+
updatedAt: data.updatedAt || "",
|
|
464
|
+
isActive: fileName === activeFile
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
jsonResponse(_ctx.res, 200, { files });
|
|
468
|
+
}
|
|
469
|
+
async function handleGetActiveTheme({ res }) {
|
|
470
|
+
const activeFile = themesResource.getActiveName();
|
|
471
|
+
const filePath = themesResource.filePath(activeFile);
|
|
472
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
473
|
+
jsonResponse(res, 404, { error: "Active theme not found" });
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
477
|
+
data._fileName = activeFile;
|
|
478
|
+
jsonResponse(res, 200, data);
|
|
479
|
+
}
|
|
480
|
+
async function handleSetActiveTheme({ req, res }) {
|
|
481
|
+
const body = JSON.parse(await readBody(req));
|
|
482
|
+
const fileName = sanitizeFileName(body.name || "default");
|
|
483
|
+
if (!import_fs2.default.existsSync(themesResource.filePath(fileName))) {
|
|
484
|
+
jsonResponse(res, 404, { error: "Theme not found" });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
themesResource.setActiveName(fileName);
|
|
488
|
+
jsonResponse(res, 200, { ok: true, activeFile: fileName });
|
|
489
|
+
}
|
|
490
|
+
async function handleGetProductionTheme({ res }) {
|
|
491
|
+
const prodFile = themesResource.getProductionName();
|
|
492
|
+
const filePath = themesResource.filePath(prodFile);
|
|
493
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
494
|
+
jsonResponse(res, 200, { fileName: prodFile, name: prodFile, cssVariables: {} });
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
498
|
+
jsonResponse(res, 200, {
|
|
499
|
+
fileName: prodFile,
|
|
500
|
+
name: data.name || prodFile,
|
|
501
|
+
updatedAt: data.updatedAt || "",
|
|
502
|
+
cssVariables: data.cssVariables || {}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
async function handleSetProductionTheme({ req, res }) {
|
|
506
|
+
const body = JSON.parse(await readBody(req));
|
|
507
|
+
const fileName = sanitizeFileName(body.name || "default");
|
|
508
|
+
if (!import_fs2.default.existsSync(themesResource.filePath(fileName))) {
|
|
509
|
+
jsonResponse(res, 404, { error: "Theme not found" });
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
themesResource.setProductionName(fileName);
|
|
513
|
+
syncTokensToCss(fileName);
|
|
514
|
+
syncFontsToCss(fileName);
|
|
515
|
+
syncComponentsToCss();
|
|
516
|
+
const data = JSON.parse(import_fs2.default.readFileSync(themesResource.filePath(fileName), "utf-8"));
|
|
517
|
+
jsonResponse(res, 200, {
|
|
518
|
+
ok: true,
|
|
519
|
+
fileName,
|
|
520
|
+
name: data.name || fileName,
|
|
521
|
+
updatedAt: data.updatedAt || ""
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async function handleThemeByName({ params, req, res }) {
|
|
525
|
+
const [fileName] = params;
|
|
526
|
+
const filePath = themesResource.filePath(fileName);
|
|
527
|
+
if (req.method === "GET") {
|
|
528
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
529
|
+
jsonResponse(res, 404, { error: "Not found" });
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
533
|
+
data._fileName = fileName;
|
|
534
|
+
jsonResponse(res, 200, data);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (req.method === "PUT") {
|
|
538
|
+
const body = JSON.parse(await readBody(req));
|
|
539
|
+
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
540
|
+
if (import_fs2.default.existsSync(filePath)) {
|
|
541
|
+
try {
|
|
542
|
+
const existing = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
543
|
+
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
544
|
+
} catch {
|
|
227
545
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
546
|
+
}
|
|
547
|
+
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
548
|
+
import_fs2.default.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
549
|
+
if (fileName === themesResource.getProductionName()) {
|
|
550
|
+
syncTokensToCss(fileName);
|
|
551
|
+
syncFontsToCss(fileName);
|
|
552
|
+
syncComponentsToCss();
|
|
553
|
+
}
|
|
554
|
+
jsonResponse(res, 200, { ok: true, fileName });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (req.method === "DELETE") {
|
|
558
|
+
if (fileName === "default") {
|
|
559
|
+
jsonResponse(res, 403, { error: "Cannot delete the default theme" });
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (import_fs2.default.existsSync(filePath)) {
|
|
563
|
+
import_fs2.default.unlinkSync(filePath);
|
|
564
|
+
if (themesResource.getActiveName() === fileName) {
|
|
565
|
+
themesResource.setActiveName("default");
|
|
247
566
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
567
|
+
}
|
|
568
|
+
jsonResponse(res, 200, { ok: true });
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async function handleListComponents({ res }) {
|
|
573
|
+
const components = listComponentNames().map((comp) => {
|
|
574
|
+
const r = componentResource(comp);
|
|
575
|
+
return {
|
|
576
|
+
name: comp,
|
|
577
|
+
activeFile: r.getActiveName(),
|
|
578
|
+
productionFile: r.getProductionName()
|
|
579
|
+
};
|
|
580
|
+
});
|
|
581
|
+
jsonResponse(res, 200, { components });
|
|
582
|
+
}
|
|
583
|
+
async function handleGetComponentActive({ params, res }) {
|
|
584
|
+
const [comp] = params;
|
|
585
|
+
const r = componentResource(comp);
|
|
586
|
+
const activeFile = r.getActiveName();
|
|
587
|
+
const cfg = readComponentConfig(comp, activeFile);
|
|
588
|
+
if (!cfg) {
|
|
589
|
+
jsonResponse(res, 404, { error: "Active config not found" });
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
jsonResponse(res, 200, { ...cfg, _fileName: activeFile });
|
|
593
|
+
}
|
|
594
|
+
async function handleSetComponentActive({ params, req, res }) {
|
|
595
|
+
const [comp] = params;
|
|
596
|
+
const body = JSON.parse(await readBody(req));
|
|
597
|
+
const fileName = sanitizeFileName(body.name || "default");
|
|
598
|
+
const r = componentResource(comp);
|
|
599
|
+
const configPath = r.filePath(fileName);
|
|
600
|
+
if (!import_fs2.default.existsSync(configPath)) {
|
|
601
|
+
jsonResponse(res, 404, { error: "Config not found" });
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
r.ensureDir();
|
|
605
|
+
r.setActiveName(fileName);
|
|
606
|
+
jsonResponse(res, 200, { ok: true, activeFile: fileName });
|
|
607
|
+
}
|
|
608
|
+
async function handleGetComponentProduction({ params, res }) {
|
|
609
|
+
const [comp] = params;
|
|
610
|
+
const r = componentResource(comp);
|
|
611
|
+
const prodFile = r.getProductionName();
|
|
612
|
+
const cfg = readComponentConfig(comp, prodFile);
|
|
613
|
+
jsonResponse(res, 200, {
|
|
614
|
+
fileName: prodFile,
|
|
615
|
+
name: cfg?.name || prodFile,
|
|
616
|
+
aliases: cfg?.aliases || {}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
async function handleSetComponentProduction({ params, req, res }) {
|
|
620
|
+
const [comp] = params;
|
|
621
|
+
const body = JSON.parse(await readBody(req));
|
|
622
|
+
const fileName = sanitizeFileName(body.name || "default");
|
|
623
|
+
const r = componentResource(comp);
|
|
624
|
+
const configPath = r.filePath(fileName);
|
|
625
|
+
if (!import_fs2.default.existsSync(configPath)) {
|
|
626
|
+
jsonResponse(res, 404, { error: "Config not found" });
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
r.ensureDir();
|
|
630
|
+
r.setProductionName(fileName);
|
|
631
|
+
syncComponentsToCss();
|
|
632
|
+
const cfg = readComponentConfig(comp, fileName);
|
|
633
|
+
jsonResponse(res, 200, {
|
|
634
|
+
ok: true,
|
|
635
|
+
fileName,
|
|
636
|
+
name: cfg?.name || fileName,
|
|
637
|
+
aliases: cfg?.aliases || {}
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
async function handleComponentConfigByName({ params, req, res }) {
|
|
641
|
+
const [comp, name] = params;
|
|
642
|
+
const r = componentResource(comp);
|
|
643
|
+
const configPath = r.filePath(name);
|
|
644
|
+
if (req.method === "GET") {
|
|
645
|
+
if (!import_fs2.default.existsSync(configPath)) {
|
|
646
|
+
jsonResponse(res, 404, { error: "Not found" });
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const data = JSON.parse(import_fs2.default.readFileSync(configPath, "utf-8"));
|
|
650
|
+
data._fileName = name;
|
|
651
|
+
jsonResponse(res, 200, data);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (req.method === "PUT") {
|
|
655
|
+
if (name === "default") {
|
|
656
|
+
jsonResponse(res, 403, { error: "Cannot modify default config (regenerated from source)" });
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const body = JSON.parse(await readBody(req));
|
|
660
|
+
body.component = comp;
|
|
661
|
+
body.name = body.name || name;
|
|
662
|
+
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
663
|
+
if (import_fs2.default.existsSync(configPath)) {
|
|
664
|
+
try {
|
|
665
|
+
const existing = JSON.parse(import_fs2.default.readFileSync(configPath, "utf-8"));
|
|
666
|
+
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
667
|
+
} catch {
|
|
269
668
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
if (import_fs.default.existsSync(CSS_BACKUP_DIR)) {
|
|
293
|
-
for (const f of import_fs.default.readdirSync(CSS_BACKUP_DIR)) {
|
|
294
|
-
if (!f.endsWith(".css")) continue;
|
|
295
|
-
const match2 = f.match(/^(.+)_(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z)\.css$/);
|
|
296
|
-
if (!match2) continue;
|
|
297
|
-
const stat = import_fs.default.statSync(import_path.default.join(CSS_BACKUP_DIR, f));
|
|
298
|
-
backups.push({
|
|
299
|
-
type: "css",
|
|
300
|
-
file: f,
|
|
301
|
-
name: match2[1],
|
|
302
|
-
timestamp: fileTimestampToISO2(match2[2]),
|
|
303
|
-
size: stat.size
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
308
|
-
jsonResponse(res, 200, { backups });
|
|
309
|
-
} catch (err) {
|
|
310
|
-
jsonResponse(res, 500, { error: err.message });
|
|
311
|
-
}
|
|
312
|
-
return;
|
|
669
|
+
}
|
|
670
|
+
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
671
|
+
r.ensureDir();
|
|
672
|
+
import_fs2.default.writeFileSync(configPath, JSON.stringify(body, null, 2));
|
|
673
|
+
if (r.getProductionName() === name) {
|
|
674
|
+
syncComponentsToCss();
|
|
675
|
+
}
|
|
676
|
+
jsonResponse(res, 200, { ok: true, fileName: name });
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (req.method === "DELETE") {
|
|
680
|
+
if (name === "default") {
|
|
681
|
+
jsonResponse(res, 403, { error: "Cannot delete default config" });
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (import_fs2.default.existsSync(configPath)) {
|
|
685
|
+
import_fs2.default.unlinkSync(configPath);
|
|
686
|
+
if (r.getActiveName() === name) {
|
|
687
|
+
r.setActiveName("default");
|
|
313
688
|
}
|
|
314
|
-
{
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const [, type, file] = restoreMatch;
|
|
318
|
-
try {
|
|
319
|
-
if (type === "css") {
|
|
320
|
-
const backupPath = import_path.default.join(CSS_BACKUP_DIR, decodeURIComponent(file));
|
|
321
|
-
if (!backupPath.startsWith(CSS_BACKUP_DIR) || !import_fs.default.existsSync(backupPath)) {
|
|
322
|
-
jsonResponse(res, 404, { error: "Backup not found" });
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
backupCssFile();
|
|
326
|
-
import_fs.default.copyFileSync(backupPath, CSS_PATH);
|
|
327
|
-
jsonResponse(res, 200, { ok: true, restored: file });
|
|
328
|
-
} else {
|
|
329
|
-
const backupPath = import_path.default.join(TOKENS_BACKUP_DIR, decodeURIComponent(file));
|
|
330
|
-
if (!backupPath.startsWith(TOKENS_BACKUP_DIR) || !import_fs.default.existsSync(backupPath)) {
|
|
331
|
-
jsonResponse(res, 404, { error: "Backup not found" });
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
const nameMatch = decodeURIComponent(file).match(/^(.+)_\d{4}-/);
|
|
335
|
-
if (!nameMatch) {
|
|
336
|
-
jsonResponse(res, 400, { error: "Cannot determine token file name from backup" });
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
const tokenFilePath = import_path.default.join(TOKENS_DIR, `${nameMatch[1]}.json`);
|
|
340
|
-
backupTokenFile(tokenFilePath, nameMatch[1]);
|
|
341
|
-
import_fs.default.copyFileSync(backupPath, tokenFilePath);
|
|
342
|
-
jsonResponse(res, 200, { ok: true, restored: file });
|
|
343
|
-
}
|
|
344
|
-
} catch (err) {
|
|
345
|
-
jsonResponse(res, 500, { error: err.message });
|
|
346
|
-
}
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
689
|
+
if (r.getProductionName() === name) {
|
|
690
|
+
r.setProductionName("default");
|
|
691
|
+
syncComponentsToCss();
|
|
349
692
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
693
|
+
}
|
|
694
|
+
jsonResponse(res, 200, { ok: true });
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function handleListComponentConfigs({ params, res }) {
|
|
699
|
+
const [comp] = params;
|
|
700
|
+
const r = componentResource(comp);
|
|
701
|
+
if (!import_fs2.default.existsSync(r.dir)) {
|
|
702
|
+
jsonResponse(res, 404, { error: "Component not found" });
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const activeFile = r.getActiveName();
|
|
706
|
+
const productionFile = r.getProductionName();
|
|
707
|
+
const files = import_fs2.default.readdirSync(r.dir).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
|
|
708
|
+
const filePath = import_path2.default.join(r.dir, f);
|
|
709
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
710
|
+
const fileName = f.replace(".json", "");
|
|
711
|
+
return {
|
|
712
|
+
name: data.name || fileName,
|
|
713
|
+
fileName,
|
|
714
|
+
updatedAt: data.updatedAt || "",
|
|
715
|
+
isActive: fileName === activeFile,
|
|
716
|
+
isProduction: fileName === productionFile
|
|
717
|
+
};
|
|
718
|
+
});
|
|
719
|
+
jsonResponse(res, 200, { component: comp, files, activeFile, productionFile });
|
|
720
|
+
}
|
|
721
|
+
async function handleListPresets({ res }) {
|
|
722
|
+
const activeFile = presetsResource.getActiveName();
|
|
723
|
+
const files = import_fs2.default.readdirSync(PRESETS_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
|
|
724
|
+
const filePath = import_path2.default.join(PRESETS_DIR, f);
|
|
725
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
726
|
+
const fileName = f.replace(".json", "");
|
|
727
|
+
return {
|
|
728
|
+
name: data.name || fileName,
|
|
729
|
+
fileName,
|
|
730
|
+
updatedAt: data.updatedAt || "",
|
|
731
|
+
isActive: fileName === activeFile
|
|
732
|
+
};
|
|
733
|
+
});
|
|
734
|
+
jsonResponse(res, 200, { files, activeFile });
|
|
735
|
+
}
|
|
736
|
+
async function handleGetActivePreset({ res }) {
|
|
737
|
+
const activeFile = presetsResource.getActiveName();
|
|
738
|
+
const filePath = presetsResource.filePath(activeFile);
|
|
739
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
740
|
+
jsonResponse(res, 404, { error: "Active preset not found" });
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
744
|
+
data._fileName = activeFile;
|
|
745
|
+
jsonResponse(res, 200, data);
|
|
746
|
+
}
|
|
747
|
+
async function handleSetActivePreset({ req, res }) {
|
|
748
|
+
const body = JSON.parse(await readBody(req));
|
|
749
|
+
const fileName = sanitizeFileName(body.name || "default");
|
|
750
|
+
if (!import_fs2.default.existsSync(presetsResource.filePath(fileName))) {
|
|
751
|
+
jsonResponse(res, 404, { error: "Preset not found" });
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
presetsResource.setActiveName(fileName);
|
|
755
|
+
jsonResponse(res, 200, { ok: true, activeFile: fileName });
|
|
756
|
+
}
|
|
757
|
+
async function handlePresetByName({ params, req, res }) {
|
|
758
|
+
const [fileName] = params;
|
|
759
|
+
const filePath = presetsResource.filePath(fileName);
|
|
760
|
+
if (req.method === "GET") {
|
|
761
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
762
|
+
jsonResponse(res, 404, { error: "Not found" });
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
766
|
+
data._fileName = fileName;
|
|
767
|
+
jsonResponse(res, 200, data);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
if (req.method === "PUT") {
|
|
771
|
+
const body = JSON.parse(await readBody(req));
|
|
772
|
+
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
773
|
+
if (import_fs2.default.existsSync(filePath)) {
|
|
774
|
+
try {
|
|
775
|
+
const existing = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
|
|
776
|
+
if (existing.createdAt) body.createdAt = existing.createdAt;
|
|
777
|
+
} catch {
|
|
364
778
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
779
|
+
}
|
|
780
|
+
if (!body.createdAt) body.createdAt = body.updatedAt;
|
|
781
|
+
import_fs2.default.writeFileSync(filePath, JSON.stringify(body, null, 2));
|
|
782
|
+
jsonResponse(res, 200, { ok: true, fileName });
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (req.method === "DELETE") {
|
|
786
|
+
if (fileName === "default") {
|
|
787
|
+
jsonResponse(res, 403, { error: "Cannot delete the default preset" });
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (import_fs2.default.existsSync(filePath)) {
|
|
791
|
+
import_fs2.default.unlinkSync(filePath);
|
|
792
|
+
if (presetsResource.getActiveName() === fileName) {
|
|
793
|
+
presetsResource.setActiveName("default");
|
|
373
794
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
795
|
+
}
|
|
796
|
+
jsonResponse(res, 200, { ok: true });
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
async function handleApplyPreset({ params, res }) {
|
|
801
|
+
const [fileName] = params;
|
|
802
|
+
const presetPath = presetsResource.filePath(fileName);
|
|
803
|
+
if (!import_fs2.default.existsSync(presetPath)) {
|
|
804
|
+
jsonResponse(res, 404, { error: "Preset not found" });
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const preset = JSON.parse(import_fs2.default.readFileSync(presetPath, "utf-8"));
|
|
808
|
+
const themeName = sanitizeFileName(preset.theme || "default");
|
|
809
|
+
const themePath = themesResource.filePath(themeName);
|
|
810
|
+
if (!import_fs2.default.existsSync(themePath)) {
|
|
811
|
+
jsonResponse(res, 422, { error: `Preset references missing theme: ${themeName}` });
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const knownComponents = new Set(listComponentNames());
|
|
815
|
+
const componentConfigs = preset.componentConfigs ?? {};
|
|
816
|
+
const resolvedConfigs = {};
|
|
817
|
+
const apply = [];
|
|
818
|
+
for (const [comp, configFile] of Object.entries(componentConfigs)) {
|
|
819
|
+
if (!knownComponents.has(comp)) continue;
|
|
820
|
+
const sanitized = sanitizeFileName(String(configFile) || "default");
|
|
821
|
+
const r = componentResource(comp);
|
|
822
|
+
const cfgPath = r.filePath(sanitized);
|
|
823
|
+
if (!import_fs2.default.existsSync(cfgPath)) {
|
|
824
|
+
jsonResponse(res, 422, {
|
|
825
|
+
error: `Preset references missing config: ${comp}/${sanitized}`
|
|
826
|
+
});
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
apply.push([comp, sanitized]);
|
|
830
|
+
}
|
|
831
|
+
themesResource.setActiveName(themeName);
|
|
832
|
+
const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
|
|
833
|
+
themeData._fileName = themeName;
|
|
834
|
+
for (const [comp, configFile] of apply) {
|
|
835
|
+
const r = componentResource(comp);
|
|
836
|
+
r.setActiveName(configFile);
|
|
837
|
+
const cfg = readComponentConfig(comp, configFile);
|
|
838
|
+
if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: configFile };
|
|
839
|
+
}
|
|
840
|
+
for (const comp of knownComponents) {
|
|
841
|
+
if (resolvedConfigs[comp]) continue;
|
|
842
|
+
const activeName = componentResource(comp).getActiveName();
|
|
843
|
+
const cfg = readComponentConfig(comp, activeName);
|
|
844
|
+
if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: activeName };
|
|
845
|
+
}
|
|
846
|
+
presetsResource.setActiveName(fileName);
|
|
847
|
+
jsonResponse(res, 200, {
|
|
848
|
+
ok: true,
|
|
849
|
+
preset: { ...preset, _fileName: fileName },
|
|
850
|
+
theme: themeData,
|
|
851
|
+
componentConfigs: resolvedConfigs
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
function methodNotAllowed({ res }) {
|
|
855
|
+
jsonResponse(res, 405, { error: "Method not allowed" });
|
|
856
|
+
}
|
|
857
|
+
const routes = [
|
|
858
|
+
// Themes — list / active / production are exact strings, must run before THEME_BY_NAME_REGEX
|
|
859
|
+
{ method: "GET", pattern: THEMES_ROUTE, handler: handleListThemes },
|
|
860
|
+
{ method: "GET", pattern: THEMES_ACTIVE_ROUTE, handler: handleGetActiveTheme },
|
|
861
|
+
{ method: "PUT", pattern: THEMES_ACTIVE_ROUTE, handler: handleSetActiveTheme },
|
|
862
|
+
{ method: "GET", pattern: THEMES_PRODUCTION_ROUTE, handler: handleGetProductionTheme },
|
|
863
|
+
{ method: "PUT", pattern: THEMES_PRODUCTION_ROUTE, handler: handleSetProductionTheme },
|
|
864
|
+
// Component configs — list of components
|
|
865
|
+
{ method: "GET", pattern: COMPONENT_CONFIGS_ROUTE, handler: handleListComponents },
|
|
866
|
+
// Component configs — :comp/active (must precede :comp/:name)
|
|
867
|
+
{ method: "GET", pattern: COMP_ACTIVE_REGEX, handler: handleGetComponentActive },
|
|
868
|
+
{ method: "PUT", pattern: COMP_ACTIVE_REGEX, handler: handleSetComponentActive },
|
|
869
|
+
{ method: "POST", pattern: COMP_ACTIVE_REGEX, handler: methodNotAllowed },
|
|
870
|
+
{ method: "DELETE", pattern: COMP_ACTIVE_REGEX, handler: methodNotAllowed },
|
|
871
|
+
// Component configs — :comp/production (must precede :comp/:name)
|
|
872
|
+
{ method: "GET", pattern: COMP_PRODUCTION_REGEX, handler: handleGetComponentProduction },
|
|
873
|
+
{ method: "PUT", pattern: COMP_PRODUCTION_REGEX, handler: handleSetComponentProduction },
|
|
874
|
+
{ method: "POST", pattern: COMP_PRODUCTION_REGEX, handler: methodNotAllowed },
|
|
875
|
+
{ method: "DELETE", pattern: COMP_PRODUCTION_REGEX, handler: methodNotAllowed },
|
|
876
|
+
// Component configs — :comp/:name (must precede :comp listing)
|
|
877
|
+
{ method: "GET", pattern: COMP_BY_NAME_REGEX, handler: handleComponentConfigByName },
|
|
878
|
+
{ method: "PUT", pattern: COMP_BY_NAME_REGEX, handler: handleComponentConfigByName },
|
|
879
|
+
{ method: "DELETE", pattern: COMP_BY_NAME_REGEX, handler: handleComponentConfigByName },
|
|
880
|
+
{ method: "POST", pattern: COMP_BY_NAME_REGEX, handler: methodNotAllowed },
|
|
881
|
+
// Component configs — list configs for :comp (broadest, runs last among comp routes)
|
|
882
|
+
{ method: "GET", pattern: COMP_LIST_REGEX, handler: handleListComponentConfigs },
|
|
883
|
+
// Themes — :name CRUD (broadest theme route, runs last)
|
|
884
|
+
{ method: "GET", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
885
|
+
{ method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
886
|
+
{ method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
887
|
+
// Presets — list / active are exact strings, must run before regexes
|
|
888
|
+
{ method: "GET", pattern: PRESETS_ROUTE, handler: handleListPresets },
|
|
889
|
+
{ method: "GET", pattern: PRESETS_ACTIVE_ROUTE, handler: handleGetActivePreset },
|
|
890
|
+
{ method: "PUT", pattern: PRESETS_ACTIVE_ROUTE, handler: handleSetActivePreset },
|
|
891
|
+
// Presets — :name/apply (more specific than :name)
|
|
892
|
+
{ method: "PUT", pattern: PRESET_APPLY_REGEX, handler: handleApplyPreset },
|
|
893
|
+
{ method: "POST", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
|
|
894
|
+
{ method: "GET", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
|
|
895
|
+
{ method: "DELETE", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
|
|
896
|
+
// Presets — :name CRUD (broadest preset route, runs last)
|
|
897
|
+
{ method: "GET", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName },
|
|
898
|
+
{ method: "PUT", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName },
|
|
899
|
+
{ method: "DELETE", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName }
|
|
900
|
+
];
|
|
901
|
+
return {
|
|
902
|
+
name: "theme-file-api",
|
|
903
|
+
config() {
|
|
904
|
+
return {
|
|
905
|
+
define: {
|
|
906
|
+
__PROJECT_ROOT__: JSON.stringify(process.cwd())
|
|
435
907
|
}
|
|
436
|
-
|
|
908
|
+
};
|
|
909
|
+
},
|
|
910
|
+
configureServer(server) {
|
|
911
|
+
ensureThemesDir();
|
|
912
|
+
ensureComponentConfigsDir();
|
|
913
|
+
ensurePresetsDir();
|
|
914
|
+
server.middlewares.use(async (req, res, next) => {
|
|
915
|
+
const handled = await dispatch(req, res, routes);
|
|
916
|
+
if (!handled) next();
|
|
437
917
|
});
|
|
918
|
+
},
|
|
919
|
+
handleHotUpdate(ctx) {
|
|
920
|
+
const normalized = import_path2.default.resolve(ctx.file);
|
|
921
|
+
if (!normalized.startsWith(COMPONENTS_SRC_DIR)) return;
|
|
922
|
+
if (!normalized.endsWith(".svelte")) return;
|
|
923
|
+
const comp = componentNameFromFile(normalized);
|
|
924
|
+
generateDefaultConfig(comp, normalized);
|
|
438
925
|
}
|
|
439
926
|
};
|
|
440
927
|
}
|
|
441
928
|
// Annotate the CommonJS export names for ESM import in node:
|
|
442
929
|
0 && (module.exports = {
|
|
443
|
-
|
|
930
|
+
themeFileApi
|
|
444
931
|
});
|