@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/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileManager.svelte
RENAMED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
6
|
import { onMount, onDestroy } from 'svelte';
|
|
7
|
+
import UIInfoPopover from '../../ui/UIInfoPopover.svelte';
|
|
7
8
|
import { get } from 'svelte/store';
|
|
8
|
-
import type { ComponentConfig, ComponentConfigMeta } from '../../
|
|
9
|
+
import type { AliasDiskValue, ComponentConfig, ComponentConfigMeta } from '../../core/themes/themeTypes';
|
|
9
10
|
import { componentSourceFile } from './componentSources';
|
|
10
11
|
import {
|
|
11
12
|
loadComponentConfig,
|
|
@@ -14,19 +15,25 @@
|
|
|
14
15
|
setActiveComponentFile,
|
|
15
16
|
setComponentProductionFile,
|
|
16
17
|
type ComponentProductionInfo,
|
|
17
|
-
} from '../../
|
|
18
|
+
} from '../../core/components/componentConfigService';
|
|
18
19
|
import {
|
|
19
20
|
editorState,
|
|
20
21
|
componentDirty,
|
|
21
22
|
loadComponentActive,
|
|
22
23
|
markComponentSaved,
|
|
23
|
-
} from '../../
|
|
24
|
-
import { bumpProductionRevision } from '../../
|
|
25
|
-
import {
|
|
26
|
-
import type {
|
|
27
|
-
import {
|
|
24
|
+
} from '../../core/store/editorStore';
|
|
25
|
+
import { bumpProductionRevision } from '../../core/productionPulse';
|
|
26
|
+
import { listManifests, saveAsManifest } from '../../core/manifests/manifestService';
|
|
27
|
+
import type { ManifestMeta } from '../../core/themes/themeTypes';
|
|
28
|
+
import { CURRENT_COMPONENT_SCHEMA_VERSION } from '../../core/themes/migrations';
|
|
29
|
+
import type { CssVarRef } from '../../core/store/editorTypes';
|
|
30
|
+
import { safeFetch } from '../../core/storage/storage';
|
|
31
|
+
import { flashStatus } from '../../core/flashStatus';
|
|
28
32
|
import ComponentFileMenu from './ComponentFileMenu.svelte';
|
|
29
33
|
import SaveAsDialog from './SaveAsDialog.svelte';
|
|
34
|
+
import FilePill from '../../ui/FilePill.svelte';
|
|
35
|
+
import UIPillButton from '../../ui/UIPillButton.svelte';
|
|
36
|
+
import UISquareButton from '../../ui/UISquareButton.svelte';
|
|
30
37
|
|
|
31
38
|
|
|
32
39
|
|
|
@@ -50,74 +57,29 @@
|
|
|
50
57
|
|
|
51
58
|
type SaveStatus = 'idle' | 'saving' | 'saved' | 'error';
|
|
52
59
|
let saveStatus: SaveStatus = 'idle';
|
|
60
|
+
const setSaveStatus = (s: SaveStatus) => (saveStatus = s);
|
|
53
61
|
|
|
54
|
-
/** Show a transient saveStatus (saved/error) and revert to idle after 2s.
|
|
55
|
-
Centralises the timing so all flash sites stay in sync. */
|
|
56
|
-
function flashStatus(state: Exclude<SaveStatus, 'idle'>) {
|
|
57
|
-
saveStatus = state;
|
|
58
|
-
setTimeout(() => (saveStatus = 'idle'), 2000);
|
|
59
|
-
}
|
|
60
62
|
let files: ComponentConfigMeta[] = $state([]);
|
|
61
63
|
let activeFileName = $state('default');
|
|
62
64
|
let currentDisplayName = $state('Default');
|
|
63
65
|
let saveAsDialog = $state(false);
|
|
66
|
+
// Title + description re-set per trigger so the auto-prompts (Save on
|
|
67
|
+
// default, Adopt on default+dirty) explain why they appeared, while the
|
|
68
|
+
// manual Save As button stays terse.
|
|
69
|
+
let saveAsTitle = $state('Save Component As');
|
|
70
|
+
let saveAsDescription = $state('');
|
|
64
71
|
|
|
65
72
|
let productionInfo = $state<ComponentProductionInfo | null>(null);
|
|
66
73
|
type ProductionStatus = 'idle' | 'updating' | 'done' | 'error';
|
|
67
74
|
let productionUpdateStatus: ProductionStatus = $state('idle');
|
|
68
75
|
let adoptFeedback = $state('');
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
let
|
|
72
|
-
let
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
/** Anchor the fixed-position popover centered below the info button.
|
|
76
|
-
Uses position: fixed so it escapes the sticky header's stacking
|
|
77
|
-
context (which was letting the side panels paint over it). */
|
|
78
|
-
function positionInfoPopover(): void {
|
|
79
|
-
const btn = infoBtnEl;
|
|
80
|
-
const pop = infoPopoverEl;
|
|
81
|
-
if (!btn || !pop) return;
|
|
82
|
-
const br = btn.getBoundingClientRect();
|
|
83
|
-
const pr = pop.getBoundingClientRect();
|
|
84
|
-
const margin = 8;
|
|
85
|
-
let left = br.left + br.width / 2 - pr.width / 2;
|
|
86
|
-
const vw = window.innerWidth;
|
|
87
|
-
if (left < margin) left = margin;
|
|
88
|
-
if (left + pr.width > vw - margin) left = vw - margin - pr.width;
|
|
89
|
-
pop.style.left = `${left}px`;
|
|
90
|
-
pop.style.top = `${br.bottom + margin}px`;
|
|
91
|
-
infoPopoverReady = true;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
$effect(() => {
|
|
95
|
-
if (!infoOpen) {
|
|
96
|
-
infoPopoverReady = false;
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
// Two rAFs: first so Svelte mounts the popover and the bind: ref is set,
|
|
100
|
-
// second so its rendered width is measurable before we anchor it.
|
|
101
|
-
let raf1 = requestAnimationFrame(() => {
|
|
102
|
-
raf1 = requestAnimationFrame(positionInfoPopover);
|
|
103
|
-
});
|
|
104
|
-
window.addEventListener('scroll', positionInfoPopover, true);
|
|
105
|
-
window.addEventListener('resize', positionInfoPopover);
|
|
106
|
-
return () => {
|
|
107
|
-
cancelAnimationFrame(raf1);
|
|
108
|
-
window.removeEventListener('scroll', positionInfoPopover, true);
|
|
109
|
-
window.removeEventListener('resize', positionInfoPopover);
|
|
110
|
-
};
|
|
111
|
-
});
|
|
77
|
+
// Manifest SaveAs prompt for the "Adopt while default manifest is active" case.
|
|
78
|
+
let manifestSaveAsDialog = $state(false);
|
|
79
|
+
let manifests: ManifestMeta[] = $state([]);
|
|
80
|
+
let retryAdoptAfterManifestSave = false;
|
|
112
81
|
|
|
113
|
-
|
|
114
|
-
function flashProductionStatus(state: Exclude<ProductionStatus, 'idle'>) {
|
|
115
|
-
productionUpdateStatus = state;
|
|
116
|
-
setTimeout(() => {
|
|
117
|
-
productionUpdateStatus = 'idle';
|
|
118
|
-
adoptFeedback = '';
|
|
119
|
-
}, 2000);
|
|
120
|
-
}
|
|
82
|
+
const setProductionStatus = (s: ProductionStatus) => (productionUpdateStatus = s);
|
|
121
83
|
|
|
122
84
|
let compDirty = $derived($componentDirty[component] ?? false);
|
|
123
85
|
let isApplied = $derived(!!productionInfo && productionInfo.fileName === activeFileName && !compDirty);
|
|
@@ -150,40 +112,30 @@
|
|
|
150
112
|
await refreshFiles();
|
|
151
113
|
await refreshProduction();
|
|
152
114
|
window.addEventListener('keydown', handleKeydown);
|
|
153
|
-
document.addEventListener('mousedown', handleDocumentMousedown, true);
|
|
154
115
|
});
|
|
155
116
|
|
|
156
117
|
onDestroy(() => {
|
|
157
118
|
window.removeEventListener('keydown', handleKeydown);
|
|
158
|
-
document.removeEventListener('mousedown', handleDocumentMousedown, true);
|
|
159
119
|
});
|
|
160
120
|
|
|
161
121
|
function handleKeydown(e: KeyboardEvent) {
|
|
162
122
|
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
|
163
123
|
e.preventDefault();
|
|
164
124
|
handleSave();
|
|
165
|
-
} else if (e.key === 'Escape' && infoOpen) {
|
|
166
|
-
infoOpen = false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function handleDocumentMousedown(e: MouseEvent) {
|
|
171
|
-
if (!infoOpen) return;
|
|
172
|
-
const target = e.target as Element | null;
|
|
173
|
-
if (target && !target.closest('.cfm-info-btn, .cfm-info-popover')) {
|
|
174
|
-
infoOpen = false;
|
|
175
125
|
}
|
|
176
126
|
}
|
|
177
127
|
|
|
178
|
-
function
|
|
179
|
-
|
|
128
|
+
function refToDiskValue(ref: CssVarRef): AliasDiskValue {
|
|
129
|
+
if (ref.kind === 'token') return ref.name;
|
|
130
|
+
if (ref.kind === 'literal') return ref.value;
|
|
131
|
+
return { kind: 'gradient', value: ref.value };
|
|
180
132
|
}
|
|
181
133
|
|
|
182
|
-
function currentAliases(): Record<string,
|
|
134
|
+
function currentAliases(): Record<string, AliasDiskValue> {
|
|
183
135
|
const slice = get(editorState).components[component];
|
|
184
136
|
if (!slice) return {};
|
|
185
|
-
const out: Record<string,
|
|
186
|
-
for (const [k, ref] of Object.entries(slice.aliases)) out[k] =
|
|
137
|
+
const out: Record<string, AliasDiskValue> = {};
|
|
138
|
+
for (const [k, ref] of Object.entries(slice.aliases)) out[k] = refToDiskValue(ref);
|
|
187
139
|
return out;
|
|
188
140
|
}
|
|
189
141
|
|
|
@@ -211,21 +163,24 @@
|
|
|
211
163
|
|
|
212
164
|
async function handleSave() {
|
|
213
165
|
if (activeFileName === 'default') {
|
|
214
|
-
// Default is regenerated from source
|
|
215
|
-
|
|
166
|
+
// Default is regenerated from source, so the first Save quietly opens
|
|
167
|
+
// Save As — same dialog as the Save As button, no scolding description.
|
|
168
|
+
openSaveAs();
|
|
216
169
|
return;
|
|
217
170
|
}
|
|
218
171
|
saveStatus = 'saving';
|
|
219
172
|
try {
|
|
220
173
|
await persist(activeFileName, currentDisplayName);
|
|
221
|
-
flashStatus('saved');
|
|
174
|
+
flashStatus(setSaveStatus, 'saved');
|
|
222
175
|
await refreshFiles();
|
|
223
176
|
} catch {
|
|
224
|
-
flashStatus('error');
|
|
177
|
+
flashStatus(setSaveStatus, 'error');
|
|
225
178
|
}
|
|
226
179
|
}
|
|
227
180
|
|
|
228
181
|
function openSaveAs() {
|
|
182
|
+
saveAsTitle = 'Save Component As';
|
|
183
|
+
saveAsDescription = '';
|
|
229
184
|
saveAsDialog = true;
|
|
230
185
|
}
|
|
231
186
|
|
|
@@ -234,10 +189,10 @@
|
|
|
234
189
|
saveStatus = 'saving';
|
|
235
190
|
try {
|
|
236
191
|
await persist(fileName, displayName);
|
|
237
|
-
flashStatus('saved');
|
|
192
|
+
flashStatus(setSaveStatus, 'saved');
|
|
238
193
|
await refreshFiles();
|
|
239
194
|
} catch {
|
|
240
|
-
flashStatus('error');
|
|
195
|
+
flashStatus(setSaveStatus, 'error');
|
|
241
196
|
}
|
|
242
197
|
}
|
|
243
198
|
|
|
@@ -262,11 +217,16 @@
|
|
|
262
217
|
// Multi-step service flow (delete + reload-default-on-active-removal).
|
|
263
218
|
// Silent by design — see handleLoad.
|
|
264
219
|
try {
|
|
220
|
+
// Capture before refreshFiles() reads the server's reverted active back
|
|
221
|
+
// into local state — otherwise the "was this the active file?" check
|
|
222
|
+
// below sees the post-revert value and skips the reload.
|
|
223
|
+
const wasActive = file.fileName === activeFileName;
|
|
265
224
|
await deleteComponentConfig(component, file.fileName);
|
|
266
225
|
await refreshFiles();
|
|
267
226
|
await refreshProduction();
|
|
268
|
-
if (
|
|
269
|
-
// Server reverts active to default; reload default aliases into the store
|
|
227
|
+
if (wasActive) {
|
|
228
|
+
// Server reverts active to default; reload default aliases into the store
|
|
229
|
+
// so the deleted file's CSS vars are replaced by default's.
|
|
270
230
|
const defaultCfg = await loadComponentConfig(component, 'default');
|
|
271
231
|
loadComponentActive(component, 'default', defaultCfg.aliases, defaultCfg.config, defaultCfg.schemaVersion ?? 0);
|
|
272
232
|
activeFileName = 'default';
|
|
@@ -283,6 +243,9 @@
|
|
|
283
243
|
// regenerated from source and can't be overwritten, so route to Save As
|
|
284
244
|
// and bail; the user can re-trigger Adopt after the new file is saved.
|
|
285
245
|
if (compDirty && activeFileName === 'default') {
|
|
246
|
+
saveAsTitle = 'Save Component As';
|
|
247
|
+
saveAsDescription =
|
|
248
|
+
'Adopting pushes this component to production. The default config is read-only, so save your edits to a new file first.';
|
|
286
249
|
saveAsDialog = true;
|
|
287
250
|
return;
|
|
288
251
|
}
|
|
@@ -300,10 +263,38 @@
|
|
|
300
263
|
adoptFeedback = wasDirty
|
|
301
264
|
? `Saved "${adoptingName}" and adopted`
|
|
302
265
|
: `Adopted "${adoptingName}"`;
|
|
303
|
-
|
|
304
|
-
} catch {
|
|
266
|
+
flashStatus(setProductionStatus, 'done', { onIdle: () => (adoptFeedback = '') });
|
|
267
|
+
} catch (err) {
|
|
268
|
+
const e = err as Error & { code?: string };
|
|
269
|
+
if (e.code === 'ACTIVE_IS_PROTECTED') {
|
|
270
|
+
adoptFeedback = '';
|
|
271
|
+
productionUpdateStatus = 'idle';
|
|
272
|
+
retryAdoptAfterManifestSave = true;
|
|
273
|
+
try {
|
|
274
|
+
manifests = await listManifests();
|
|
275
|
+
} catch {
|
|
276
|
+
manifests = [];
|
|
277
|
+
}
|
|
278
|
+
manifestSaveAsDialog = true;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
305
281
|
adoptFeedback = '';
|
|
306
|
-
|
|
282
|
+
flashStatus(setProductionStatus, 'error', { onIdle: () => (adoptFeedback = '') });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function onManifestSaveAs(detail: { displayName: string; fileName: string }) {
|
|
287
|
+
manifestSaveAsDialog = false;
|
|
288
|
+
try {
|
|
289
|
+
await saveAsManifest(detail.fileName, detail.displayName);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
window.alert(`Failed to create manifest: ${(err as Error).message}`);
|
|
292
|
+
retryAdoptAfterManifestSave = false;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (retryAdoptAfterManifestSave) {
|
|
296
|
+
retryAdoptAfterManifestSave = false;
|
|
297
|
+
await handleUpdateProduction();
|
|
307
298
|
}
|
|
308
299
|
}
|
|
309
300
|
|
|
@@ -324,14 +315,11 @@
|
|
|
324
315
|
<h2 class="cfm-title">{title}</h2>
|
|
325
316
|
{/if}
|
|
326
317
|
{#if sourceFile && projectRoot}
|
|
327
|
-
<
|
|
328
|
-
|
|
318
|
+
<UIPillButton
|
|
319
|
+
icon="fa-code"
|
|
329
320
|
href="vscode://file/{projectRoot}/{sourceFile}"
|
|
330
321
|
title="Open {sourceFile} in VS Code"
|
|
331
|
-
>
|
|
332
|
-
<i class="fas fa-code"></i>
|
|
333
|
-
<span>Show component source</span>
|
|
334
|
-
</a>
|
|
322
|
+
>Show component source</UIPillButton>
|
|
335
323
|
{/if}
|
|
336
324
|
</div>
|
|
337
325
|
|
|
@@ -345,18 +333,19 @@
|
|
|
345
333
|
<span>{compDirty ? 'unsaved' : isApplied ? 'live' : 'saved'}</span>
|
|
346
334
|
</span>
|
|
347
335
|
</div>
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
336
|
+
<FilePill
|
|
337
|
+
name={currentDisplayName}
|
|
338
|
+
isProtected={activeFileName === 'default'}
|
|
339
|
+
dirty={compDirty}
|
|
340
|
+
applied={isApplied}
|
|
341
|
+
protectedTitle="Protected system config"
|
|
352
342
|
title={compDirty
|
|
353
343
|
? 'Unsaved changes'
|
|
354
344
|
: isApplied
|
|
355
345
|
? 'Active config is applied to production'
|
|
356
346
|
: ''}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
</span>
|
|
347
|
+
style="flex: 0 1 11.25rem; min-width: 0; max-width: 11.25rem;"
|
|
348
|
+
/>
|
|
360
349
|
<div class="cfm-actions">
|
|
361
350
|
<ComponentFileMenu
|
|
362
351
|
{component}
|
|
@@ -369,15 +358,12 @@
|
|
|
369
358
|
ondelete={handleDelete}
|
|
370
359
|
/>
|
|
371
360
|
{#if resetVariables}
|
|
372
|
-
<
|
|
373
|
-
|
|
361
|
+
<UISquareButton
|
|
362
|
+
icon="fa-rotate-left"
|
|
374
363
|
onclick={handleReset}
|
|
375
364
|
disabled={!resetDirty}
|
|
376
365
|
title="Revert unsaved changes to {currentDisplayName}"
|
|
377
|
-
>
|
|
378
|
-
<i class="fas fa-rotate-left"></i>
|
|
379
|
-
<span>Reset</span>
|
|
380
|
-
</button>
|
|
366
|
+
>Reset</UISquareButton>
|
|
381
367
|
{/if}
|
|
382
368
|
</div>
|
|
383
369
|
</div>
|
|
@@ -395,15 +381,22 @@
|
|
|
395
381
|
<span>live</span>
|
|
396
382
|
</span>
|
|
397
383
|
</div>
|
|
398
|
-
<
|
|
399
|
-
|
|
400
|
-
|
|
384
|
+
<FilePill
|
|
385
|
+
name={productionInfo?.name ?? '—'}
|
|
386
|
+
isProtected={productionInfo?.fileName === 'default'}
|
|
387
|
+
protectedTitle="Protected system config"
|
|
388
|
+
style="flex: 0 1 11.25rem; min-width: 0; max-width: 11.25rem;"
|
|
389
|
+
/>
|
|
401
390
|
<div class="cfm-actions">
|
|
402
|
-
<
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
391
|
+
<UISquareButton
|
|
392
|
+
variant="success"
|
|
393
|
+
icon={productionUpdateStatus === 'updating'
|
|
394
|
+
? 'fa-spinner'
|
|
395
|
+
: productionUpdateStatus === 'done'
|
|
396
|
+
? 'fa-check'
|
|
397
|
+
: productionUpdateStatus === 'error'
|
|
398
|
+
? 'fa-xmark'
|
|
399
|
+
: 'fa-arrow-down'}
|
|
407
400
|
onclick={handleUpdateProduction}
|
|
408
401
|
disabled={productionUpdateStatus === 'updating' || !productionInfo || (productionInfo.fileName === activeFileName && !compDirty)}
|
|
409
402
|
title={!productionInfo
|
|
@@ -416,55 +409,21 @@
|
|
|
416
409
|
? `Save "${currentDisplayName}" and adopt`
|
|
417
410
|
: `Adopt "${currentDisplayName}" from editor`}
|
|
418
411
|
>
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
</
|
|
434
|
-
{#if infoOpen}
|
|
435
|
-
<div
|
|
436
|
-
class="cfm-info-popover"
|
|
437
|
-
class:ready={infoPopoverReady}
|
|
438
|
-
role="dialog"
|
|
439
|
-
aria-label="About Save and Adopt"
|
|
440
|
-
bind:this={infoPopoverEl}
|
|
441
|
-
>
|
|
442
|
-
<header class="cfm-info-header">
|
|
443
|
-
<span class="cfm-info-title">Component Configuration</span>
|
|
444
|
-
<button
|
|
445
|
-
type="button"
|
|
446
|
-
class="cfm-info-close"
|
|
447
|
-
aria-label="Close"
|
|
448
|
-
onclick={() => (infoOpen = false)}
|
|
449
|
-
>
|
|
450
|
-
<i class="fas fa-xmark"></i>
|
|
451
|
-
</button>
|
|
452
|
-
</header>
|
|
453
|
-
<div class="cfm-info-body">
|
|
454
|
-
<p>
|
|
455
|
-
Editor and Prod both use a saved file. When they share the
|
|
456
|
-
<em>same</em> file, <strong>Saved changes</strong> go to into production
|
|
457
|
-
immediately. They are sharing the configuration.
|
|
458
|
-
</p>
|
|
459
|
-
<p>
|
|
460
|
-
To experiment without changing production,<strong>Save As</strong> a new file first.
|
|
461
|
-
</p>
|
|
462
|
-
<p>
|
|
463
|
-
When ready, click <strong>Adopt</strong> to use the new file on prod.
|
|
464
|
-
</p>
|
|
465
|
-
</div>
|
|
466
|
-
</div>
|
|
467
|
-
{/if}
|
|
412
|
+
{#if productionUpdateStatus === 'idle'}Adopt{:else if productionUpdateStatus === 'updating'}Adopting{:else if productionUpdateStatus === 'done'}Adopted{:else}Error{/if}
|
|
413
|
+
</UISquareButton>
|
|
414
|
+
<UIInfoPopover title="Component Configuration" ariaLabel="About Save and Adopt">
|
|
415
|
+
<p>
|
|
416
|
+
Editor and Prod both use a saved file. When they share the
|
|
417
|
+
<em>same</em> file, <strong>Saved changes</strong> go to into production
|
|
418
|
+
immediately. They are sharing the configuration.
|
|
419
|
+
</p>
|
|
420
|
+
<p>
|
|
421
|
+
To experiment without changing production,<strong>Save As</strong> a new file first.
|
|
422
|
+
</p>
|
|
423
|
+
<p>
|
|
424
|
+
When ready, click <strong>Adopt</strong> to use the new file on prod.
|
|
425
|
+
</p>
|
|
426
|
+
</UIInfoPopover>
|
|
468
427
|
{#if adoptFeedback}
|
|
469
428
|
<span class="cfm-feedback" aria-live="polite">{adoptFeedback}</span>
|
|
470
429
|
{/if}
|
|
@@ -477,33 +436,47 @@
|
|
|
477
436
|
bind:show={saveAsDialog}
|
|
478
437
|
{currentDisplayName}
|
|
479
438
|
{files}
|
|
439
|
+
currentFileName={activeFileName}
|
|
440
|
+
reservedDisplayNames={files.filter((f) => f.fileName === 'default').map((f) => f.name)}
|
|
441
|
+
title={saveAsTitle}
|
|
442
|
+
description={saveAsDescription}
|
|
443
|
+
placeholder="Component config name…"
|
|
444
|
+
branchFromDefaultName={`my-${(title || 'component').toLowerCase().trim().replace(/\s+/g, '-')}`}
|
|
480
445
|
onsave={confirmSaveAs}
|
|
481
446
|
/>
|
|
482
447
|
|
|
448
|
+
<SaveAsDialog
|
|
449
|
+
bind:show={manifestSaveAsDialog}
|
|
450
|
+
currentDisplayName="my-manifest"
|
|
451
|
+
files={manifests}
|
|
452
|
+
reservedDisplayNames={manifests.filter((m) => m.fileName === 'default').map((m) => m.name)}
|
|
453
|
+
title="Save Manifest As"
|
|
454
|
+
placeholder="Manifest name…"
|
|
455
|
+
description="Adopting a component change updates the active manifest, The default manifest is locked. Name a new manifest for the site."
|
|
456
|
+
reservedNameMessage='That name is reserved for the protected default manifest.'
|
|
457
|
+
onsave={onManifestSaveAs}
|
|
458
|
+
/>
|
|
459
|
+
|
|
483
460
|
<style>
|
|
484
461
|
.cfm-bar {
|
|
485
462
|
--cfm-applied: #5aa85e;
|
|
486
|
-
--cfm-rail-neutral: var(--ui-border
|
|
463
|
+
--cfm-rail-neutral: var(--ui-border);
|
|
487
464
|
--cfm-rail-dirty: var(--ui-highlight);
|
|
488
465
|
--cfm-rail-applied: var(--cfm-applied);
|
|
489
466
|
|
|
490
|
-
position: sticky;
|
|
491
|
-
top: 0;
|
|
492
|
-
z-index: 5;
|
|
493
467
|
display: flex;
|
|
494
468
|
flex-direction: column;
|
|
495
469
|
gap: var(--ui-space-8);
|
|
496
|
-
padding: var(--ui-space-12);
|
|
497
|
-
background: var(--ui-surface-low);
|
|
498
|
-
border: 1px solid var(--ui-border-faint);
|
|
499
|
-
border-radius: var(--ui-radius-lg);
|
|
500
470
|
}
|
|
501
471
|
|
|
502
472
|
.cfm-title-row {
|
|
503
473
|
display: flex;
|
|
504
474
|
align-items: center;
|
|
505
|
-
|
|
475
|
+
justify-content: space-between;
|
|
476
|
+
gap: var(--ui-space-16);
|
|
506
477
|
flex-wrap: wrap;
|
|
478
|
+
max-width: 34rem;
|
|
479
|
+
margin-bottom: var(--ui-space-16);
|
|
507
480
|
}
|
|
508
481
|
|
|
509
482
|
.cfm-title {
|
|
@@ -512,37 +485,15 @@
|
|
|
512
485
|
font-size: var(--ui-font-size-3xl);
|
|
513
486
|
font-weight: var(--ui-font-weight-semibold);
|
|
514
487
|
color: var(--ui-text-primary);
|
|
515
|
-
letter-spacing: -0.015em;
|
|
516
488
|
line-height: 1.1;
|
|
517
489
|
}
|
|
518
490
|
|
|
519
|
-
.source-link {
|
|
520
|
-
display: inline-flex;
|
|
521
|
-
align-items: center;
|
|
522
|
-
gap: var(--ui-space-6);
|
|
523
|
-
margin-left: 2.5rem;
|
|
524
|
-
height: 26px;
|
|
525
|
-
padding: 0 14px;
|
|
526
|
-
font-size: var(--ui-font-size-xs);
|
|
527
|
-
font-weight: 500;
|
|
528
|
-
color: var(--ui-text-secondary);
|
|
529
|
-
text-decoration: none;
|
|
530
|
-
border: 1px solid var(--ui-border-default);
|
|
531
|
-
border-radius: 999px;
|
|
532
|
-
transition: all var(--ui-transition-fast);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
.source-link:hover {
|
|
536
|
-
color: var(--ui-text-primary);
|
|
537
|
-
border-color: var(--ui-border-strong);
|
|
538
|
-
background: var(--ui-hover);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
491
|
/* ── two-row pipeline ─────────────────────────────────────── */
|
|
542
492
|
.cfm-rows {
|
|
543
493
|
display: flex;
|
|
544
494
|
flex-direction: column;
|
|
545
495
|
gap: var(--ui-space-6);
|
|
496
|
+
max-width: 34rem;
|
|
546
497
|
}
|
|
547
498
|
|
|
548
499
|
.cfm-row {
|
|
@@ -553,7 +504,7 @@
|
|
|
553
504
|
gap: var(--ui-space-10);
|
|
554
505
|
padding: var(--ui-space-8) var(--ui-space-10) var(--ui-space-8) var(--ui-space-16);
|
|
555
506
|
background: var(--ui-surface-lower);
|
|
556
|
-
border: 1px solid var(--ui-border-
|
|
507
|
+
border: 1px solid var(--ui-border-low);
|
|
557
508
|
border-radius: var(--ui-radius-md);
|
|
558
509
|
}
|
|
559
510
|
|
|
@@ -586,7 +537,6 @@
|
|
|
586
537
|
font-size: var(--ui-font-size-xs);
|
|
587
538
|
font-weight: var(--ui-font-weight-semibold);
|
|
588
539
|
text-transform: uppercase;
|
|
589
|
-
letter-spacing: 0.08em;
|
|
590
540
|
color: var(--ui-text-secondary);
|
|
591
541
|
line-height: 1.1;
|
|
592
542
|
}
|
|
@@ -596,7 +546,6 @@
|
|
|
596
546
|
align-items: center;
|
|
597
547
|
gap: var(--ui-space-4);
|
|
598
548
|
font-size: 0.75rem;
|
|
599
|
-
letter-spacing: 0.02em;
|
|
600
549
|
color: var(--ui-text-muted);
|
|
601
550
|
line-height: 1;
|
|
602
551
|
}
|
|
@@ -626,41 +575,6 @@
|
|
|
626
575
|
opacity: 1;
|
|
627
576
|
}
|
|
628
577
|
|
|
629
|
-
/* filename pill — fixed width so editor and production pills
|
|
630
|
-
stack with their left and right edges perfectly aligned. */
|
|
631
|
-
.cfm-pill {
|
|
632
|
-
display: inline-flex;
|
|
633
|
-
align-items: center;
|
|
634
|
-
gap: var(--ui-space-6);
|
|
635
|
-
flex: 0 0 7.5rem;
|
|
636
|
-
width: 7.5rem;
|
|
637
|
-
padding: var(--ui-space-6) var(--ui-space-10);
|
|
638
|
-
background: var(--ui-surface-lowest);
|
|
639
|
-
border: 1px solid var(--ui-border-subtle);
|
|
640
|
-
border-radius: var(--ui-radius-md);
|
|
641
|
-
transition: border-color var(--ui-transition-fast), box-shadow var(--ui-transition-fast);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
.cfm-pill.dirty {
|
|
645
|
-
border-color: color-mix(in srgb, var(--ui-highlight) 60%, var(--ui-border-subtle));
|
|
646
|
-
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-highlight) 35%, transparent);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
.cfm-pill.applied {
|
|
650
|
-
border-color: color-mix(in srgb, var(--cfm-applied) 50%, var(--ui-border-subtle));
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
.cfm-pill-name {
|
|
654
|
-
flex: 1;
|
|
655
|
-
min-width: 0;
|
|
656
|
-
font-size: var(--ui-font-size-md);
|
|
657
|
-
font-weight: var(--ui-font-weight-semibold);
|
|
658
|
-
color: var(--ui-text-primary);
|
|
659
|
-
white-space: nowrap;
|
|
660
|
-
overflow: hidden;
|
|
661
|
-
text-overflow: ellipsis;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
578
|
/* actions cluster — sits directly next to the filename pill so the
|
|
665
579
|
buttons stay near the input and don't drift under the open editor panel.
|
|
666
580
|
position: relative anchors the info popover below the cluster. */
|
|
@@ -714,195 +628,8 @@
|
|
|
714
628
|
line-height: 1;
|
|
715
629
|
}
|
|
716
630
|
|
|
717
|
-
/* buttons */
|
|
718
|
-
.cfm-btn {
|
|
719
|
-
display: inline-flex;
|
|
720
|
-
align-items: center;
|
|
721
|
-
gap: var(--ui-space-6);
|
|
722
|
-
padding: var(--ui-space-6) var(--ui-space-12);
|
|
723
|
-
background: var(--ui-surface);
|
|
724
|
-
border: 1px solid var(--ui-border-subtle);
|
|
725
|
-
border-radius: var(--ui-radius-md);
|
|
726
|
-
color: var(--ui-text-secondary);
|
|
727
|
-
font-size: var(--ui-font-size-md);
|
|
728
|
-
font-weight: var(--ui-font-weight-medium);
|
|
729
|
-
cursor: pointer;
|
|
730
|
-
transition: all var(--ui-transition-fast);
|
|
731
|
-
white-space: nowrap;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
.cfm-btn i {
|
|
735
|
-
width: 1rem;
|
|
736
|
-
text-align: center;
|
|
737
|
-
font-size: 0.85em;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
.cfm-btn:hover:not(:disabled) {
|
|
741
|
-
background: var(--ui-surface-high);
|
|
742
|
-
color: var(--ui-text-primary);
|
|
743
|
-
border-color: var(--ui-border-default);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
.cfm-btn:disabled {
|
|
747
|
-
opacity: 0.45;
|
|
748
|
-
cursor: not-allowed;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
.cfm-btn.primary {
|
|
752
|
-
background: color-mix(in srgb, var(--cfm-applied) 18%, var(--ui-surface-high));
|
|
753
|
-
border-color: color-mix(in srgb, var(--cfm-applied) 45%, var(--ui-border-medium));
|
|
754
|
-
color: var(--ui-text-primary);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
.cfm-btn.primary:hover:not(:disabled) {
|
|
758
|
-
background: color-mix(in srgb, var(--cfm-applied) 30%, var(--ui-surface-higher));
|
|
759
|
-
border-color: color-mix(in srgb, var(--cfm-applied) 70%, var(--ui-border-strong));
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
.cfm-btn.primary:disabled {
|
|
763
|
-
background: var(--ui-surface);
|
|
764
|
-
border-color: var(--ui-border-subtle);
|
|
765
|
-
color: var(--ui-text-muted);
|
|
766
|
-
opacity: 1;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
.cfm-btn.primary.saving i { animation: cfm-spin 1s linear infinite; }
|
|
770
|
-
.cfm-btn.primary.saved {
|
|
771
|
-
background: color-mix(in srgb, var(--cfm-applied) 30%, var(--ui-surface-high));
|
|
772
|
-
color: var(--cfm-applied);
|
|
773
|
-
}
|
|
774
|
-
.cfm-btn.primary.error {
|
|
775
|
-
color: var(--ui-text-muted);
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
@keyframes cfm-spin {
|
|
779
|
-
from { transform: rotate(0deg); }
|
|
780
|
-
to { transform: rotate(360deg); }
|
|
781
|
-
}
|
|
782
|
-
|
|
783
631
|
@keyframes cfm-pulse {
|
|
784
632
|
0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ui-highlight) 22%, transparent); }
|
|
785
633
|
50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--ui-highlight) 10%, transparent); }
|
|
786
634
|
}
|
|
787
|
-
|
|
788
|
-
/* info button — naked icon, no chrome. The icon itself carries the
|
|
789
|
-
affordance; hover/active simply brighten its color. */
|
|
790
|
-
.cfm-info-btn {
|
|
791
|
-
display: inline-flex;
|
|
792
|
-
align-items: center;
|
|
793
|
-
justify-content: center;
|
|
794
|
-
padding: var(--ui-space-2) var(--ui-space-4);
|
|
795
|
-
background: transparent;
|
|
796
|
-
border: 0;
|
|
797
|
-
color: var(--ui-text-tertiary);
|
|
798
|
-
font-size: 1.15rem;
|
|
799
|
-
line-height: 1;
|
|
800
|
-
cursor: pointer;
|
|
801
|
-
transition: color var(--ui-transition-fast);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
.cfm-info-btn:hover,
|
|
805
|
-
.cfm-info-btn[aria-expanded='true'] {
|
|
806
|
-
color: var(--ui-text-primary);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
.cfm-info-popover {
|
|
810
|
-
/* Fixed positioning escapes the sticky header's stacking context,
|
|
811
|
-
so the popover paints over the side panels. JS in this file
|
|
812
|
-
anchors it centered below the info button. */
|
|
813
|
-
position: fixed;
|
|
814
|
-
top: 0;
|
|
815
|
-
left: 0;
|
|
816
|
-
width: 22rem;
|
|
817
|
-
max-width: calc(100vw - var(--ui-space-24));
|
|
818
|
-
padding: 0;
|
|
819
|
-
background: var(--ui-surface-higher);
|
|
820
|
-
border: 1px solid var(--ui-border-medium);
|
|
821
|
-
border-radius: var(--ui-radius-lg);
|
|
822
|
-
box-shadow: var(--ui-shadow-lg);
|
|
823
|
-
z-index: 1000;
|
|
824
|
-
color: var(--ui-text-secondary);
|
|
825
|
-
font-family: var(--ui-font-family, system-ui, sans-serif);
|
|
826
|
-
overflow: hidden;
|
|
827
|
-
visibility: hidden;
|
|
828
|
-
animation: cfm-info-in 140ms ease-out;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
.cfm-info-popover.ready {
|
|
832
|
-
visibility: visible;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
.cfm-info-header {
|
|
836
|
-
display: flex;
|
|
837
|
-
align-items: center;
|
|
838
|
-
justify-content: space-between;
|
|
839
|
-
gap: var(--ui-space-8);
|
|
840
|
-
padding: var(--ui-space-10) var(--ui-space-12) var(--ui-space-10) var(--ui-space-16);
|
|
841
|
-
border-bottom: 1px solid var(--ui-border-subtle);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
.cfm-info-title {
|
|
845
|
-
color: var(--ui-text-primary);
|
|
846
|
-
font-size: var(--ui-font-size-sm);
|
|
847
|
-
font-weight: var(--ui-font-weight-semibold);
|
|
848
|
-
letter-spacing: -0.01em;
|
|
849
|
-
line-height: 1.2;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
.cfm-info-close {
|
|
853
|
-
display: inline-flex;
|
|
854
|
-
align-items: center;
|
|
855
|
-
justify-content: center;
|
|
856
|
-
width: var(--ui-space-24);
|
|
857
|
-
height: var(--ui-space-24);
|
|
858
|
-
padding: 0;
|
|
859
|
-
background: transparent;
|
|
860
|
-
border: 0;
|
|
861
|
-
border-radius: var(--ui-radius-sm);
|
|
862
|
-
color: var(--ui-text-tertiary);
|
|
863
|
-
font-size: var(--ui-font-size-xs);
|
|
864
|
-
line-height: 1;
|
|
865
|
-
cursor: pointer;
|
|
866
|
-
transition: color var(--ui-transition-fast), background var(--ui-transition-fast);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
.cfm-info-close:hover {
|
|
870
|
-
color: var(--ui-text-primary);
|
|
871
|
-
background: var(--ui-hover);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
.cfm-info-body {
|
|
875
|
-
padding: var(--ui-space-16);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
.cfm-info-popover p {
|
|
879
|
-
margin: 0 0 var(--ui-space-12) 0;
|
|
880
|
-
font-size: var(--ui-font-size-xs);
|
|
881
|
-
line-height: 1.55;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
.cfm-info-popover p:last-child {
|
|
885
|
-
margin-bottom: 0;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
.cfm-info-popover strong {
|
|
889
|
-
color: var(--ui-text-primary);
|
|
890
|
-
font-weight: var(--ui-font-weight-semibold);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
.cfm-info-popover em {
|
|
894
|
-
font-style: italic;
|
|
895
|
-
color: var(--ui-text-primary);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
@keyframes cfm-info-in {
|
|
899
|
-
from { opacity: 0; transform: translateY(-3px); }
|
|
900
|
-
to { opacity: 1; transform: translateY(0); }
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/* narrow viewports: hide button text, keep icons visible */
|
|
904
|
-
@media (max-width: 640px) {
|
|
905
|
-
.cfm-btn span { display: none; }
|
|
906
|
-
.cfm-btn { padding: var(--ui-space-6) var(--ui-space-10); }
|
|
907
|
-
}
|
|
908
635
|
</style>
|