@motion-proto/live-tokens 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/dist-plugin/index.cjs +854 -226
- package/dist-plugin/index.d.cts +2 -1
- package/dist-plugin/index.d.ts +2 -1
- package/dist-plugin/index.js +852 -225
- package/package.json +26 -40
- package/src/{styles → app}/site.css +1 -1
- package/src/{component-editor → editor/component-editor}/BadgeEditor.svelte +8 -82
- package/src/{component-editor → editor/component-editor}/CalloutEditor.svelte +4 -4
- package/src/{component-editor → editor/component-editor}/CardEditor.svelte +28 -76
- package/src/{component-editor → editor/component-editor}/CollapsibleSectionEditor.svelte +37 -30
- package/src/{component-editor → editor/component-editor}/CornerBadgeEditor.svelte +31 -93
- package/src/{component-editor → editor/component-editor}/DialogEditor.svelte +60 -57
- package/src/editor/component-editor/ImageEditor.svelte +30 -0
- package/src/{component-editor → editor/component-editor}/InlineEditActionsEditor.svelte +6 -4
- package/src/editor/component-editor/MenuSelectEditor.svelte +160 -0
- package/src/{component-editor → editor/component-editor}/NotificationEditor.svelte +67 -38
- package/src/{component-editor → editor/component-editor}/ProgressBarEditor.svelte +5 -4
- package/src/{component-editor → editor/component-editor}/RadioButtonEditor.svelte +3 -3
- package/src/editor/component-editor/SectionDividerEditor.svelte +565 -0
- package/src/{component-editor → editor/component-editor}/SegmentedControlEditor.svelte +2 -2
- package/src/{component-editor → editor/component-editor}/StandardButtonsEditor.svelte +29 -21
- package/src/{component-editor → editor/component-editor}/TabBarEditor.svelte +9 -14
- package/src/{component-editor → editor/component-editor}/TableEditor.svelte +9 -18
- package/src/{component-editor → editor/component-editor}/TooltipEditor.svelte +11 -47
- package/src/editor/component-editor/editors.d.ts +10 -0
- package/src/{component-editor → editor/component-editor}/registry.ts +28 -18
- package/src/{component-editor → editor/component-editor}/scaffolding/AngleDial.svelte +54 -15
- package/src/{component-editor → editor/component-editor}/scaffolding/ComponentEditorBase.svelte +3 -51
- package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileManager.svelte +151 -424
- package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileMenu.svelte +18 -170
- package/src/{component-editor → editor/component-editor}/scaffolding/ComponentsTab.svelte +2 -2
- package/src/{component-editor → editor/component-editor}/scaffolding/CopyFromMenu.svelte +44 -4
- package/src/{component-editor → editor/component-editor}/scaffolding/FieldsetWrapper.svelte +1 -1
- package/src/{component-editor → editor/component-editor}/scaffolding/LinkageChart.svelte +6 -6
- package/src/{component-editor → editor/component-editor}/scaffolding/LinkedBlock.svelte +6 -12
- package/src/editor/component-editor/scaffolding/NonStylableConfig.svelte +38 -0
- package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
- package/src/{component-editor → editor/component-editor}/scaffolding/SaveAsDialog.svelte +66 -12
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +85 -0
- package/src/editor/component-editor/scaffolding/ShadowBackdropControls.svelte +132 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +345 -0
- package/src/{component-editor → editor/component-editor}/scaffolding/TokenLayout.svelte +17 -12
- package/src/{component-editor → editor/component-editor}/scaffolding/TypeEditor.svelte +13 -1
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +858 -0
- package/src/{component-editor → editor/component-editor}/scaffolding/buildTypeGroupTokens.ts +1 -0
- package/src/{component-editor → editor/component-editor}/scaffolding/editorContext.ts +19 -9
- package/src/{component-editor → editor/component-editor}/scaffolding/linkedBlock.ts +2 -2
- package/src/{component-editor → editor/component-editor}/scaffolding/types.ts +25 -0
- package/src/{lib → editor/core/components}/componentConfigKeys.ts +8 -0
- package/src/{lib → editor/core/components}/componentConfigService.ts +3 -3
- package/src/{lib → editor/core/components}/componentPersist.ts +11 -9
- package/src/editor/core/flashStatus.ts +30 -0
- package/src/{lib → editor/core/fonts}/fontLoader.ts +2 -2
- package/src/{lib → editor/core/fonts}/fontMigration.ts +4 -4
- package/src/{lib → editor/core/fonts}/fontParse.ts +1 -1
- package/src/editor/core/manifests/manifestService.ts +171 -0
- package/src/editor/core/palettes/familySwap.ts +99 -0
- package/src/{lib → editor/core/palettes}/paletteDerivation.ts +71 -2
- package/src/{lib → editor/core/palettes}/tokenRegistry.ts +9 -6
- package/src/editor/core/productionPulse.ts +37 -0
- package/src/{lib → editor/core/routing}/router.ts +1 -1
- package/src/{lib/files/versionedFileResource.ts → editor/core/storage/files/versionedFileResourceClient.ts} +8 -1
- package/src/{lib → editor/core/store}/editorCore.ts +24 -8
- package/src/{lib → editor/core/store}/editorPersistence.ts +3 -3
- package/src/{lib → editor/core/store}/editorRenderer.ts +2 -2
- package/src/{lib → editor/core/store}/editorStore.ts +222 -28
- package/src/{lib → editor/core/store}/editorTypes.ts +56 -13
- package/src/editor/core/store/gradientSource.ts +192 -0
- package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
- package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
- package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
- package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
- package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
- package/src/{lib → editor/core/themes}/migrations/index.ts +10 -0
- package/src/{lib → editor/core/themes}/slices/columns.ts +2 -2
- package/src/{lib → editor/core/themes}/slices/components.ts +20 -6
- package/src/{lib → editor/core/themes}/slices/fonts.ts +1 -1
- package/src/{lib → editor/core/themes}/slices/gradients.ts +89 -14
- package/src/{lib → editor/core/themes}/slices/overlays.ts +1 -1
- package/src/{lib → editor/core/themes}/slices/palettes.ts +1 -1
- package/src/{lib → editor/core/themes}/slices/shadows.ts +3 -3
- package/src/{lib → editor/core/themes}/themeInit.ts +8 -8
- package/src/{lib → editor/core/themes}/themeService.ts +6 -6
- package/src/{lib → editor/core/themes}/themeTypes.ts +67 -8
- package/src/editor/index.ts +69 -0
- package/src/{lib → editor/overlay}/ColumnsOverlay.svelte +0 -1
- package/src/{lib → editor/overlay}/LiveEditorOverlay.svelte +80 -129
- package/src/{lib → editor/overlay}/columnsOverlay.ts +2 -2
- package/src/{pages → editor/pages}/ComponentEditorPage.svelte +12 -12
- package/src/{pages → editor/pages}/Editor.svelte +4 -4
- package/src/{pages → editor/pages}/EditorShell.svelte +18 -36
- package/src/{styles → editor/styles}/ui-editor.css +43 -22
- package/src/{styles → editor/styles}/ui-form-controls.css +23 -24
- package/src/{ui → editor/ui}/BezierCurveEditor.svelte +119 -68
- package/src/{ui → editor/ui}/ColorEditPanel.svelte +13 -13
- package/src/{ui → editor/ui}/EditorViewSwitcher.svelte +7 -6
- package/src/editor/ui/FileLoadList.svelte +367 -0
- package/src/editor/ui/FilePill.svelte +80 -0
- package/src/editor/ui/FontStackEditor.svelte +499 -0
- package/src/editor/ui/GradientEditor.svelte +690 -0
- package/src/{ui → editor/ui}/GradientStopPicker.svelte +12 -4
- package/src/editor/ui/ManifestFileManager.svelte +438 -0
- package/src/{ui → editor/ui}/PaletteEditor.svelte +180 -673
- package/src/editor/ui/ProjectFontsSection.svelte +638 -0
- package/src/{ui → editor/ui}/SurfacesTab.svelte +3 -3
- package/src/{ui → editor/ui}/TextTab.svelte +3 -3
- package/src/editor/ui/ThemeFileManager.svelte +783 -0
- package/src/{ui → editor/ui}/UICopyPopover.svelte +4 -4
- package/src/{ui → editor/ui}/UIFontFamilySelector.svelte +6 -7
- package/src/{ui → editor/ui}/UIFontSizeSelector.svelte +4 -1
- package/src/editor/ui/UIInfoPopover.svelte +243 -0
- package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
- package/src/{ui → editor/ui}/UILineHeightSelector.svelte +5 -5
- package/src/{ui → editor/ui}/UILinkToggle.svelte +2 -2
- package/src/{ui → editor/ui}/UIPaddingSelector.svelte +6 -6
- package/src/{ui → editor/ui}/UIPaletteSelector.svelte +57 -30
- package/src/editor/ui/UIPillButton.svelte +168 -0
- package/src/{ui → editor/ui}/UIRadio.svelte +2 -2
- package/src/{ui → editor/ui}/UIRelinkConfirmPopover.svelte +4 -4
- package/src/editor/ui/UISegmentedControl.svelte +114 -0
- package/src/editor/ui/UISquareButton.svelte +172 -0
- package/src/{ui → editor/ui}/UITokenSelector.svelte +14 -11
- package/src/{ui → editor/ui}/UIVariantSelector.svelte +1 -1
- package/src/{ui → editor/ui}/VariablesTab.svelte +46 -17
- package/src/{ui → editor/ui}/palette/GradientStopEditor.svelte +13 -13
- package/src/{ui → editor/ui}/palette/OverridesPanel.svelte +24 -47
- package/src/{ui → editor/ui}/palette/PaletteBase.svelte +11 -8
- package/src/{ui → editor/ui}/palette/paletteEditorState.ts +1 -1
- package/src/editor/ui/palette/paletteMath.ts +275 -0
- package/src/{ui → editor/ui}/sections/ColumnsSection.svelte +137 -18
- package/src/{ui → editor/ui}/sections/GradientsSection.svelte +8 -8
- package/src/{ui → editor/ui}/sections/OverlaysSection.svelte +18 -18
- package/src/{ui → editor/ui}/sections/ShadowsSection.svelte +23 -23
- package/src/{ui → editor/ui}/sections/TokenScaleTable.svelte +3 -3
- package/src/{components → system/components}/Badge.svelte +0 -36
- package/src/{components → system/components}/Button.svelte +2 -2
- package/src/{components → system/components}/Card.svelte +34 -60
- package/src/{components → system/components}/CollapsibleSection.svelte +25 -2
- package/src/{components → system/components}/CornerBadge.svelte +8 -24
- package/src/{components → system/components}/Dialog.svelte +1 -1
- package/src/system/components/FloatingTokenTags.css +275 -0
- package/src/system/components/FloatingTokenTags.svelte +543 -0
- package/src/{components → system/components}/InlineEditActions.svelte +6 -4
- package/src/system/components/MenuSelect.svelte +229 -0
- package/src/{components → system/components}/Notification.svelte +8 -1
- package/src/{components → system/components}/ProgressBar.svelte +29 -11
- package/src/system/components/SectionDivider.svelte +560 -0
- package/src/{components → system/components}/SegmentedControl.svelte +49 -43
- package/src/{components → system/components}/TabBar.svelte +81 -65
- package/src/{components → system/components}/Table.svelte +17 -3
- package/src/{components → system/components}/Tooltip.svelte +6 -4
- package/src/system/styles/CONVENTIONS.md +178 -0
- package/src/system/styles/fonts.css +20 -0
- package/src/system/styles/tokens.css +601 -0
- package/src/system/styles/tokens.generated.css +544 -0
- package/src/component-editor/ImageEditor.svelte +0 -74
- package/src/component-editor/SectionDividerEditor.svelte +0 -265
- package/src/component-editor/scaffolding/DividerEditor.svelte +0 -94
- package/src/component-editor/scaffolding/GradientCard.svelte +0 -296
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +0 -62
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +0 -37
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +0 -61
- package/src/component-editor/scaffolding/StateBlock.svelte +0 -132
- package/src/component-editor/scaffolding/VariantGroup.svelte +0 -310
- package/src/components/SectionDivider.svelte +0 -483
- package/src/data/google-fonts.json +0 -75
- package/src/lib/index.ts +0 -68
- package/src/lib/presetService.ts +0 -214
- package/src/lib/productionPulse.ts +0 -32
- package/src/styles/fonts.css +0 -30
- package/src/styles/tokens.css +0 -1324
- package/src/ui/FontStackEditor.svelte +0 -361
- package/src/ui/GradientEditor.svelte +0 -470
- package/src/ui/PresetFileManager.svelte +0 -1116
- package/src/ui/ProjectFontsSection.svelte +0 -645
- package/src/ui/ThemeFileManager.svelte +0 -1020
- package/src/ui/UnsavedComponentsDialog.svelte +0 -315
- /package/src/{component-editor → editor/component-editor}/index.ts +0 -0
- /package/src/{component-editor → editor/component-editor}/scaffolding/DemoHeader.svelte +0 -0
- /package/src/{component-editor → editor/component-editor}/scaffolding/componentSectionType.ts +0 -0
- /package/src/{component-editor → editor/component-editor}/scaffolding/componentSources.ts +0 -0
- /package/src/{component-editor → editor/component-editor}/scaffolding/defaultSections.ts +0 -0
- /package/src/{component-editor → editor/component-editor}/scaffolding/siblings.ts +0 -0
- /package/src/{lib → editor/core}/cssVarSync.ts +0 -0
- /package/src/{lib → editor/core/palettes}/oklch.ts +0 -0
- /package/src/{lib → editor/core/routing}/navLinkTypes.ts +0 -0
- /package/src/{lib → editor/core/routing}/parentRouteStore.ts +0 -0
- /package/src/{lib → editor/core/storage}/storage.ts +0 -0
- /package/src/{lib → editor/core/store}/editorConfig.ts +0 -0
- /package/src/{lib → editor/core/store}/editorConfigStore.ts +0 -0
- /package/src/{lib → editor/core/store}/editorKeybindings.ts +0 -0
- /package/src/{lib → editor/core/store}/editorViewStore.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-05-10-sectiondivider-gradient-stops.ts +0 -0
- /package/src/{lib → editor/core/themes}/migrations/2026-05-13-primary-to-brand.ts +0 -0
- /package/src/{lib → editor/core/themes}/parsers/globalRootBlock.ts +0 -0
- /package/src/{lib → editor/core/themes}/slices/domainVars.ts +0 -0
- /package/src/{lib → editor/overlay}/overlayState.ts +0 -0
- /package/src/{pages → editor/pages}/ComponentEditorPage.svelte.d.ts +0 -0
- /package/src/{pages → editor/pages}/Editor.svelte.d.ts +0 -0
- /package/src/{ui → editor/ui}/Toggle.svelte +0 -0
- /package/src/{ui → editor/ui}/UIDialog.svelte +0 -0
- /package/src/{ui → editor/ui}/UIFontWeightSelector.svelte +0 -0
- /package/src/{ui → editor/ui}/UIOptionItem.svelte +0 -0
- /package/src/{ui → editor/ui}/UIOptionList.svelte +0 -0
- /package/src/{ui → editor/ui}/UIRadioGroup.svelte +0 -0
- /package/src/{lib → editor/ui}/copyPopover.ts +0 -0
- /package/src/{ui → editor/ui}/curveEngine.ts +0 -0
- /package/src/{ui → editor/ui}/index.ts +0 -0
- /package/src/{ui → editor/ui}/keepInViewport.ts +0 -0
- /package/src/{ui → editor/ui}/palette/ScaleCurveEditor.svelte +0 -0
- /package/src/{lib → editor/ui}/scrollSection.ts +0 -0
- /package/src/{ui → editor/ui}/sections/tokenScales.ts +0 -0
- /package/src/{ui → editor/ui}/variantScales.ts +0 -0
- /package/src/{assets → system/assets}/newspaper.webp +0 -0
- /package/src/{assets → system/assets}/offering.webp +0 -0
- /package/src/{components → system/components}/Callout.svelte +0 -0
- /package/src/{components → system/components}/Image.svelte +0 -0
- /package/src/{components → system/components}/RadioButton.svelte +0 -0
- /package/src/{components → system/components}/types.ts +0 -0
- /package/src/{styles → system/styles}/_padding.scss +0 -0
- /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
- /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
- /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
- /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
- /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
- /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin.woff2 +0 -0
package/dist-plugin/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
//
|
|
1
|
+
// vite-plugin/themeFileApi.ts
|
|
2
2
|
import fs2 from "fs";
|
|
3
3
|
import path2 from "path";
|
|
4
4
|
|
|
5
|
-
// src/
|
|
5
|
+
// src/editor/core/themes/parsers/globalRootBlock.ts
|
|
6
6
|
function extractGlobalRootBody(source) {
|
|
7
7
|
const re = /:global\(:root\)\s*\{([^}]*)\}/g;
|
|
8
8
|
const bodies = [];
|
|
@@ -13,12 +13,416 @@ function extractGlobalRootBody(source) {
|
|
|
13
13
|
return bodies.join("\n");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
// src/
|
|
16
|
+
// src/editor/core/storage/files/versionedFileResourceClient.ts
|
|
17
17
|
function sanitizeFileName(name) {
|
|
18
18
|
return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9\-_]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unnamed";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// src/
|
|
21
|
+
// src/editor/core/palettes/oklch.ts
|
|
22
|
+
function srgbToLinear(c) {
|
|
23
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
24
|
+
}
|
|
25
|
+
function linearToSrgb(c) {
|
|
26
|
+
return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
27
|
+
}
|
|
28
|
+
function hexToLinearRgb(hex) {
|
|
29
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
30
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
31
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
32
|
+
return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
|
|
33
|
+
}
|
|
34
|
+
function linearRgbToHex(r, g, b) {
|
|
35
|
+
const toHex = (c) => {
|
|
36
|
+
const v = Math.round(Math.max(0, Math.min(1, linearToSrgb(c))) * 255);
|
|
37
|
+
return v.toString(16).padStart(2, "0");
|
|
38
|
+
};
|
|
39
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
40
|
+
}
|
|
41
|
+
function linearRgbToOklab(r, g, b) {
|
|
42
|
+
const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
|
43
|
+
const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
|
|
44
|
+
const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
|
|
45
|
+
const l = Math.cbrt(l_);
|
|
46
|
+
const m = Math.cbrt(m_);
|
|
47
|
+
const s = Math.cbrt(s_);
|
|
48
|
+
return [
|
|
49
|
+
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
50
|
+
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
51
|
+
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function oklabToLinearRgb(L, a, b) {
|
|
55
|
+
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
|
56
|
+
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
|
57
|
+
const s_ = L - 0.0894841775 * a - 1.291485548 * b;
|
|
58
|
+
const l = l_ * l_ * l_;
|
|
59
|
+
const m = m_ * m_ * m_;
|
|
60
|
+
const s = s_ * s_ * s_;
|
|
61
|
+
return [
|
|
62
|
+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
63
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
64
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
function oklabToOklch(L, a, b) {
|
|
68
|
+
const c = Math.sqrt(a * a + b * b);
|
|
69
|
+
let h = Math.atan2(b, a) * 180 / Math.PI;
|
|
70
|
+
if (h < 0) h += 360;
|
|
71
|
+
return { l: L, c, h };
|
|
72
|
+
}
|
|
73
|
+
function oklchToOklab(l, c, h) {
|
|
74
|
+
const hRad = h * Math.PI / 180;
|
|
75
|
+
return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
|
|
76
|
+
}
|
|
77
|
+
function hexToOklch(hex) {
|
|
78
|
+
const [r, g, b] = hexToLinearRgb(hex);
|
|
79
|
+
const [L, a, bVal] = linearRgbToOklab(r, g, b);
|
|
80
|
+
return oklabToOklch(L, a, bVal);
|
|
81
|
+
}
|
|
82
|
+
function oklchToHex(l, c, h) {
|
|
83
|
+
const [L, a, b] = oklchToOklab(l, c, h);
|
|
84
|
+
const [r, g, bVal] = oklabToLinearRgb(L, a, b);
|
|
85
|
+
return linearRgbToHex(r, g, bVal);
|
|
86
|
+
}
|
|
87
|
+
function isInGamut(r, g, b) {
|
|
88
|
+
const eps = 1e-4;
|
|
89
|
+
return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
|
|
90
|
+
}
|
|
91
|
+
function gamutClamp(l, c, h) {
|
|
92
|
+
if (l <= 0) return { l: 0, c: 0, h };
|
|
93
|
+
if (l >= 1) return { l: 1, c: 0, h };
|
|
94
|
+
const [L, a, b] = oklchToOklab(l, c, h);
|
|
95
|
+
const [r, g, bVal] = oklabToLinearRgb(L, a, b);
|
|
96
|
+
if (isInGamut(r, g, bVal)) return { l, c, h };
|
|
97
|
+
let lo = 0;
|
|
98
|
+
let hi = c;
|
|
99
|
+
for (let i = 0; i < 20; i++) {
|
|
100
|
+
const mid = (lo + hi) / 2;
|
|
101
|
+
const [La, aa, ba] = oklchToOklab(l, mid, h);
|
|
102
|
+
const [rr, gg, bb] = oklabToLinearRgb(La, aa, ba);
|
|
103
|
+
if (isInGamut(rr, gg, bb)) {
|
|
104
|
+
lo = mid;
|
|
105
|
+
} else {
|
|
106
|
+
hi = mid;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { l, c: lo, h };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/editor/ui/curveEngine.ts
|
|
113
|
+
function makeAnchor(x, y, tangentLen = 15) {
|
|
114
|
+
return { x, y, inDx: -tangentLen, inDy: 0, outDx: tangentLen, outDy: 0 };
|
|
115
|
+
}
|
|
116
|
+
function evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, t) {
|
|
117
|
+
const u = 1 - t, u2 = u * u, u3 = u2 * u;
|
|
118
|
+
const t2 = t * t, t3 = t2 * t;
|
|
119
|
+
return {
|
|
120
|
+
x: u3 * p0x + 3 * u2 * t * c0x + 3 * u * t2 * c1x + t3 * p1x,
|
|
121
|
+
y: u3 * p0y + 3 * u2 * t * c0y + 3 * u * t2 * c1y + t3 * p1y
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function sampleCurve(anchors, xPos) {
|
|
125
|
+
if (anchors.length === 0) return 0;
|
|
126
|
+
if (anchors.length === 1) return anchors[0].y;
|
|
127
|
+
if (xPos <= anchors[0].x) return anchors[0].y;
|
|
128
|
+
if (xPos >= anchors[anchors.length - 1].x) return anchors[anchors.length - 1].y;
|
|
129
|
+
let seg = 0;
|
|
130
|
+
while (seg < anchors.length - 2 && anchors[seg + 1].x < xPos) seg++;
|
|
131
|
+
const a0 = anchors[seg], a1 = anchors[seg + 1];
|
|
132
|
+
const p0x = a0.x, p0y = a0.y;
|
|
133
|
+
const c0x = a0.x + a0.outDx, c0y = a0.y + a0.outDy;
|
|
134
|
+
const c1x = a1.x + a1.inDx, c1y = a1.y + a1.inDy;
|
|
135
|
+
const p1x = a1.x, p1y = a1.y;
|
|
136
|
+
let lo = 0, hi = 1;
|
|
137
|
+
for (let i = 0; i < 20; i++) {
|
|
138
|
+
const mid = (lo + hi) / 2;
|
|
139
|
+
const pt = evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, mid);
|
|
140
|
+
if (pt.x < xPos) lo = mid;
|
|
141
|
+
else hi = mid;
|
|
142
|
+
}
|
|
143
|
+
return evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, (lo + hi) / 2).y;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/editor/core/palettes/paletteDerivation.ts
|
|
147
|
+
var PALETTE_SPECS = [
|
|
148
|
+
{ label: "Neutral", cssNamespace: "neutral", mode: "gray", initialColor: "#808080" },
|
|
149
|
+
{ label: "Alternate", cssNamespace: "alternate", mode: "gray", initialColor: "#808080" },
|
|
150
|
+
{ label: "Background", cssNamespace: "canvas", mode: "chromatic", emptySelector: true, initialColor: "#1a1a2e" },
|
|
151
|
+
{ label: "Brand", cssNamespace: "brand", mode: "chromatic", initialColor: "#c93636" },
|
|
152
|
+
{ label: "Accent", cssNamespace: "accent", mode: "chromatic", initialColor: "#f49e0b" },
|
|
153
|
+
{ label: "Special", cssNamespace: "special", mode: "chromatic", initialColor: "#8b5cf6" },
|
|
154
|
+
{ label: "Info", cssNamespace: "info", mode: "chromatic", initialColor: "#3077e8" },
|
|
155
|
+
{ label: "Success", cssNamespace: "success", mode: "chromatic", initialColor: "#21c45d" },
|
|
156
|
+
{ label: "Warning", cssNamespace: "warning", mode: "chromatic", initialColor: "#e66e1a" },
|
|
157
|
+
{ label: "Danger", cssNamespace: "danger", mode: "chromatic", initialColor: "#e8304f" }
|
|
158
|
+
];
|
|
159
|
+
var PALETTE_STEPS = [
|
|
160
|
+
{ label: "100" },
|
|
161
|
+
{ label: "200" },
|
|
162
|
+
{ label: "300" },
|
|
163
|
+
{ label: "400" },
|
|
164
|
+
{ label: "500" },
|
|
165
|
+
{ label: "600" },
|
|
166
|
+
{ label: "700" },
|
|
167
|
+
{ label: "800" },
|
|
168
|
+
{ label: "850" },
|
|
169
|
+
{ label: "900" },
|
|
170
|
+
{ label: "950" }
|
|
171
|
+
];
|
|
172
|
+
var GRAY_STEPS = [
|
|
173
|
+
{ label: "100" },
|
|
174
|
+
{ label: "200" },
|
|
175
|
+
{ label: "300" },
|
|
176
|
+
{ label: "400" },
|
|
177
|
+
{ label: "500" },
|
|
178
|
+
{ label: "600" },
|
|
179
|
+
{ label: "700" },
|
|
180
|
+
{ label: "800" },
|
|
181
|
+
{ label: "850" },
|
|
182
|
+
{ label: "900" },
|
|
183
|
+
{ label: "950" }
|
|
184
|
+
];
|
|
185
|
+
var SCALES = [
|
|
186
|
+
{
|
|
187
|
+
title: "Surfaces",
|
|
188
|
+
isText: false,
|
|
189
|
+
steps: [
|
|
190
|
+
{ name: "lowest", position: -1 },
|
|
191
|
+
{ name: "lower", position: -2 / 3 },
|
|
192
|
+
{ name: "low", position: -1 / 3 },
|
|
193
|
+
{ name: "default", position: 0 },
|
|
194
|
+
{ name: "high", position: 1 / 3 },
|
|
195
|
+
{ name: "higher", position: 2 / 3 },
|
|
196
|
+
{ name: "highest", position: 1 }
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
title: "Borders",
|
|
201
|
+
isText: false,
|
|
202
|
+
steps: [
|
|
203
|
+
{ name: "faint", position: -1 },
|
|
204
|
+
{ name: "subtle", position: -0.5 },
|
|
205
|
+
{ name: "default", position: 0 },
|
|
206
|
+
{ name: "medium", position: 0.5 },
|
|
207
|
+
{ name: "strong", position: 1 }
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
title: "Text",
|
|
212
|
+
isText: true,
|
|
213
|
+
steps: [
|
|
214
|
+
{ name: "primary", position: 0 },
|
|
215
|
+
{ name: "secondary", position: 0 },
|
|
216
|
+
{ name: "tertiary", position: 0 },
|
|
217
|
+
{ name: "muted", position: 0 },
|
|
218
|
+
{ name: "disabled", position: 0 }
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
var DEFAULT_PALETTE_LIGHTNESS = () => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
|
|
223
|
+
var DEFAULT_PALETTE_SATURATION = () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
|
|
224
|
+
var DEFAULT_GRAY_LIGHTNESS = () => [makeAnchor(0, 92, 5), makeAnchor(100, 3, 5)];
|
|
225
|
+
var DEFAULT_GRAY_SATURATION = () => [makeAnchor(0, 20, 30), makeAnchor(100, 20, 30)];
|
|
226
|
+
var DEFAULT_TINT_CHROMA = 0.04;
|
|
227
|
+
var defaultScaleCurves = {
|
|
228
|
+
Surfaces: {
|
|
229
|
+
lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
|
|
230
|
+
saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
|
|
231
|
+
},
|
|
232
|
+
Borders: {
|
|
233
|
+
lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
|
|
234
|
+
saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
|
|
235
|
+
},
|
|
236
|
+
Text: {
|
|
237
|
+
lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
|
|
238
|
+
saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function paletteStepKey(label) {
|
|
242
|
+
return `Palette-${label}`;
|
|
243
|
+
}
|
|
244
|
+
function grayStepKey(label) {
|
|
245
|
+
return `gray-${label}`;
|
|
246
|
+
}
|
|
247
|
+
function stepKey(scaleTitle, stepName) {
|
|
248
|
+
return `${scaleTitle}-${stepName}`;
|
|
249
|
+
}
|
|
250
|
+
function stepIndexToX(index, total) {
|
|
251
|
+
return total > 1 ? index / (total - 1) * 100 : 50;
|
|
252
|
+
}
|
|
253
|
+
function computePaletteColor(index, base, lightnessCurve, saturationCurve, curveOffset) {
|
|
254
|
+
const { c: baseC, h } = hexToOklch(base);
|
|
255
|
+
const xPos = stepIndexToX(index, PALETTE_STEPS.length);
|
|
256
|
+
const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
|
|
257
|
+
const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
|
|
258
|
+
const targetC = baseC * satMul;
|
|
259
|
+
const clamped = gamutClamp(targetL, targetC, h);
|
|
260
|
+
return oklchToHex(clamped.l, clamped.c, clamped.h);
|
|
261
|
+
}
|
|
262
|
+
function computeGrayColor(index, hue, chroma, grayLightnessCurve, graySaturationCurve, curveOffset) {
|
|
263
|
+
const xPos = stepIndexToX(index, GRAY_STEPS.length);
|
|
264
|
+
const lOff = curveOffset["gray-lightness"] ?? 0;
|
|
265
|
+
const sOff = curveOffset["gray-saturation"] ?? 0;
|
|
266
|
+
const targetL = Math.max(0, Math.min(100, sampleCurve(grayLightnessCurve, xPos) + lOff)) / 100;
|
|
267
|
+
const satMul = Math.max(0, Math.min(2, (sampleCurve(graySaturationCurve, xPos) + sOff) / 100));
|
|
268
|
+
const targetC = chroma * satMul;
|
|
269
|
+
const clamped = gamutClamp(targetL, targetC, hue);
|
|
270
|
+
return oklchToHex(clamped.l, clamped.c, clamped.h);
|
|
271
|
+
}
|
|
272
|
+
function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
|
|
273
|
+
const scale = SCALES.find((s) => s.title === scaleTitle);
|
|
274
|
+
const idx = scale.steps.indexOf(step);
|
|
275
|
+
const xPos = stepIndexToX(idx, scale.steps.length);
|
|
276
|
+
const defs = defaultScaleCurves[scaleTitle];
|
|
277
|
+
const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
|
|
278
|
+
const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
|
|
279
|
+
const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
|
|
280
|
+
const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
|
|
281
|
+
const { l: baseL, c: baseC, h: baseH } = hexToOklch(base);
|
|
282
|
+
let targetL;
|
|
283
|
+
if (scale.isText) {
|
|
284
|
+
const lMul = Math.max(0, Math.min(2, (sampleCurve(lCurve, xPos) + lOff) / 100));
|
|
285
|
+
targetL = Math.max(0, Math.min(1, baseL * lMul));
|
|
286
|
+
} else {
|
|
287
|
+
targetL = Math.max(0, Math.min(100, sampleCurve(lCurve, xPos) + lOff)) / 100;
|
|
288
|
+
}
|
|
289
|
+
const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
|
|
290
|
+
const targetC = baseC * satMul;
|
|
291
|
+
const clamped = gamutClamp(targetL, targetC, baseH);
|
|
292
|
+
return oklchToHex(clamped.l, clamped.c, clamped.h);
|
|
293
|
+
}
|
|
294
|
+
function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
|
|
295
|
+
if (cssNamespace === null) return null;
|
|
296
|
+
if (scaleTitle === "Surfaces") {
|
|
297
|
+
const suffix = stepName === "default" ? "" : `-${stepName}`;
|
|
298
|
+
return cssNamespace === "neutral" ? `--surface-neutral${suffix}` : `--surface-${cssNamespace}${suffix}`;
|
|
299
|
+
}
|
|
300
|
+
if (scaleTitle === "Borders") {
|
|
301
|
+
const suffix = stepName === "default" ? "" : `-${stepName}`;
|
|
302
|
+
return cssNamespace === "neutral" ? `--border-neutral${suffix}` : `--border-${cssNamespace}${suffix}`;
|
|
303
|
+
}
|
|
304
|
+
if (scaleTitle === "Text") {
|
|
305
|
+
if (cssNamespace === "neutral") return `--text-${stepName}`;
|
|
306
|
+
return stepName === "primary" ? `--text-${cssNamespace}` : `--text-${cssNamespace}-${stepName}`;
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
function derivePaletteVars(spec, config) {
|
|
311
|
+
const out = {};
|
|
312
|
+
if (!config) return out;
|
|
313
|
+
const baseColor = config.baseColor ?? spec.initialColor;
|
|
314
|
+
const overrides = config.overrides ?? {};
|
|
315
|
+
const curveOffset = config.curveOffset ?? {};
|
|
316
|
+
const scaleCurves = config.scaleCurves ?? {};
|
|
317
|
+
let baseForScales;
|
|
318
|
+
if (spec.mode === "gray") {
|
|
319
|
+
const grayLightnessCurve = config.grayLightnessCurve ?? DEFAULT_GRAY_LIGHTNESS();
|
|
320
|
+
const graySaturationCurve = config.graySaturationCurve ?? DEFAULT_GRAY_SATURATION();
|
|
321
|
+
const tintHue = config.tintHue ?? 240;
|
|
322
|
+
const tintChroma = config.tintChroma ?? DEFAULT_TINT_CHROMA;
|
|
323
|
+
let gray500 = "#808080";
|
|
324
|
+
GRAY_STEPS.forEach((step, index) => {
|
|
325
|
+
const k = grayStepKey(step.label);
|
|
326
|
+
const hex = computeGrayColor(index, tintHue, tintChroma, grayLightnessCurve, graySaturationCurve, curveOffset);
|
|
327
|
+
const effective = k in overrides ? overrides[k] : hex;
|
|
328
|
+
out[`--color-${spec.cssNamespace}-${step.label}`] = effective;
|
|
329
|
+
if (step.label === "500") gray500 = hex;
|
|
330
|
+
});
|
|
331
|
+
baseForScales = gray500;
|
|
332
|
+
} else {
|
|
333
|
+
const lightnessCurve = config.lightnessCurve ?? DEFAULT_PALETTE_LIGHTNESS();
|
|
334
|
+
const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
|
|
335
|
+
PALETTE_STEPS.forEach((ps, index) => {
|
|
336
|
+
const k = paletteStepKey(ps.label);
|
|
337
|
+
const hex = computePaletteColor(index, baseColor, lightnessCurve, saturationCurve, curveOffset);
|
|
338
|
+
const effective = k in overrides ? overrides[k] : hex;
|
|
339
|
+
out[`--color-${spec.cssNamespace}-${ps.label}`] = effective;
|
|
340
|
+
});
|
|
341
|
+
baseForScales = baseColor;
|
|
342
|
+
}
|
|
343
|
+
for (const scale of SCALES) {
|
|
344
|
+
for (const step of scale.steps) {
|
|
345
|
+
const k = stepKey(scale.title, step.name);
|
|
346
|
+
const hex = k in overrides ? overrides[k] : computeDerivedColor(step, baseForScales, scale.title, scaleCurves, curveOffset);
|
|
347
|
+
const varName = scaleToCssVar(scale.title, step.name, spec.cssNamespace);
|
|
348
|
+
if (varName) out[varName] = hex;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (spec.emptySelector) {
|
|
352
|
+
const emptyMode = config.emptyMode ?? "solid";
|
|
353
|
+
const emptyStep = config.emptyStep ?? "850";
|
|
354
|
+
const gradientStyle = config.gradientStyle ?? "linear";
|
|
355
|
+
const gradientAngle = config.gradientAngle ?? 180;
|
|
356
|
+
const gradientReverse = config.gradientReverse ?? false;
|
|
357
|
+
const gradientSize = config.gradientSize ?? "page";
|
|
358
|
+
const gradientStops = config.gradientStops ?? [
|
|
359
|
+
{ position: 0, paletteLabel: "800" },
|
|
360
|
+
{ position: 100, paletteLabel: "950" }
|
|
361
|
+
];
|
|
362
|
+
if (emptyMode === "solid") {
|
|
363
|
+
const stepHex = out[`--color-${spec.cssNamespace}-${emptyStep}`];
|
|
364
|
+
if (stepHex) out["--page-bg"] = stepHex;
|
|
365
|
+
out["--page-bg-attachment"] = "scroll";
|
|
366
|
+
} else {
|
|
367
|
+
const sortedStops = [...gradientStops].sort(
|
|
368
|
+
(a, b) => gradientReverse ? b.position - a.position : a.position - b.position
|
|
369
|
+
);
|
|
370
|
+
const stopsCss = sortedStops.map((s) => `${out[`--color-${spec.cssNamespace}-${s.paletteLabel}`] ?? "#000000"} ${s.position}%`).join(", ");
|
|
371
|
+
let gradient;
|
|
372
|
+
switch (gradientStyle) {
|
|
373
|
+
case "radial":
|
|
374
|
+
gradient = `radial-gradient(circle, ${stopsCss})`;
|
|
375
|
+
break;
|
|
376
|
+
case "conic":
|
|
377
|
+
gradient = `conic-gradient(from ${gradientAngle}deg, ${stopsCss})`;
|
|
378
|
+
break;
|
|
379
|
+
default:
|
|
380
|
+
gradient = `linear-gradient(${gradientAngle}deg, ${stopsCss})`;
|
|
381
|
+
}
|
|
382
|
+
out["--page-bg"] = gradient;
|
|
383
|
+
out["--page-bg-attachment"] = gradientSize === "window" ? "fixed" : "scroll";
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
function palettesToVars(palettes) {
|
|
389
|
+
const out = {};
|
|
390
|
+
for (const spec of PALETTE_SPECS) {
|
|
391
|
+
Object.assign(out, derivePaletteVars(spec, palettes[spec.label]));
|
|
392
|
+
}
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
var HEX_RE = /^#[0-9a-f]{6}$/i;
|
|
396
|
+
function reconcilePalettesFromCssVars(palettes, cssVars) {
|
|
397
|
+
const next = structuredClone(palettes);
|
|
398
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
399
|
+
const snapped = /* @__PURE__ */ new Set();
|
|
400
|
+
for (const spec of PALETTE_SPECS) {
|
|
401
|
+
const current = next[spec.label];
|
|
402
|
+
if (current === void 0) continue;
|
|
403
|
+
if (current._imported === true) {
|
|
404
|
+
const anchorHex = cssVars[`--color-${spec.cssNamespace}-500`];
|
|
405
|
+
if (anchorHex && HEX_RE.test(anchorHex.trim())) {
|
|
406
|
+
const hex = anchorHex.trim();
|
|
407
|
+
if (spec.mode === "gray") {
|
|
408
|
+
const { c, h } = hexToOklch(hex);
|
|
409
|
+
next[spec.label] = { ...current, tintHue: h, tintChroma: c, _imported: false };
|
|
410
|
+
} else {
|
|
411
|
+
next[spec.label] = { ...current, baseColor: hex, _imported: false };
|
|
412
|
+
}
|
|
413
|
+
snapped.add(spec.label);
|
|
414
|
+
} else {
|
|
415
|
+
next[spec.label] = { ...current, _imported: false };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
for (const k of Object.keys(derivePaletteVars(spec, next[spec.label]))) {
|
|
419
|
+
consumed.add(k);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { palettes: next, consumed, snapped };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// vite-plugin/files/versionedFileResourceServer.ts
|
|
22
426
|
import fs from "fs";
|
|
23
427
|
import path from "path";
|
|
24
428
|
function versionedFileResourceServer(opts) {
|
|
@@ -78,7 +482,7 @@ function versionedFileResourceServer(opts) {
|
|
|
78
482
|
};
|
|
79
483
|
}
|
|
80
484
|
|
|
81
|
-
//
|
|
485
|
+
// vite-plugin/files/routeTable.ts
|
|
82
486
|
async function dispatch(req, res, routes) {
|
|
83
487
|
const url = req.url || "";
|
|
84
488
|
const method = req.method || "GET";
|
|
@@ -106,16 +510,45 @@ async function dispatch(req, res, routes) {
|
|
|
106
510
|
return false;
|
|
107
511
|
}
|
|
108
512
|
|
|
109
|
-
//
|
|
110
|
-
|
|
513
|
+
// vite-plugin/files/nameAllocator.ts
|
|
514
|
+
function nextAvailableName(exists, baseName, maxAttempts = 1e3) {
|
|
515
|
+
if (!exists(baseName)) return baseName;
|
|
516
|
+
for (let i = 2; i < maxAttempts; i++) {
|
|
517
|
+
const candidate = `${baseName}-${i}`;
|
|
518
|
+
if (!exists(candidate)) return candidate;
|
|
519
|
+
}
|
|
520
|
+
throw new Error(`Could not allocate a non-colliding name for "${baseName}"`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// vite-plugin/themeFileApi.ts
|
|
524
|
+
import { fileURLToPath } from "url";
|
|
525
|
+
var PKG_VERSION = (() => {
|
|
526
|
+
try {
|
|
527
|
+
let dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
528
|
+
for (let i = 0; i < 4; i++) {
|
|
529
|
+
const p = path2.join(dir, "package.json");
|
|
530
|
+
if (fs2.existsSync(p)) {
|
|
531
|
+
const json = JSON.parse(fs2.readFileSync(p, "utf-8"));
|
|
532
|
+
if (json?.name === "@motion-proto/live-tokens") return json.version ?? "";
|
|
533
|
+
}
|
|
534
|
+
const up = path2.dirname(dir);
|
|
535
|
+
if (up === dir) break;
|
|
536
|
+
dir = up;
|
|
537
|
+
}
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
return "";
|
|
541
|
+
})();
|
|
111
542
|
function themeFileApi(opts) {
|
|
112
543
|
const THEMES_DIR = path2.resolve(opts.themesDir);
|
|
113
544
|
const CSS_PATH = path2.resolve(opts.tokensCssPath);
|
|
545
|
+
const GENERATED_CSS_PATH = opts.tokensGeneratedCssPath ? path2.resolve(opts.tokensGeneratedCssPath) : path2.join(path2.dirname(CSS_PATH), "tokens.generated.css");
|
|
114
546
|
const FONTS_CSS_PATH = opts.fontsCssPath ? path2.resolve(opts.fontsCssPath) : path2.join(path2.dirname(CSS_PATH), "fonts.css");
|
|
115
547
|
const API_BASE = opts.apiBase ?? "/api";
|
|
116
548
|
const COMPONENT_CONFIGS_DIR = opts.componentConfigsDir ? path2.resolve(opts.componentConfigsDir) : path2.resolve("component-configs");
|
|
117
|
-
const COMPONENTS_SRC_DIR = opts.componentsSrcDir ? path2.resolve(opts.componentsSrcDir) : path2.resolve("src/components");
|
|
118
|
-
const
|
|
549
|
+
const COMPONENTS_SRC_DIR = opts.componentsSrcDir ? path2.resolve(opts.componentsSrcDir) : path2.resolve("src/system/components");
|
|
550
|
+
const MANIFESTS_DIR = opts.manifestsDir ? path2.resolve(opts.manifestsDir) : path2.resolve("manifests");
|
|
551
|
+
const LEGACY_PRESETS_DIR = path2.resolve("presets");
|
|
119
552
|
const themesResource = versionedFileResourceServer({
|
|
120
553
|
dir: THEMES_DIR
|
|
121
554
|
});
|
|
@@ -128,7 +561,7 @@ function themeFileApi(opts) {
|
|
|
128
561
|
}
|
|
129
562
|
return r;
|
|
130
563
|
}
|
|
131
|
-
const
|
|
564
|
+
const manifestsResource = versionedFileResourceServer({ dir: MANIFESTS_DIR });
|
|
132
565
|
function ensureThemesDir() {
|
|
133
566
|
themesResource.ensureDir();
|
|
134
567
|
if (!fs2.existsSync(path2.join(THEMES_DIR, "default.json"))) {
|
|
@@ -156,6 +589,17 @@ function themeFileApi(opts) {
|
|
|
156
589
|
res.setHeader("Content-Type", "application/json");
|
|
157
590
|
res.end(JSON.stringify(data));
|
|
158
591
|
}
|
|
592
|
+
function normalizeTheme(theme) {
|
|
593
|
+
if (!theme || typeof theme !== "object") return theme;
|
|
594
|
+
const palettes = theme.editorConfigs ?? {};
|
|
595
|
+
const cssVars = theme.cssVariables ?? {};
|
|
596
|
+
const { palettes: nextPalettes, consumed } = reconcilePalettesFromCssVars(palettes, cssVars);
|
|
597
|
+
const nextCssVars = {};
|
|
598
|
+
for (const [k, v] of Object.entries(cssVars)) {
|
|
599
|
+
if (!consumed.has(k)) nextCssVars[k] = v;
|
|
600
|
+
}
|
|
601
|
+
return { ...theme, editorConfigs: nextPalettes, cssVariables: nextCssVars };
|
|
602
|
+
}
|
|
159
603
|
const SYSTEM_CASCADES_SSR = {
|
|
160
604
|
"system-ui-sans": 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
161
605
|
"system-ui-serif": 'Georgia, "Times New Roman", serif',
|
|
@@ -187,42 +631,78 @@ function themeFileApi(opts) {
|
|
|
187
631
|
}
|
|
188
632
|
return out;
|
|
189
633
|
}
|
|
190
|
-
function
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
634
|
+
function regenerateTokensCss() {
|
|
635
|
+
const lines = [];
|
|
636
|
+
lines.push("/* Generated by themeFileApi from the production theme and component configs. Do not edit. */");
|
|
637
|
+
lines.push("/* tokens.css holds developer-authored defaults; this file holds editor overrides. */");
|
|
638
|
+
lines.push("");
|
|
639
|
+
const productionThemeName = themesResource.getProductionName();
|
|
640
|
+
const themePath = path2.join(THEMES_DIR, `${productionThemeName}.json`);
|
|
641
|
+
let themeVarCount = 0;
|
|
642
|
+
if (fs2.existsSync(themePath)) {
|
|
643
|
+
const themeData = JSON.parse(fs2.readFileSync(themePath, "utf-8"));
|
|
644
|
+
const cssVars = { ...themeData.cssVariables || {} };
|
|
645
|
+
Object.assign(cssVars, palettesToVars(themeData.editorConfigs ?? {}));
|
|
646
|
+
const resolvedFontVars = resolveFontStacks(themeData);
|
|
647
|
+
for (const [name, value] of Object.entries(resolvedFontVars)) {
|
|
648
|
+
cssVars[name] = value;
|
|
649
|
+
}
|
|
650
|
+
themeVarCount = Object.keys(cssVars).length;
|
|
651
|
+
if (themeVarCount > 0) {
|
|
652
|
+
lines.push(`/* Production theme: ${productionThemeName} */`);
|
|
653
|
+
lines.push(":root:root {");
|
|
654
|
+
for (const [name, value] of Object.entries(cssVars)) {
|
|
655
|
+
lines.push(` ${name}: ${value};`);
|
|
208
656
|
}
|
|
209
|
-
|
|
657
|
+
lines.push("}");
|
|
658
|
+
lines.push("");
|
|
210
659
|
}
|
|
211
|
-
);
|
|
212
|
-
let finalContent = updatedContent;
|
|
213
|
-
if (remaining.size > 0) {
|
|
214
|
-
const newVars = [...remaining].map((name) => ` ${name}: ${cssVars[name]};`).join("\n");
|
|
215
|
-
finalContent = finalContent.replace(
|
|
216
|
-
/\n\}(\s*)$/,
|
|
217
|
-
`
|
|
218
|
-
|
|
219
|
-
/* Token additions */
|
|
220
|
-
${newVars}
|
|
221
|
-
}$1`
|
|
222
|
-
);
|
|
223
660
|
}
|
|
224
|
-
|
|
225
|
-
|
|
661
|
+
let componentOverrideCount = 0;
|
|
662
|
+
if (fs2.existsSync(COMPONENT_CONFIGS_DIR)) {
|
|
663
|
+
const blocks = [];
|
|
664
|
+
for (const comp of listComponentNames()) {
|
|
665
|
+
const prod = componentResource(comp).getProductionName();
|
|
666
|
+
if (prod === "default") continue;
|
|
667
|
+
const prodCfg = readComponentConfig(comp, prod);
|
|
668
|
+
const defaultCfg = readComponentConfig(comp, "default");
|
|
669
|
+
if (!prodCfg || !defaultCfg) continue;
|
|
670
|
+
const overrides = [];
|
|
671
|
+
const defaultAliases = defaultCfg.aliases ?? {};
|
|
672
|
+
for (const [varName, semanticValue] of Object.entries(prodCfg.aliases ?? {})) {
|
|
673
|
+
if (!aliasValuesEqual(defaultAliases[varName], semanticValue)) {
|
|
674
|
+
overrides.push([varName, semanticValue]);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (overrides.length === 0) continue;
|
|
678
|
+
const block = [` /* ${comp} (${prod}) */`];
|
|
679
|
+
for (const [varName, semanticValue] of overrides) {
|
|
680
|
+
block.push(` ${varName}: ${aliasValueToCss(semanticValue)};`);
|
|
681
|
+
}
|
|
682
|
+
blocks.push(block);
|
|
683
|
+
componentOverrideCount += overrides.length;
|
|
684
|
+
}
|
|
685
|
+
if (blocks.length > 0) {
|
|
686
|
+
lines.push("/* Component aliases (production configs differing from defaults) */");
|
|
687
|
+
lines.push(":root:root {");
|
|
688
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
689
|
+
if (i > 0) lines.push("");
|
|
690
|
+
lines.push(...blocks[i]);
|
|
691
|
+
}
|
|
692
|
+
lines.push("}");
|
|
693
|
+
lines.push("");
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (!fs2.existsSync(path2.dirname(GENERATED_CSS_PATH))) {
|
|
697
|
+
fs2.mkdirSync(path2.dirname(GENERATED_CSS_PATH), { recursive: true });
|
|
698
|
+
}
|
|
699
|
+
fs2.writeFileSync(GENERATED_CSS_PATH, lines.join("\n"));
|
|
700
|
+
console.log(
|
|
701
|
+
`[regenerateTokensCss] Wrote ${path2.basename(GENERATED_CSS_PATH)} (${themeVarCount} theme vars, ${componentOverrideCount} component overrides)`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
function syncTokensToCss(_fileName) {
|
|
705
|
+
regenerateTokensCss();
|
|
226
706
|
}
|
|
227
707
|
function syncFontsToCss(fileName) {
|
|
228
708
|
const themePath = path2.join(THEMES_DIR, `${fileName}.json`);
|
|
@@ -234,15 +714,20 @@ ${newVars}
|
|
|
234
714
|
lines.push("/* Generated from the production theme by syncFontsToCss. Do not edit. */");
|
|
235
715
|
lines.push("/* Both fonts.css and fonts/ are in dist/, so relative paths work at runtime. */");
|
|
236
716
|
lines.push("");
|
|
237
|
-
|
|
717
|
+
const urlSources = sources.filter((s) => s.kind !== "font-face" && s.url);
|
|
718
|
+
const faceSources = sources.filter((s) => s.kind === "font-face" && s.cssText);
|
|
719
|
+
for (const source of urlSources) {
|
|
238
720
|
const familyNames = source.families.map((f) => f.name).join(", ");
|
|
239
721
|
const label = source.label ? `${source.label} \u2014 ${familyNames}` : familyNames;
|
|
240
722
|
lines.push(`/* ${label} */`);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
723
|
+
lines.push(`@import url('${source.url}');`);
|
|
724
|
+
lines.push("");
|
|
725
|
+
}
|
|
726
|
+
for (const source of faceSources) {
|
|
727
|
+
const familyNames = source.families.map((f) => f.name).join(", ");
|
|
728
|
+
const label = source.label ? `${source.label} \u2014 ${familyNames}` : familyNames;
|
|
729
|
+
lines.push(`/* ${label} */`);
|
|
730
|
+
lines.push(source.cssText);
|
|
246
731
|
lines.push("");
|
|
247
732
|
}
|
|
248
733
|
const content = lines.join("\n");
|
|
@@ -324,25 +809,90 @@ ${newVars}
|
|
|
324
809
|
r.ensureMeta();
|
|
325
810
|
}
|
|
326
811
|
}
|
|
327
|
-
function
|
|
328
|
-
|
|
329
|
-
|
|
812
|
+
function ensureManifestsDir() {
|
|
813
|
+
if (!fs2.existsSync(MANIFESTS_DIR) && fs2.existsSync(LEGACY_PRESETS_DIR)) {
|
|
814
|
+
fs2.renameSync(LEGACY_PRESETS_DIR, MANIFESTS_DIR);
|
|
815
|
+
const legacyProd = path2.join(MANIFESTS_DIR, "_production.json");
|
|
816
|
+
if (fs2.existsSync(legacyProd)) fs2.unlinkSync(legacyProd);
|
|
817
|
+
}
|
|
818
|
+
manifestsResource.ensureDir();
|
|
819
|
+
const defaultPath = path2.join(MANIFESTS_DIR, "default.json");
|
|
330
820
|
if (!fs2.existsSync(defaultPath)) {
|
|
331
821
|
const componentConfigs = {};
|
|
332
822
|
for (const comp of listComponentNames()) {
|
|
333
823
|
componentConfigs[comp] = componentResource(comp).getActiveName();
|
|
334
824
|
}
|
|
335
825
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
336
|
-
const
|
|
337
|
-
name: "Default
|
|
826
|
+
const defaultManifest = {
|
|
827
|
+
name: "Default",
|
|
338
828
|
createdAt: now,
|
|
339
829
|
updatedAt: now,
|
|
340
830
|
theme: themesResource.getActiveName(),
|
|
341
831
|
componentConfigs
|
|
342
832
|
};
|
|
343
|
-
fs2.writeFileSync(defaultPath, JSON.stringify(
|
|
833
|
+
fs2.writeFileSync(defaultPath, JSON.stringify(defaultManifest, null, 2));
|
|
344
834
|
}
|
|
345
|
-
|
|
835
|
+
if (!fs2.existsSync(manifestsResource.activePath)) {
|
|
836
|
+
fs2.writeFileSync(
|
|
837
|
+
manifestsResource.activePath,
|
|
838
|
+
JSON.stringify({ activeFile: "default" })
|
|
839
|
+
);
|
|
840
|
+
} else {
|
|
841
|
+
const activeName = manifestsResource.getActiveName();
|
|
842
|
+
if (!fs2.existsSync(manifestsResource.filePath(activeName))) {
|
|
843
|
+
manifestsResource.setActiveName("default");
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const stragglerProd = path2.join(MANIFESTS_DIR, "_production.json");
|
|
847
|
+
if (fs2.existsSync(stragglerProd)) fs2.unlinkSync(stragglerProd);
|
|
848
|
+
}
|
|
849
|
+
function patchActiveManifest(field, comp, fileName) {
|
|
850
|
+
const activeFile = manifestsResource.getActiveName();
|
|
851
|
+
if (activeFile === "default") return false;
|
|
852
|
+
const manifestPath = manifestsResource.filePath(activeFile);
|
|
853
|
+
if (!fs2.existsSync(manifestPath)) return false;
|
|
854
|
+
let manifest;
|
|
855
|
+
try {
|
|
856
|
+
manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
|
|
857
|
+
} catch {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
if (field === "theme") {
|
|
861
|
+
manifest.theme = fileName;
|
|
862
|
+
} else if (field === "component" && comp) {
|
|
863
|
+
manifest.componentConfigs = manifest.componentConfigs ?? {};
|
|
864
|
+
manifest.componentConfigs[comp] = fileName;
|
|
865
|
+
}
|
|
866
|
+
manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
867
|
+
fs2.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
function formatAliasGradient(v) {
|
|
871
|
+
const stopColor = (s) => {
|
|
872
|
+
const base = s.color.startsWith("--") ? `var(${s.color})` : s.color;
|
|
873
|
+
const opacity = s.opacity ?? 100;
|
|
874
|
+
return opacity >= 100 ? base : `color-mix(in srgb, ${base} ${opacity}%, transparent)`;
|
|
875
|
+
};
|
|
876
|
+
if (v.type === "none") return "transparent";
|
|
877
|
+
if (v.type === "solid") {
|
|
878
|
+
const first = v.stops[0];
|
|
879
|
+
if (!first) return "transparent";
|
|
880
|
+
return stopColor(first);
|
|
881
|
+
}
|
|
882
|
+
const stops = v.stops.map((s) => `${stopColor(s)} ${s.position}%`).join(", ");
|
|
883
|
+
if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
|
|
884
|
+
const radial = v.radius && v.radius > 0 ? `circle ${v.radius}px at center` : "circle";
|
|
885
|
+
return `radial-gradient(${radial}, ${stops})`;
|
|
886
|
+
}
|
|
887
|
+
function aliasValueToCss(v) {
|
|
888
|
+
if (typeof v === "string") return v.startsWith("--") ? `var(${v})` : v;
|
|
889
|
+
return formatAliasGradient(v.value);
|
|
890
|
+
}
|
|
891
|
+
function aliasValuesEqual(a, b) {
|
|
892
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
893
|
+
if (typeof a === "string" && typeof b === "string") return a === b;
|
|
894
|
+
if (typeof a !== typeof b) return false;
|
|
895
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
346
896
|
}
|
|
347
897
|
function readComponentConfig(comp, name) {
|
|
348
898
|
const filePath = componentResource(comp).filePath(name);
|
|
@@ -353,70 +903,25 @@ ${newVars}
|
|
|
353
903
|
return null;
|
|
354
904
|
}
|
|
355
905
|
}
|
|
356
|
-
const COMPONENT_OVERRIDES_START = "/* component-aliases:start */";
|
|
357
|
-
const COMPONENT_OVERRIDES_END = "/* component-aliases:end */";
|
|
358
906
|
function syncComponentsToCss() {
|
|
359
|
-
|
|
360
|
-
if (!fs2.existsSync(COMPONENT_CONFIGS_DIR)) return;
|
|
361
|
-
const lines = [];
|
|
362
|
-
const components = listComponentNames();
|
|
363
|
-
for (const comp of components) {
|
|
364
|
-
const prod = componentResource(comp).getProductionName();
|
|
365
|
-
if (prod === "default") continue;
|
|
366
|
-
const prodCfg = readComponentConfig(comp, prod);
|
|
367
|
-
const defaultCfg = readComponentConfig(comp, "default");
|
|
368
|
-
if (!prodCfg || !defaultCfg) continue;
|
|
369
|
-
const overrides = [];
|
|
370
|
-
for (const [varName, semanticName] of Object.entries(prodCfg.aliases ?? {})) {
|
|
371
|
-
if ((defaultCfg.aliases ?? {})[varName] !== semanticName) {
|
|
372
|
-
overrides.push([varName, semanticName]);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
if (overrides.length === 0) continue;
|
|
376
|
-
lines.push(` /* ${comp} (${prod}) */`);
|
|
377
|
-
for (const [varName, semanticName] of overrides) {
|
|
378
|
-
lines.push(` ${varName}: var(${semanticName});`);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
const block = lines.length > 0 ? `
|
|
382
|
-
|
|
383
|
-
${COMPONENT_OVERRIDES_START}
|
|
384
|
-
:root:root {
|
|
385
|
-
${lines.join("\n")}
|
|
386
|
-
}
|
|
387
|
-
${COMPONENT_OVERRIDES_END}
|
|
388
|
-
` : "";
|
|
389
|
-
let cssContent = fs2.readFileSync(CSS_PATH, "utf-8");
|
|
390
|
-
const startIdx = cssContent.indexOf(COMPONENT_OVERRIDES_START);
|
|
391
|
-
const endIdx = cssContent.indexOf(COMPONENT_OVERRIDES_END);
|
|
392
|
-
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
393
|
-
let stripStart = startIdx;
|
|
394
|
-
while (stripStart > 0 && cssContent[stripStart - 1] === "\n") stripStart--;
|
|
395
|
-
const after = cssContent.slice(endIdx + COMPONENT_OVERRIDES_END.length);
|
|
396
|
-
cssContent = cssContent.slice(0, stripStart) + (block || "\n") + after.replace(/^\n+/, "");
|
|
397
|
-
} else if (block) {
|
|
398
|
-
cssContent = cssContent.replace(/\n*$/, "") + block;
|
|
399
|
-
}
|
|
400
|
-
fs2.writeFileSync(CSS_PATH, cssContent);
|
|
401
|
-
console.log(
|
|
402
|
-
`[syncComponentsToCss] Wrote ${lines.filter((l) => !l.trim().startsWith("/*")).length} alias override(s) to tokens.css`
|
|
403
|
-
);
|
|
907
|
+
regenerateTokensCss();
|
|
404
908
|
}
|
|
405
909
|
const escapedBase = API_BASE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
406
910
|
const THEMES_ROUTE = `${API_BASE}/themes`;
|
|
407
911
|
const THEMES_ACTIVE_ROUTE = `${API_BASE}/themes/active`;
|
|
408
912
|
const THEMES_PRODUCTION_ROUTE = `${API_BASE}/themes/production`;
|
|
409
913
|
const COMPONENT_CONFIGS_ROUTE = `${API_BASE}/component-configs`;
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
const PRESETS_PRODUCTION_ROUTE = `${API_BASE}/presets/production`;
|
|
914
|
+
const MANIFESTS_ROUTE = `${API_BASE}/manifests`;
|
|
915
|
+
const MANIFESTS_ACTIVE_ROUTE = `${API_BASE}/manifests/active`;
|
|
413
916
|
const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
|
|
414
917
|
const COMP_LIST_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)$`);
|
|
415
918
|
const COMP_ACTIVE_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/active$`);
|
|
416
919
|
const COMP_PRODUCTION_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/production$`);
|
|
417
920
|
const COMP_BY_NAME_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/([a-z0-9\\-_]+)$`);
|
|
418
|
-
const
|
|
419
|
-
const
|
|
921
|
+
const MANIFEST_APPLY_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)/apply$`);
|
|
922
|
+
const MANIFEST_EXPORT_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)/export$`);
|
|
923
|
+
const MANIFEST_IMPORT_ROUTE = `${API_BASE}/manifests/import`;
|
|
924
|
+
const MANIFEST_BY_NAME_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)$`);
|
|
420
925
|
async function handleListThemes(_ctx) {
|
|
421
926
|
const activeFile = themesResource.getActiveName();
|
|
422
927
|
const files = fs2.readdirSync(THEMES_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
|
|
@@ -439,7 +944,7 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
439
944
|
jsonResponse(res, 404, { error: "Active theme not found" });
|
|
440
945
|
return;
|
|
441
946
|
}
|
|
442
|
-
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
947
|
+
const data = normalizeTheme(JSON.parse(fs2.readFileSync(filePath, "utf-8")));
|
|
443
948
|
data._fileName = activeFile;
|
|
444
949
|
jsonResponse(res, 200, data);
|
|
445
950
|
}
|
|
@@ -460,7 +965,7 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
460
965
|
jsonResponse(res, 200, { fileName: prodFile, name: prodFile, cssVariables: {} });
|
|
461
966
|
return;
|
|
462
967
|
}
|
|
463
|
-
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
968
|
+
const data = normalizeTheme(JSON.parse(fs2.readFileSync(filePath, "utf-8")));
|
|
464
969
|
jsonResponse(res, 200, {
|
|
465
970
|
fileName: prodFile,
|
|
466
971
|
name: data.name || prodFile,
|
|
@@ -475,10 +980,18 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
475
980
|
jsonResponse(res, 404, { error: "Theme not found" });
|
|
476
981
|
return;
|
|
477
982
|
}
|
|
983
|
+
if (manifestsResource.getActiveName() === "default") {
|
|
984
|
+
jsonResponse(res, 409, {
|
|
985
|
+
error: "Active manifest is protected. Save As first.",
|
|
986
|
+
code: "ACTIVE_IS_PROTECTED"
|
|
987
|
+
});
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
478
990
|
themesResource.setProductionName(fileName);
|
|
479
991
|
syncTokensToCss(fileName);
|
|
480
992
|
syncFontsToCss(fileName);
|
|
481
993
|
syncComponentsToCss();
|
|
994
|
+
patchActiveManifest("theme", null, fileName);
|
|
482
995
|
const data = JSON.parse(fs2.readFileSync(themesResource.filePath(fileName), "utf-8"));
|
|
483
996
|
jsonResponse(res, 200, {
|
|
484
997
|
ok: true,
|
|
@@ -495,7 +1008,7 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
495
1008
|
jsonResponse(res, 404, { error: "Not found" });
|
|
496
1009
|
return;
|
|
497
1010
|
}
|
|
498
|
-
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
1011
|
+
const data = normalizeTheme(JSON.parse(fs2.readFileSync(filePath, "utf-8")));
|
|
499
1012
|
data._fileName = fileName;
|
|
500
1013
|
jsonResponse(res, 200, data);
|
|
501
1014
|
return;
|
|
@@ -525,6 +1038,13 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
525
1038
|
jsonResponse(res, 403, { error: "Cannot delete the default theme" });
|
|
526
1039
|
return;
|
|
527
1040
|
}
|
|
1041
|
+
if (themesResource.getProductionName() === fileName) {
|
|
1042
|
+
jsonResponse(res, 403, {
|
|
1043
|
+
error: "Cannot delete the production theme. Adopt a different theme first.",
|
|
1044
|
+
code: "PRODUCTION_THEME"
|
|
1045
|
+
});
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
528
1048
|
if (fs2.existsSync(filePath)) {
|
|
529
1049
|
fs2.unlinkSync(filePath);
|
|
530
1050
|
if (themesResource.getActiveName() === fileName) {
|
|
@@ -592,9 +1112,17 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
592
1112
|
jsonResponse(res, 404, { error: "Config not found" });
|
|
593
1113
|
return;
|
|
594
1114
|
}
|
|
1115
|
+
if (manifestsResource.getActiveName() === "default") {
|
|
1116
|
+
jsonResponse(res, 409, {
|
|
1117
|
+
error: "Active manifest is protected. Save As first.",
|
|
1118
|
+
code: "ACTIVE_IS_PROTECTED"
|
|
1119
|
+
});
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
595
1122
|
r.ensureDir();
|
|
596
1123
|
r.setProductionName(fileName);
|
|
597
1124
|
syncComponentsToCss();
|
|
1125
|
+
patchActiveManifest("component", comp, fileName);
|
|
598
1126
|
const cfg = readComponentConfig(comp, fileName);
|
|
599
1127
|
jsonResponse(res, 200, {
|
|
600
1128
|
ok: true,
|
|
@@ -684,112 +1212,46 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
684
1212
|
});
|
|
685
1213
|
jsonResponse(res, 200, { component: comp, files, activeFile, productionFile });
|
|
686
1214
|
}
|
|
687
|
-
async function
|
|
688
|
-
const activeFile =
|
|
689
|
-
const files = fs2.readdirSync(
|
|
690
|
-
const filePath = path2.join(
|
|
1215
|
+
async function handleListManifests({ res }) {
|
|
1216
|
+
const activeFile = manifestsResource.getActiveName();
|
|
1217
|
+
const files = fs2.readdirSync(MANIFESTS_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
|
|
1218
|
+
const filePath = path2.join(MANIFESTS_DIR, f);
|
|
691
1219
|
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
692
1220
|
const fileName = f.replace(".json", "");
|
|
693
1221
|
return {
|
|
694
1222
|
name: data.name || fileName,
|
|
695
1223
|
fileName,
|
|
696
1224
|
updatedAt: data.updatedAt || "",
|
|
697
|
-
isActive: fileName === activeFile
|
|
1225
|
+
isActive: fileName === activeFile,
|
|
1226
|
+
isProtected: fileName === "default"
|
|
698
1227
|
};
|
|
699
1228
|
});
|
|
700
1229
|
jsonResponse(res, 200, { files, activeFile });
|
|
701
1230
|
}
|
|
702
|
-
async function
|
|
703
|
-
const activeFile =
|
|
704
|
-
const filePath =
|
|
1231
|
+
async function handleGetActiveManifest({ res }) {
|
|
1232
|
+
const activeFile = manifestsResource.getActiveName();
|
|
1233
|
+
const filePath = manifestsResource.filePath(activeFile);
|
|
705
1234
|
if (!fs2.existsSync(filePath)) {
|
|
706
|
-
jsonResponse(res, 404, { error: "Active
|
|
1235
|
+
jsonResponse(res, 404, { error: "Active manifest not found" });
|
|
707
1236
|
return;
|
|
708
1237
|
}
|
|
709
1238
|
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
710
1239
|
data._fileName = activeFile;
|
|
711
1240
|
jsonResponse(res, 200, data);
|
|
712
1241
|
}
|
|
713
|
-
async function
|
|
1242
|
+
async function handleSetActiveManifest({ req, res }) {
|
|
714
1243
|
const body = JSON.parse(await readBody(req));
|
|
715
1244
|
const fileName = sanitizeFileName(body.name || "default");
|
|
716
|
-
if (!fs2.existsSync(
|
|
717
|
-
jsonResponse(res, 404, { error: "
|
|
1245
|
+
if (!fs2.existsSync(manifestsResource.filePath(fileName))) {
|
|
1246
|
+
jsonResponse(res, 404, { error: "Manifest not found" });
|
|
718
1247
|
return;
|
|
719
1248
|
}
|
|
720
|
-
|
|
1249
|
+
manifestsResource.setActiveName(fileName);
|
|
721
1250
|
jsonResponse(res, 200, { ok: true, activeFile: fileName });
|
|
722
1251
|
}
|
|
723
|
-
async function
|
|
724
|
-
const prodFile = presetsResource.getProductionName();
|
|
725
|
-
const filePath = presetsResource.filePath(prodFile);
|
|
726
|
-
if (!fs2.existsSync(filePath)) {
|
|
727
|
-
jsonResponse(res, 200, {
|
|
728
|
-
fileName: prodFile,
|
|
729
|
-
name: prodFile,
|
|
730
|
-
theme: "default",
|
|
731
|
-
componentConfigs: {},
|
|
732
|
-
updatedAt: ""
|
|
733
|
-
});
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
737
|
-
jsonResponse(res, 200, {
|
|
738
|
-
fileName: prodFile,
|
|
739
|
-
name: data.name || prodFile,
|
|
740
|
-
theme: data.theme || "default",
|
|
741
|
-
componentConfigs: data.componentConfigs || {},
|
|
742
|
-
updatedAt: data.updatedAt || ""
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
async function handleSetProductionPreset({ req, res }) {
|
|
746
|
-
const body = JSON.parse(await readBody(req));
|
|
747
|
-
const fileName = sanitizeFileName(body.name || "default");
|
|
748
|
-
const presetPath = presetsResource.filePath(fileName);
|
|
749
|
-
if (!fs2.existsSync(presetPath)) {
|
|
750
|
-
jsonResponse(res, 404, { error: "Preset not found" });
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
const preset = JSON.parse(fs2.readFileSync(presetPath, "utf-8"));
|
|
754
|
-
const themeName = sanitizeFileName(preset.theme || "default");
|
|
755
|
-
if (!fs2.existsSync(themesResource.filePath(themeName))) {
|
|
756
|
-
jsonResponse(res, 422, { error: `Preset references missing theme: ${themeName}` });
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
const knownComponents = new Set(listComponentNames());
|
|
760
|
-
const componentConfigs = preset.componentConfigs ?? {};
|
|
761
|
-
const apply = [];
|
|
762
|
-
for (const [comp, configFile] of Object.entries(componentConfigs)) {
|
|
763
|
-
if (!knownComponents.has(comp)) continue;
|
|
764
|
-
const sanitized = sanitizeFileName(String(configFile) || "default");
|
|
765
|
-
if (!fs2.existsSync(componentResource(comp).filePath(sanitized))) {
|
|
766
|
-
jsonResponse(res, 422, {
|
|
767
|
-
error: `Preset references missing config: ${comp}/${sanitized}`
|
|
768
|
-
});
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
apply.push([comp, sanitized]);
|
|
772
|
-
}
|
|
773
|
-
themesResource.setProductionName(themeName);
|
|
774
|
-
for (const [comp, configFile] of apply) {
|
|
775
|
-
componentResource(comp).setProductionName(configFile);
|
|
776
|
-
}
|
|
777
|
-
presetsResource.setProductionName(fileName);
|
|
778
|
-
syncTokensToCss(themeName);
|
|
779
|
-
syncFontsToCss(themeName);
|
|
780
|
-
syncComponentsToCss();
|
|
781
|
-
jsonResponse(res, 200, {
|
|
782
|
-
ok: true,
|
|
783
|
-
fileName,
|
|
784
|
-
name: preset.name || fileName,
|
|
785
|
-
theme: themeName,
|
|
786
|
-
componentConfigs: Object.fromEntries(apply),
|
|
787
|
-
updatedAt: preset.updatedAt || ""
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
async function handlePresetByName({ params, req, res }) {
|
|
1252
|
+
async function handleManifestByName({ params, req, res }) {
|
|
791
1253
|
const [fileName] = params;
|
|
792
|
-
const filePath =
|
|
1254
|
+
const filePath = manifestsResource.filePath(fileName);
|
|
793
1255
|
if (req.method === "GET") {
|
|
794
1256
|
if (!fs2.existsSync(filePath)) {
|
|
795
1257
|
jsonResponse(res, 404, { error: "Not found" });
|
|
@@ -802,7 +1264,7 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
802
1264
|
}
|
|
803
1265
|
if (req.method === "PUT") {
|
|
804
1266
|
if (fileName === "default") {
|
|
805
|
-
jsonResponse(res, 403, { error: "Cannot overwrite the default
|
|
1267
|
+
jsonResponse(res, 403, { error: "Cannot overwrite the default manifest" });
|
|
806
1268
|
return;
|
|
807
1269
|
}
|
|
808
1270
|
const body = JSON.parse(await readBody(req));
|
|
@@ -821,35 +1283,35 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
821
1283
|
}
|
|
822
1284
|
if (req.method === "DELETE") {
|
|
823
1285
|
if (fileName === "default") {
|
|
824
|
-
jsonResponse(res, 403, { error: "Cannot delete the default
|
|
1286
|
+
jsonResponse(res, 403, { error: "Cannot delete the default manifest" });
|
|
825
1287
|
return;
|
|
826
1288
|
}
|
|
827
1289
|
if (fs2.existsSync(filePath)) {
|
|
828
1290
|
fs2.unlinkSync(filePath);
|
|
829
|
-
if (
|
|
830
|
-
|
|
1291
|
+
if (manifestsResource.getActiveName() === fileName) {
|
|
1292
|
+
manifestsResource.setActiveName("default");
|
|
831
1293
|
}
|
|
832
1294
|
}
|
|
833
1295
|
jsonResponse(res, 200, { ok: true });
|
|
834
1296
|
return;
|
|
835
1297
|
}
|
|
836
1298
|
}
|
|
837
|
-
async function
|
|
1299
|
+
async function handleApplyManifest({ params, res }) {
|
|
838
1300
|
const [fileName] = params;
|
|
839
|
-
const
|
|
840
|
-
if (!fs2.existsSync(
|
|
841
|
-
jsonResponse(res, 404, { error: "
|
|
1301
|
+
const manifestPath = manifestsResource.filePath(fileName);
|
|
1302
|
+
if (!fs2.existsSync(manifestPath)) {
|
|
1303
|
+
jsonResponse(res, 404, { error: "Manifest not found" });
|
|
842
1304
|
return;
|
|
843
1305
|
}
|
|
844
|
-
const
|
|
845
|
-
const themeName = sanitizeFileName(
|
|
1306
|
+
const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
|
|
1307
|
+
const themeName = sanitizeFileName(manifest.theme || "default");
|
|
846
1308
|
const themePath = themesResource.filePath(themeName);
|
|
847
1309
|
if (!fs2.existsSync(themePath)) {
|
|
848
|
-
jsonResponse(res, 422, { error: `
|
|
1310
|
+
jsonResponse(res, 422, { error: `Manifest references missing theme: ${themeName}` });
|
|
849
1311
|
return;
|
|
850
1312
|
}
|
|
851
1313
|
const knownComponents = new Set(listComponentNames());
|
|
852
|
-
const componentConfigs =
|
|
1314
|
+
const componentConfigs = manifest.componentConfigs ?? {};
|
|
853
1315
|
const resolvedConfigs = {};
|
|
854
1316
|
const apply = [];
|
|
855
1317
|
for (const [comp, configFile] of Object.entries(componentConfigs)) {
|
|
@@ -859,18 +1321,22 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
859
1321
|
const cfgPath = r.filePath(sanitized);
|
|
860
1322
|
if (!fs2.existsSync(cfgPath)) {
|
|
861
1323
|
jsonResponse(res, 422, {
|
|
862
|
-
error: `
|
|
1324
|
+
error: `Manifest references missing config: ${comp}/${sanitized}`
|
|
863
1325
|
});
|
|
864
1326
|
return;
|
|
865
1327
|
}
|
|
866
1328
|
apply.push([comp, sanitized]);
|
|
867
1329
|
}
|
|
868
1330
|
themesResource.setActiveName(themeName);
|
|
869
|
-
|
|
1331
|
+
themesResource.setProductionName(themeName);
|
|
1332
|
+
syncTokensToCss(themeName);
|
|
1333
|
+
syncFontsToCss(themeName);
|
|
1334
|
+
const themeData = normalizeTheme(JSON.parse(fs2.readFileSync(themePath, "utf-8")));
|
|
870
1335
|
themeData._fileName = themeName;
|
|
871
1336
|
for (const [comp, configFile] of apply) {
|
|
872
1337
|
const r = componentResource(comp);
|
|
873
1338
|
r.setActiveName(configFile);
|
|
1339
|
+
r.setProductionName(configFile);
|
|
874
1340
|
const cfg = readComponentConfig(comp, configFile);
|
|
875
1341
|
if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: configFile };
|
|
876
1342
|
}
|
|
@@ -880,14 +1346,166 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
880
1346
|
const cfg = readComponentConfig(comp, activeName);
|
|
881
1347
|
if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: activeName };
|
|
882
1348
|
}
|
|
883
|
-
|
|
1349
|
+
syncComponentsToCss();
|
|
1350
|
+
manifestsResource.setActiveName(fileName);
|
|
884
1351
|
jsonResponse(res, 200, {
|
|
885
1352
|
ok: true,
|
|
886
|
-
|
|
1353
|
+
manifest: { ...manifest, _fileName: fileName },
|
|
887
1354
|
theme: themeData,
|
|
888
1355
|
componentConfigs: resolvedConfigs
|
|
889
1356
|
});
|
|
890
1357
|
}
|
|
1358
|
+
async function handleExportManifest({ params, res }) {
|
|
1359
|
+
const [fileName] = params;
|
|
1360
|
+
const manifestPath = manifestsResource.filePath(fileName);
|
|
1361
|
+
if (!fs2.existsSync(manifestPath)) {
|
|
1362
|
+
jsonResponse(res, 404, { error: "Manifest not found" });
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
|
|
1366
|
+
const themeName = sanitizeFileName(manifest.theme || "default");
|
|
1367
|
+
const themePath = themesResource.filePath(themeName);
|
|
1368
|
+
if (!fs2.existsSync(themePath)) {
|
|
1369
|
+
jsonResponse(res, 422, { error: `Manifest references missing theme: ${themeName}` });
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const theme = JSON.parse(fs2.readFileSync(themePath, "utf-8"));
|
|
1373
|
+
const knownComponents = new Set(listComponentNames());
|
|
1374
|
+
const componentConfigs = {};
|
|
1375
|
+
for (const [comp, configFile] of Object.entries(manifest.componentConfigs ?? {})) {
|
|
1376
|
+
if (!knownComponents.has(comp)) continue;
|
|
1377
|
+
const sanitized = sanitizeFileName(String(configFile) || "default");
|
|
1378
|
+
if (sanitized === "default") continue;
|
|
1379
|
+
const cfgPath = componentResource(comp).filePath(sanitized);
|
|
1380
|
+
if (!fs2.existsSync(cfgPath)) {
|
|
1381
|
+
jsonResponse(res, 422, {
|
|
1382
|
+
error: `Manifest references missing config: ${comp}/${sanitized}`
|
|
1383
|
+
});
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
const cfg = JSON.parse(fs2.readFileSync(cfgPath, "utf-8"));
|
|
1387
|
+
componentConfigs[`${comp}/${sanitized}`] = cfg;
|
|
1388
|
+
}
|
|
1389
|
+
const bundle = {
|
|
1390
|
+
kind: "manifest-bundle",
|
|
1391
|
+
schemaVersion: 1,
|
|
1392
|
+
liveTokensVersion: PKG_VERSION,
|
|
1393
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1394
|
+
manifest,
|
|
1395
|
+
theme,
|
|
1396
|
+
componentConfigs
|
|
1397
|
+
};
|
|
1398
|
+
res.statusCode = 200;
|
|
1399
|
+
res.setHeader("Content-Type", "application/json");
|
|
1400
|
+
res.setHeader(
|
|
1401
|
+
"Content-Disposition",
|
|
1402
|
+
`attachment; filename="${fileName}.bundle.json"`
|
|
1403
|
+
);
|
|
1404
|
+
res.end(JSON.stringify(bundle, null, 2));
|
|
1405
|
+
}
|
|
1406
|
+
function nextAvailableName2(resourceFilePath, baseName) {
|
|
1407
|
+
return nextAvailableName(
|
|
1408
|
+
(n) => fs2.existsSync(resourceFilePath(n)),
|
|
1409
|
+
sanitizeFileName(baseName)
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
async function handleImportManifest({ req, res }) {
|
|
1413
|
+
let bundle;
|
|
1414
|
+
try {
|
|
1415
|
+
bundle = JSON.parse(await readBody(req));
|
|
1416
|
+
} catch {
|
|
1417
|
+
jsonResponse(res, 400, { error: "Body is not valid JSON" });
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
if (!bundle || bundle.kind !== "manifest-bundle") {
|
|
1421
|
+
jsonResponse(res, 400, {
|
|
1422
|
+
error: "Not a manifest bundle (kind discriminator missing or wrong)"
|
|
1423
|
+
});
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
if (bundle.schemaVersion !== 1) {
|
|
1427
|
+
jsonResponse(res, 400, {
|
|
1428
|
+
error: `Unsupported bundle schemaVersion: ${bundle.schemaVersion}`
|
|
1429
|
+
});
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
if (!bundle.manifest || !bundle.theme || !bundle.componentConfigs) {
|
|
1433
|
+
jsonResponse(res, 400, { error: "Bundle missing manifest / theme / componentConfigs" });
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
const renames = {};
|
|
1437
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1438
|
+
const originalThemeName = sanitizeFileName(bundle.manifest.theme || "default");
|
|
1439
|
+
const finalThemeName = nextAvailableName2(
|
|
1440
|
+
(n) => themesResource.filePath(n),
|
|
1441
|
+
originalThemeName
|
|
1442
|
+
);
|
|
1443
|
+
if (finalThemeName !== originalThemeName) {
|
|
1444
|
+
renames[`theme:${originalThemeName}`] = finalThemeName;
|
|
1445
|
+
}
|
|
1446
|
+
const themeBody = { ...bundle.theme };
|
|
1447
|
+
themeBody.updatedAt = now;
|
|
1448
|
+
if (!themeBody.createdAt) themeBody.createdAt = now;
|
|
1449
|
+
themesResource.ensureDir();
|
|
1450
|
+
fs2.writeFileSync(themesResource.filePath(finalThemeName), JSON.stringify(themeBody, null, 2));
|
|
1451
|
+
const knownComponents = new Set(listComponentNames());
|
|
1452
|
+
const componentRenames = {};
|
|
1453
|
+
for (const [key, cfgValue] of Object.entries(bundle.componentConfigs)) {
|
|
1454
|
+
const [comp, originalName] = key.split("/");
|
|
1455
|
+
if (!comp || !originalName) continue;
|
|
1456
|
+
if (!knownComponents.has(comp)) continue;
|
|
1457
|
+
const r = componentResource(comp);
|
|
1458
|
+
const finalName = nextAvailableName2(
|
|
1459
|
+
(n) => r.filePath(n),
|
|
1460
|
+
originalName
|
|
1461
|
+
);
|
|
1462
|
+
if (finalName !== originalName) {
|
|
1463
|
+
renames[`componentConfig:${comp}/${originalName}`] = finalName;
|
|
1464
|
+
}
|
|
1465
|
+
componentRenames[`${comp}/${originalName}`] = finalName;
|
|
1466
|
+
const cfgBody = { ...cfgValue };
|
|
1467
|
+
cfgBody.component = comp;
|
|
1468
|
+
cfgBody.name = finalName;
|
|
1469
|
+
cfgBody.updatedAt = now;
|
|
1470
|
+
if (!cfgBody.createdAt) cfgBody.createdAt = now;
|
|
1471
|
+
r.ensureDir();
|
|
1472
|
+
fs2.writeFileSync(r.filePath(finalName), JSON.stringify(cfgBody, null, 2));
|
|
1473
|
+
}
|
|
1474
|
+
const rewrittenManifest = {
|
|
1475
|
+
...bundle.manifest,
|
|
1476
|
+
theme: finalThemeName,
|
|
1477
|
+
componentConfigs: {}
|
|
1478
|
+
};
|
|
1479
|
+
for (const [comp, configName] of Object.entries(bundle.manifest.componentConfigs ?? {})) {
|
|
1480
|
+
const original = String(configName);
|
|
1481
|
+
if (original === "default") {
|
|
1482
|
+
rewrittenManifest.componentConfigs[comp] = "default";
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
const finalName = componentRenames[`${comp}/${original}`] ?? original;
|
|
1486
|
+
rewrittenManifest.componentConfigs[comp] = finalName;
|
|
1487
|
+
}
|
|
1488
|
+
rewrittenManifest.updatedAt = now;
|
|
1489
|
+
if (!rewrittenManifest.createdAt) rewrittenManifest.createdAt = now;
|
|
1490
|
+
const originalManifestName = sanitizeFileName(bundle.manifest.name || "imported");
|
|
1491
|
+
const finalManifestName = nextAvailableName2(
|
|
1492
|
+
(n) => manifestsResource.filePath(n),
|
|
1493
|
+
originalManifestName
|
|
1494
|
+
);
|
|
1495
|
+
if (finalManifestName !== originalManifestName) {
|
|
1496
|
+
renames[`manifest:${originalManifestName}`] = finalManifestName;
|
|
1497
|
+
}
|
|
1498
|
+
manifestsResource.ensureDir();
|
|
1499
|
+
fs2.writeFileSync(
|
|
1500
|
+
manifestsResource.filePath(finalManifestName),
|
|
1501
|
+
JSON.stringify(rewrittenManifest, null, 2)
|
|
1502
|
+
);
|
|
1503
|
+
jsonResponse(res, 200, {
|
|
1504
|
+
ok: true,
|
|
1505
|
+
manifest: finalManifestName,
|
|
1506
|
+
renames
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
891
1509
|
function methodNotAllowed({ res }) {
|
|
892
1510
|
jsonResponse(res, 405, { error: "Method not allowed" });
|
|
893
1511
|
}
|
|
@@ -921,21 +1539,29 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
921
1539
|
{ method: "GET", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
922
1540
|
{ method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
923
1541
|
{ method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
|
|
924
|
-
//
|
|
925
|
-
{ method: "GET", pattern:
|
|
926
|
-
{ method: "GET", pattern:
|
|
927
|
-
{ method: "PUT", pattern:
|
|
928
|
-
|
|
929
|
-
{ method: "
|
|
930
|
-
|
|
931
|
-
{ method: "
|
|
932
|
-
{ method: "
|
|
933
|
-
|
|
934
|
-
{ method: "
|
|
935
|
-
|
|
936
|
-
{ method: "GET", pattern:
|
|
937
|
-
{ method: "
|
|
938
|
-
|
|
1542
|
+
// Manifests — list / active are exact strings, must run before regexes
|
|
1543
|
+
{ method: "GET", pattern: MANIFESTS_ROUTE, handler: handleListManifests },
|
|
1544
|
+
{ method: "GET", pattern: MANIFESTS_ACTIVE_ROUTE, handler: handleGetActiveManifest },
|
|
1545
|
+
{ method: "PUT", pattern: MANIFESTS_ACTIVE_ROUTE, handler: handleSetActiveManifest },
|
|
1546
|
+
// Manifests — exact import route runs before :name regexes
|
|
1547
|
+
{ method: "POST", pattern: MANIFEST_IMPORT_ROUTE, handler: handleImportManifest },
|
|
1548
|
+
{ method: "PUT", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
|
|
1549
|
+
{ method: "GET", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
|
|
1550
|
+
{ method: "DELETE", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
|
|
1551
|
+
// Manifests — :name/apply (more specific than :name)
|
|
1552
|
+
{ method: "PUT", pattern: MANIFEST_APPLY_REGEX, handler: handleApplyManifest },
|
|
1553
|
+
{ method: "POST", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
|
|
1554
|
+
{ method: "GET", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
|
|
1555
|
+
{ method: "DELETE", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
|
|
1556
|
+
// Manifests — :name/export (more specific than :name)
|
|
1557
|
+
{ method: "GET", pattern: MANIFEST_EXPORT_REGEX, handler: handleExportManifest },
|
|
1558
|
+
{ method: "PUT", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
|
|
1559
|
+
{ method: "POST", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
|
|
1560
|
+
{ method: "DELETE", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
|
|
1561
|
+
// Manifests — :name CRUD (broadest manifest route, runs last)
|
|
1562
|
+
{ method: "GET", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName },
|
|
1563
|
+
{ method: "PUT", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName },
|
|
1564
|
+
{ method: "DELETE", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName }
|
|
939
1565
|
];
|
|
940
1566
|
return {
|
|
941
1567
|
name: "theme-file-api",
|
|
@@ -950,7 +1576,8 @@ ${COMPONENT_OVERRIDES_END}
|
|
|
950
1576
|
configureServer(server) {
|
|
951
1577
|
ensureThemesDir();
|
|
952
1578
|
ensureComponentConfigsDir();
|
|
953
|
-
|
|
1579
|
+
ensureManifestsDir();
|
|
1580
|
+
regenerateTokensCss();
|
|
954
1581
|
server.middlewares.use(async (req, res, next) => {
|
|
955
1582
|
const handled = await dispatch(req, res, routes);
|
|
956
1583
|
if (!handled) next();
|