@syntrologie/adapt-overlays 2.10.0 → 2.12.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.
Files changed (35) hide show
  1. package/dist/WorkflowWidget.js +3 -3
  2. package/dist/cdn.d.ts +2 -2
  3. package/dist/celebrations/__tests__/reduced-motion.test.d.ts +2 -0
  4. package/dist/celebrations/__tests__/reduced-motion.test.d.ts.map +1 -0
  5. package/dist/celebrations/__tests__/reduced-motion.test.js +97 -0
  6. package/dist/celebrations/engine.d.ts.map +1 -1
  7. package/dist/celebrations/engine.js +4 -0
  8. package/dist/editor.d.ts +6 -6
  9. package/dist/editor.d.ts.map +1 -1
  10. package/dist/editor.js +6 -412
  11. package/dist/overlay-editor-state.d.ts +41 -0
  12. package/dist/overlay-editor-state.d.ts.map +1 -0
  13. package/dist/overlay-editor-state.js +131 -0
  14. package/dist/overlay-editor-ui.d.ts +9 -0
  15. package/dist/overlay-editor-ui.d.ts.map +1 -0
  16. package/dist/overlay-editor-ui.js +306 -0
  17. package/dist/runtime.d.ts +2 -2
  18. package/dist/runtime.js +1 -1
  19. package/dist/tour-types.d.ts +1 -1
  20. package/dist/tour-types.d.ts.map +1 -1
  21. package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts.map +1 -1
  22. package/node_modules/@syntro/design-system/dist/tailwind-preset.js +4 -2
  23. package/node_modules/@syntro/design-system/dist/tokens/colors.css +1 -1
  24. package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts +2 -2
  25. package/node_modules/@syntro/design-system/dist/tokens/colors.js +1 -1
  26. package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts +54 -0
  27. package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts.map +1 -1
  28. package/node_modules/@syntro/design-system/dist/tokens/effects.js +44 -0
  29. package/node_modules/@syntro/design-system/package.json +2 -2
  30. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +2 -4
  31. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +2 -4
  32. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -1
  33. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.js +0 -1
  34. package/node_modules/@syntrologie/shared-editor-ui/package.json +11 -9
  35. package/package.json +12 -12
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Adaptive Overlays - Editor State & Helpers
3
+ *
4
+ * Pure logic: anchor resolution, types, section configuration,
5
+ * flatten/filter helpers, tour step utilities, and dismissal logic.
6
+ * No React or JSX — consumed by the UI module.
7
+ */
8
+ import type { OverlaysConfig } from './schema';
9
+ import type { TourStep } from './types';
10
+ /** Extract the CSS selector string from an anchorId object. */
11
+ export declare function resolveAnchorSelector(anchorId: unknown): string;
12
+ /** Extract the target route from an AnchorId object, ignoring wildcard '**'. */
13
+ export declare function resolveAnchorRoute(anchorId: unknown): string | null;
14
+ /** Save a pending highlight selector to sessionStorage (inlined to avoid cross-package import). */
15
+ export declare function savePendingHighlight(selector: string): void;
16
+ export type OverlaySection = 'tooltips' | 'highlights' | 'badges' | 'pulses' | 'modals';
17
+ export type SectionKey = OverlaySection | 'tours';
18
+ export declare function itemKey(section: SectionKey, index: number): string;
19
+ export declare const OVERLAY_SECTIONS: OverlaySection[];
20
+ export interface FlatItem {
21
+ key: string;
22
+ section: SectionKey;
23
+ index: number;
24
+ summary: string;
25
+ anchorId: string;
26
+ rawAnchorId: unknown;
27
+ isTour: boolean;
28
+ }
29
+ export declare function flattenItems(config: OverlaysConfig): FlatItem[];
30
+ export declare function filterConfig(config: OverlaysConfig, dismissedKeys: Set<string>): OverlaysConfig;
31
+ export declare function getStepIcon(step: TourStep): string;
32
+ export declare function getStepLabel(step: TourStep): string;
33
+ export interface DetectionEntry {
34
+ found: boolean;
35
+ element: HTMLElement | null;
36
+ }
37
+ export declare function parseOverlayItemKey(key: string): {
38
+ section: OverlaySection;
39
+ index: number;
40
+ };
41
+ //# sourceMappingURL=overlay-editor-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-editor-state.d.ts","sourceRoot":"","sources":["../src/overlay-editor-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAMxC,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAK/D;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASnE;AAED,mGAAmG;AACnG,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,QAMpD;AAMD,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACxF,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,OAAO,CAAC;AAElD,wBAAgB,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAElE;AAMD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAM5C,CAAC;AAMF,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,EAAE,CAkC/D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,cAAc,CAW/F;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CASlD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAQnD;AAMD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAG3F"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Adaptive Overlays - Editor State & Helpers
3
+ *
4
+ * Pure logic: anchor resolution, types, section configuration,
5
+ * flatten/filter helpers, tour step utilities, and dismissal logic.
6
+ * No React or JSX — consumed by the UI module.
7
+ */
8
+ import { summarizeOverlayItem } from './summarize';
9
+ // ============================================================================
10
+ // Anchor Helpers
11
+ // ============================================================================
12
+ /** Extract the CSS selector string from an anchorId object. */
13
+ export function resolveAnchorSelector(anchorId) {
14
+ if (!anchorId)
15
+ return '';
16
+ if (typeof anchorId === 'string')
17
+ return anchorId;
18
+ if (typeof anchorId === 'object')
19
+ return anchorId.selector ?? '';
20
+ return '';
21
+ }
22
+ /** Extract the target route from an AnchorId object, ignoring wildcard '**'. */
23
+ export function resolveAnchorRoute(anchorId) {
24
+ if (!anchorId || typeof anchorId !== 'object')
25
+ return null;
26
+ const route = anchorId.route;
27
+ if (typeof route === 'string' && route !== '**')
28
+ return route;
29
+ if (Array.isArray(route)) {
30
+ const first = route.find((r) => typeof r === 'string' && r !== '**');
31
+ return first ?? null;
32
+ }
33
+ return null;
34
+ }
35
+ /** Save a pending highlight selector to sessionStorage (inlined to avoid cross-package import). */
36
+ export function savePendingHighlight(selector) {
37
+ try {
38
+ sessionStorage.setItem('syntro:editor:pending-highlight', selector);
39
+ }
40
+ catch {
41
+ // Silently ignore
42
+ }
43
+ }
44
+ export function itemKey(section, index) {
45
+ return `${section}:${index}`;
46
+ }
47
+ // ============================================================================
48
+ // Section Config
49
+ // ============================================================================
50
+ export const OVERLAY_SECTIONS = [
51
+ 'tooltips',
52
+ 'highlights',
53
+ 'badges',
54
+ 'pulses',
55
+ 'modals',
56
+ ];
57
+ export function flattenItems(config) {
58
+ const items = [];
59
+ for (const section of OVERLAY_SECTIONS) {
60
+ const arr = config[section] || [];
61
+ arr.forEach((item, i) => {
62
+ const rec = item;
63
+ items.push({
64
+ key: itemKey(section, i),
65
+ section,
66
+ index: i,
67
+ summary: summarizeOverlayItem(section, rec),
68
+ anchorId: resolveAnchorSelector(rec.anchorId),
69
+ rawAnchorId: rec.anchorId,
70
+ isTour: false,
71
+ });
72
+ });
73
+ }
74
+ // Tours
75
+ const tours = config.tours || [];
76
+ tours.forEach((tour, i) => {
77
+ items.push({
78
+ key: itemKey('tours', i),
79
+ section: 'tours',
80
+ index: i,
81
+ summary: summarizeOverlayItem('tours', tour),
82
+ anchorId: '',
83
+ rawAnchorId: undefined,
84
+ isTour: true,
85
+ });
86
+ });
87
+ return items;
88
+ }
89
+ export function filterConfig(config, dismissedKeys) {
90
+ const result = { ...config };
91
+ const allSections = [...OVERLAY_SECTIONS, 'tours'];
92
+ for (const section of allSections) {
93
+ const arr = config[section] || [];
94
+ const filtered = arr.filter((_, i) => !dismissedKeys.has(itemKey(section, i)));
95
+ if (filtered.length > 0 || config[section] !== undefined) {
96
+ result[section] = filtered;
97
+ }
98
+ }
99
+ return result;
100
+ }
101
+ export function getStepIcon(step) {
102
+ const action = step.action;
103
+ const kind = action.kind || '';
104
+ if (kind.includes('tooltip'))
105
+ return '\u{1f4ac}';
106
+ if (kind.includes('highlight'))
107
+ return '\u{2728}';
108
+ if (kind.includes('modal'))
109
+ return '\u{1f4e6}';
110
+ if (kind.includes('badge'))
111
+ return '\u{1f3f7}\u{fe0f}';
112
+ if (kind.includes('pulse'))
113
+ return '\u{1f4ab}';
114
+ return '\u{25cf}';
115
+ }
116
+ export function getStepLabel(step) {
117
+ const action = step.action;
118
+ const anchor = resolveAnchorSelector(action.anchorId);
119
+ if (anchor)
120
+ return anchor;
121
+ const content = action.content;
122
+ if (content?.title)
123
+ return content.title;
124
+ if (content?.body)
125
+ return content.body.slice(0, 30);
126
+ return step.id;
127
+ }
128
+ export function parseOverlayItemKey(key) {
129
+ const [section, indexStr] = key.split(':');
130
+ return { section: section, index: Number(indexStr) };
131
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Adaptive Overlays - Editor UI Component
3
+ *
4
+ * Main editor component, tabs, forms, tour drill-in, item editing, save/publish.
5
+ * All React/JSX lives here; pure logic is imported from overlay-editor-state.
6
+ */
7
+ import type { EditorPanelProps } from './types';
8
+ export declare function OverlaysEditor({ config, onChange, editor }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=overlay-editor-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-editor-ui.d.ts","sourceRoot":"","sources":["../src/overlay-editor-ui.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiCH,OAAO,KAAK,EAAE,gBAAgB,EAAY,MAAM,SAAS,CAAC;AAkF1D,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAif5E"}
@@ -0,0 +1,306 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * Adaptive Overlays - Editor UI Component
4
+ *
5
+ * Main editor component, tabs, forms, tour drill-in, item editing, save/publish.
6
+ * All React/JSX lives here; pure logic is imported from overlay-editor-state.
7
+ */
8
+ import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EditorTextarea, EmptyState, GroupHeader, } from '@syntrologie/shared-editor-ui';
9
+ import { MessageSquare, Route, Sparkles, Square, Tag, Zap } from 'lucide-react';
10
+ import { useCallback, useEffect, useRef, useState } from 'react';
11
+ import { filterConfig, flattenItems, getStepIcon, getStepLabel, parseOverlayItemKey, resolveAnchorRoute, resolveAnchorSelector, savePendingHighlight, } from './overlay-editor-state';
12
+ // ============================================================================
13
+ // Section Icons
14
+ // ============================================================================
15
+ const SECTION_ICON_MAP = {
16
+ tooltips: MessageSquare,
17
+ highlights: Sparkles,
18
+ badges: Tag,
19
+ pulses: Zap,
20
+ modals: Square,
21
+ tours: Route,
22
+ };
23
+ /** Renders the appropriate Lucide icon for a section type */
24
+ function SectionIcon({ section, className }) {
25
+ const IconComponent = SECTION_ICON_MAP[section];
26
+ return _jsx(IconComponent, { size: 16, className: className });
27
+ }
28
+ // ============================================================================
29
+ // Anchor Detection Hook
30
+ // ============================================================================
31
+ function useAnchorDetection(items, config) {
32
+ const [detectionMap, setDetectionMap] = useState(new Map());
33
+ const itemsRef = useRef(items);
34
+ const configRef = useRef(config);
35
+ itemsRef.current = items;
36
+ configRef.current = config;
37
+ useEffect(() => {
38
+ const runDetection = () => {
39
+ const map = new Map();
40
+ for (const item of itemsRef.current) {
41
+ let selectorToCheck = item.anchorId;
42
+ // For tours, detect the first step's anchor
43
+ if (item.isTour && !selectorToCheck) {
44
+ const tours = configRef.current.tours || [];
45
+ const tour = tours[item.index];
46
+ if (tour && tour.steps.length > 0) {
47
+ const firstStep = tour.steps[0];
48
+ const action = firstStep.action;
49
+ selectorToCheck = resolveAnchorSelector(action?.anchorId);
50
+ }
51
+ }
52
+ if (!selectorToCheck) {
53
+ map.set(item.key, { found: false, element: null });
54
+ continue;
55
+ }
56
+ try {
57
+ const el = document.querySelector(selectorToCheck);
58
+ map.set(item.key, { found: el !== null, element: el });
59
+ }
60
+ catch {
61
+ map.set(item.key, { found: false, element: null });
62
+ }
63
+ }
64
+ setDetectionMap(map);
65
+ };
66
+ runDetection();
67
+ const interval = setInterval(runDetection, 2000);
68
+ return () => clearInterval(interval);
69
+ }, []);
70
+ return detectionMap;
71
+ }
72
+ // ============================================================================
73
+ // OverlaysEditor Component
74
+ // ============================================================================
75
+ export function OverlaysEditor({ config, onChange, editor }) {
76
+ const typedConfig = config;
77
+ const [dismissedKeys, setDismissedKeys] = useState(() => editor.getDismissedKeys?.() ?? new Set());
78
+ const [expandedTour, setExpandedTour] = useState(null);
79
+ const [editingKey, setEditingKey] = useState(null);
80
+ const [_previewMode, setPreviewMode] = useState('after');
81
+ // Sync dismissed keys back to navigation context on every change
82
+ useEffect(() => {
83
+ editor.setDismissedKeys?.(dismissedKeys);
84
+ }, [dismissedKeys, editor]);
85
+ // React to global before/after toggle from the panel
86
+ // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally omitted — adding config/typedConfig/previewConfig would cause infinite re-renders since previewConfig triggers state updates
87
+ useEffect(() => {
88
+ const mode = editor.previewMode;
89
+ if (!mode)
90
+ return;
91
+ if (mode === 'before') {
92
+ // Remove all overlay changes — push a config with every item filtered out
93
+ const allKeys = new Set(flattenItems(typedConfig).map((item) => item.key));
94
+ const empty = filterConfig(typedConfig, allKeys);
95
+ editor.previewConfig(empty);
96
+ }
97
+ else {
98
+ // Restore the full config
99
+ editor.previewConfig(config);
100
+ }
101
+ }, [editor.previewMode]);
102
+ // Consume initialEditKey from accordion navigation on mount
103
+ const initialConsumed = useRef(false);
104
+ useEffect(() => {
105
+ if (editor.initialEditKey != null && !initialConsumed.current) {
106
+ initialConsumed.current = true;
107
+ const allFlat = flattenItems(typedConfig);
108
+ const targetIdx = Number(editor.initialEditKey);
109
+ if (targetIdx >= 0 && targetIdx < allFlat.length) {
110
+ const target = allFlat[targetIdx];
111
+ if (target.isTour) {
112
+ setExpandedTour(target.key);
113
+ }
114
+ else {
115
+ setEditingKey(target.key);
116
+ if (target.anchorId) {
117
+ editor.highlightElement(target.anchorId);
118
+ }
119
+ }
120
+ }
121
+ editor.clearInitialState?.();
122
+ }
123
+ else if (editor.initialCreate && !initialConsumed.current) {
124
+ initialConsumed.current = true;
125
+ editor.clearInitialState?.();
126
+ }
127
+ }, [editor, typedConfig]);
128
+ const allItems = flattenItems(typedConfig);
129
+ const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
130
+ const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
131
+ const overlayItems = activeItems.filter((item) => !item.isTour);
132
+ const tourItems = activeItems.filter((item) => item.isTour);
133
+ const totalItems = activeItems.length;
134
+ const [_hoveredKey, setHoveredKey] = useState(null);
135
+ const detectionMap = useAnchorDetection(allItems, typedConfig);
136
+ const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
137
+ const handleDismiss = useCallback((key) => {
138
+ setDismissedKeys((prev) => {
139
+ const next = new Set(prev);
140
+ next.add(key);
141
+ return next;
142
+ });
143
+ if (expandedTour === key)
144
+ setExpandedTour(null);
145
+ if (editingKey === key)
146
+ setEditingKey(null);
147
+ }, [expandedTour, editingKey]);
148
+ const handleRestore = useCallback((key) => {
149
+ setDismissedKeys((prev) => {
150
+ const next = new Set(prev);
151
+ next.delete(key);
152
+ return next;
153
+ });
154
+ }, []);
155
+ const handleCardClick = useCallback((item) => {
156
+ if (item.isTour) {
157
+ setExpandedTour((prev) => (prev === item.key ? null : item.key));
158
+ }
159
+ else {
160
+ if (item.anchorId) {
161
+ editor.highlightElement(item.anchorId);
162
+ }
163
+ setEditingKey(item.key);
164
+ }
165
+ }, [editor]);
166
+ const handleBackToList = useCallback(() => {
167
+ setEditingKey(null);
168
+ setPreviewMode('after');
169
+ editor.previewConfig(config);
170
+ editor.clearHighlight();
171
+ }, [editor, config]);
172
+ // Register back handler in panel header when editing
173
+ useEffect(() => {
174
+ editor.setBackHandler?.(editingKey !== null ? handleBackToList : null);
175
+ return () => editor.setBackHandler?.(null);
176
+ }, [editingKey, handleBackToList, editor]);
177
+ const _handleBeforeAfter = useCallback((mode) => {
178
+ setPreviewMode(mode);
179
+ if (mode === 'before') {
180
+ const filtered = filterConfig(typedConfig, new Set([editingKey]));
181
+ editor.previewConfig(filtered);
182
+ }
183
+ else {
184
+ editor.previewConfig(config);
185
+ }
186
+ }, [typedConfig, editingKey, editor, config]);
187
+ const handleFieldChange = useCallback((section, index, updater) => {
188
+ const arr = (typedConfig[section] || []).slice();
189
+ const item = { ...arr[index] };
190
+ arr[index] = updater(item);
191
+ const updated = { ...typedConfig, [section]: arr };
192
+ onChange(updated);
193
+ editor.setDirty(true);
194
+ }, [typedConfig, onChange, editor]);
195
+ const handlePublish = useCallback(() => {
196
+ if (dismissedKeys.size > 0) {
197
+ const filtered = filterConfig(typedConfig, dismissedKeys);
198
+ onChange(filtered);
199
+ }
200
+ editor.publish();
201
+ }, [dismissedKeys, typedConfig, onChange, editor]);
202
+ const handleBadgeClick = useCallback(async (item) => {
203
+ const detection = detectionMap.get(item.key);
204
+ if (detection?.found && item.anchorId) {
205
+ editor.highlightElement(item.anchorId);
206
+ }
207
+ else {
208
+ const route = resolveAnchorRoute(item.rawAnchorId);
209
+ if (route) {
210
+ if (item.anchorId)
211
+ savePendingHighlight(item.anchorId);
212
+ await editor.navigateTo(route);
213
+ if (item.anchorId)
214
+ editor.highlightElement(item.anchorId);
215
+ }
216
+ else if (item.anchorId) {
217
+ editor.highlightElement(item.anchorId);
218
+ }
219
+ }
220
+ }, [editor, detectionMap]);
221
+ const handleCardHover = useCallback((item) => {
222
+ setHoveredKey(item.key);
223
+ if (item.anchorId) {
224
+ editor.highlightElement(item.anchorId);
225
+ }
226
+ }, [editor]);
227
+ const handleCardLeave = useCallback(() => {
228
+ setHoveredKey(null);
229
+ editor.clearHighlight();
230
+ }, [editor]);
231
+ // ---- Edit form renderers per overlay type ----
232
+ const renderEditFields = (section, index) => {
233
+ const arr = typedConfig[section] || [];
234
+ const item = arr[index];
235
+ if (!item)
236
+ return null;
237
+ const anchorId = resolveAnchorSelector(item.anchorId);
238
+ switch (section) {
239
+ case 'tooltips': {
240
+ const content = item.content || {};
241
+ return (_jsxs("div", { className: "se-py-1", children: [_jsx("div", { className: "se-text-[11px] se-font-mono se-text-slate-grey-8 se-py-1 se-px-2 se-bg-white/[0.04] se-rounded se-mb-3", children: anchorId }), _jsx(EditorInput, { label: "Title", value: content.title || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
242
+ ...it,
243
+ content: { ...it.content, title: e.target.value },
244
+ })) }), _jsx(EditorTextarea, { label: "Body", value: content.body || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
245
+ ...it,
246
+ content: { ...it.content, body: e.target.value },
247
+ })) })] }));
248
+ }
249
+ case 'highlights':
250
+ return (_jsxs("div", { className: "se-py-1", children: [_jsx("div", { className: "se-text-[11px] se-font-mono se-text-slate-grey-8 se-py-1 se-px-2 se-bg-white/[0.04] se-rounded se-mb-3", children: anchorId }), _jsx(EditorInput, { label: "Color", value: item.style?.color || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
251
+ ...it,
252
+ style: {
253
+ ...(it.style || {}),
254
+ color: e.target.value,
255
+ },
256
+ })) })] }));
257
+ case 'badges':
258
+ return (_jsxs("div", { className: "se-py-1", children: [_jsx("div", { className: "se-text-[11px] se-font-mono se-text-slate-grey-8 se-py-1 se-px-2 se-bg-white/[0.04] se-rounded se-mb-3", children: anchorId }), _jsx(EditorInput, { label: "Content", value: item.content || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
259
+ ...it,
260
+ content: e.target.value,
261
+ })) })] }));
262
+ case 'pulses':
263
+ return (_jsxs("div", { className: "se-py-1", children: [_jsx("div", { className: "se-text-[11px] se-font-mono se-text-slate-grey-8 se-py-1 se-px-2 se-bg-white/[0.04] se-rounded se-mb-3", children: anchorId }), _jsx(EditorInput, { label: "Duration (ms)", type: "number", value: item.duration || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
264
+ ...it,
265
+ duration: Number(e.target.value) || undefined,
266
+ })) })] }));
267
+ case 'modals': {
268
+ const content = item.content || {};
269
+ return (_jsxs("div", { className: "se-py-1", children: [_jsx(EditorInput, { label: "Title", value: content.title || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
270
+ ...it,
271
+ content: { ...it.content, title: e.target.value },
272
+ })) }), _jsx(EditorTextarea, { label: "Body", value: content.body || '', onChange: (e) => handleFieldChange(section, index, (it) => ({
273
+ ...it,
274
+ content: { ...it.content, body: e.target.value },
275
+ })) })] }));
276
+ }
277
+ default:
278
+ return null;
279
+ }
280
+ };
281
+ const renderTourDrillIn = (tourIdx) => {
282
+ const tours = typedConfig.tours || [];
283
+ const tour = tours[tourIdx];
284
+ if (!tour)
285
+ return null;
286
+ return (_jsxs("div", { className: "se-p-3 se-rounded-lg se-border se-border-white/[0.08] se-bg-white/[0.02] se-mt-1 se-mb-2", children: [_jsxs("div", { className: "se-text-[13px] se-font-semibold se-text-slate-grey-10 se-mb-2", children: ['\u{1f3af}', " Tour: ", tour.tourId] }), _jsxs("label", { className: "se-flex se-items-center se-gap-2 se-text-xs se-text-[#d1d5db] se-mb-2", children: [_jsx("input", { type: "checkbox", checked: tour.autoStart || false, readOnly: true }), "Auto-start tour"] }), tour.steps.map((step, stepIdx) => (_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-py-1.5 se-px-2 se-rounded se-border se-border-white/[0.04] se-mb-1 se-text-xs se-text-[#d1d5db]", children: [_jsxs("span", { className: "se-text-[11px] se-font-bold se-text-slate-grey-7 se-min-w-[18px]", children: [stepIdx + 1, "."] }), _jsx("span", { children: getStepIcon(step) }), _jsxs("div", { className: "se-flex-1 se-overflow-hidden", children: [_jsx("div", { children: getStepLabel(step) }), step.route && (_jsx("div", { className: "se-text-[10px] se-text-slate-grey-7 se-font-mono", children: step.route }))] })] }, step.id || stepIdx))), tour.steps.length === 0 && (_jsx("div", { className: "se-text-xs se-text-slate-grey-7 se-py-2", children: "No steps in this tour." })), _jsx("button", { type: "button", className: "se-py-1 se-px-2.5 se-rounded se-border se-border-white/10 se-bg-transparent se-text-slate-grey-8 se-text-[11px] se-cursor-pointer se-mt-2", onClick: () => setExpandedTour(null), children: "\u2190 Back to list" })] }));
287
+ };
288
+ const renderCard = (item) => {
289
+ const detection = detectionMap.get(item.key);
290
+ return (_jsxs("div", { children: [_jsxs(EditorCard, { itemKey: item.key, onClick: () => handleCardClick(item), className: "se-flex se-items-center se-gap-2", onMouseEnter: () => handleCardHover(item), onMouseLeave: handleCardLeave, children: [_jsx(DetectionBadge, { found: detection?.found ?? false, onClick: () => handleBadgeClick(item) }), _jsx("span", { className: "se-shrink-0 se-flex se-items-center -se-ml-1", onClick: (e) => {
291
+ e.stopPropagation();
292
+ handleCardClick(item);
293
+ }, children: _jsx(SectionIcon, { section: item.section }) }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", onClick: () => handleCardClick(item), children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-slate-grey-7 se-text-sm se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
294
+ e.stopPropagation();
295
+ handleDismiss(item.key);
296
+ }, title: "Dismiss", children: "\u00D7" })] }), item.isTour && expandedTour === item.key && renderTourDrillIn(item.index)] }, item.key));
297
+ };
298
+ return (_jsxs(EditorLayout, { children: [_jsx(EditorHeader, { title: "Review Changes", subtitle: `${totalItems} item${totalItems !== 1 ? 's' : ''}${totalItems > 0 ? ` (${foundCount} found on this page)` : ''}`, onBack: () => editor.navigateHome() }), _jsx(EditorBody, { children: editingKey !== null ? ((() => {
299
+ const ref = parseOverlayItemKey(editingKey);
300
+ const editItem = allItems.find((it) => it.key === editingKey);
301
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-mb-3 se-text-[13px] se-font-semibold se-text-slate-grey-10", children: [_jsx("span", { children: editItem && _jsx(SectionIcon, { section: editItem.section }) }), _jsx("span", { children: editItem?.summary })] }), renderEditFields(ref.section, ref.index)] }));
302
+ })()) : (_jsxs(_Fragment, { children: [allItems.length === 0 && _jsx(EmptyState, { message: "No overlays configured." }), overlayItems.length > 0 && (_jsxs(_Fragment, { children: [_jsx(GroupHeader, { label: "OVERLAYS", count: overlayItems.length }), overlayItems.map(renderCard)] })), tourItems.length > 0 && (_jsxs(_Fragment, { children: [_jsx(GroupHeader, { label: "TOURS", count: tourItems.length, className: overlayItems.length > 0 ? 'se-mt-4' : '' }), tourItems.map(renderCard)] })), dismissedItems.length > 0 && (_jsx(DismissedSection, { count: dismissedItems.length, children: dismissedItems.map((item) => (_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-py-1.5 se-px-2.5 se-rounded-md se-border se-border-white/[0.03] se-bg-transparent se-mb-0.5 se-cursor-pointer se-text-xs se-text-slate-grey-6 se-opacity-60", children: [_jsx("span", { className: "se-shrink-0 se-flex se-items-center -se-ml-1", children: _jsx(SectionIcon, { section: item.section }) }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap se-line-through", children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-blue-5 se-text-[11px] se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
303
+ e.stopPropagation();
304
+ handleRestore(item.key);
305
+ }, children: "Restore" })] }, item.key))) }))] })) }), _jsx(EditorFooter, { onSave: () => editor.save(), onPublish: handlePublish })] }));
306
+ }
package/dist/runtime.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare const executors: readonly [{
44
44
  readonly kind: "overlays:modal";
45
45
  readonly executor: ActionExecutor<import("./types").ModalAction>;
46
46
  }, {
47
- readonly kind: "core:tour";
47
+ readonly kind: "overlays:tour";
48
48
  readonly executor: ActionExecutor<import("./tour-types").TourAction>;
49
49
  }, {
50
50
  readonly kind: "overlays:celebrate";
@@ -74,7 +74,7 @@ export declare const runtime: {
74
74
  readonly kind: "overlays:modal";
75
75
  readonly executor: ActionExecutor<import("./types").ModalAction>;
76
76
  }, {
77
- readonly kind: "core:tour";
77
+ readonly kind: "overlays:tour";
78
78
  readonly executor: ActionExecutor<import("./tour-types").TourAction>;
79
79
  }, {
80
80
  readonly kind: "overlays:celebrate";
package/dist/runtime.js CHANGED
@@ -375,7 +375,7 @@ export const executors = [
375
375
  { kind: 'overlays:badge', executor: executeBadge },
376
376
  { kind: 'overlays:tooltip', executor: executeTooltip },
377
377
  { kind: 'overlays:modal', executor: executeModal },
378
- { kind: 'core:tour', executor: executeTour },
378
+ { kind: 'overlays:tour', executor: executeTour },
379
379
  { kind: 'overlays:celebrate', executor: executeCelebrate },
380
380
  ];
381
381
  /**
@@ -13,7 +13,7 @@ export interface TourStep {
13
13
  }
14
14
  /** Orchestrate sequential steps with cross-page state */
15
15
  export interface TourAction {
16
- kind: 'core:tour';
16
+ kind: 'overlays:tour';
17
17
  label?: string;
18
18
  tourId: string;
19
19
  steps: TourStep[];
@@ -1 +1 @@
1
- {"version":3,"file":"tour-types.d.ts","sourceRoot":"","sources":["../src/tour-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,4CAA4C;AAC5C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAMD,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH"}
1
+ {"version":3,"file":"tour-types.d.ts","sourceRoot":"","sources":["../src/tour-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,4CAA4C;AAC5C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAMD,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind-preset.d.ts","sourceRoot":"","sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,CAgdxC,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"tailwind-preset.d.ts","sourceRoot":"","sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAW1C,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,CAkdxC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -14,7 +14,7 @@
14
14
  * ```
15
15
  */
16
16
  import { colors } from './tokens/colors';
17
- import { backdropBlur, focusRings, overlay, shadows } from './tokens/effects';
17
+ import { backdropBlur, elevationShadows, focusRings, glowShadows, overlay, shadows, } from './tokens/effects';
18
18
  export const syntroPreset = {
19
19
  darkMode: ['class'],
20
20
  theme: {
@@ -390,9 +390,11 @@ export const syntroPreset = {
390
390
  '3xl': '1.5rem',
391
391
  full: '9999px',
392
392
  },
393
- // Box shadow — Figma shadow scale
393
+ // Box shadow — Figma shadow scale + product glows + elevation
394
394
  boxShadow: {
395
395
  ...shadows,
396
+ ...glowShadows,
397
+ ...elevationShadows,
396
398
  DEFAULT: shadows.sm,
397
399
  // Focus rings (4px spread + xs shadow)
398
400
  'focus-primary': focusRings.primary,
@@ -316,7 +316,7 @@
316
316
  /* ========================================================================== */
317
317
  --syntro-tag-content: #cbd0d7;
318
318
  --syntro-tag-border: #2b333f;
319
- --syntro-tag-bg: #1c2124;
319
+ --syntro-tag-bg: #1c222a;
320
320
 
321
321
  /* ========================================================================== */
322
322
  /* COMPONENT TOKENS - MENU */
@@ -310,7 +310,7 @@ export declare const alert: {
310
310
  export declare const tag: {
311
311
  readonly content: "#cbd0d7";
312
312
  readonly border: "#2b333f";
313
- readonly background: "#1c2124";
313
+ readonly background: "#1c222a";
314
314
  };
315
315
  export declare const menu: {
316
316
  readonly backgroundDefault: "#0e1114";
@@ -742,7 +742,7 @@ export declare const colors: {
742
742
  readonly tag: {
743
743
  readonly content: "#cbd0d7";
744
744
  readonly border: "#2b333f";
745
- readonly background: "#1c2124";
745
+ readonly background: "#1c222a";
746
746
  };
747
747
  readonly menu: {
748
748
  readonly backgroundDefault: "#0e1114";
@@ -349,7 +349,7 @@ export const alert = {
349
349
  export const tag = {
350
350
  content: slateGrey[10],
351
351
  border: slateGrey[4],
352
- background: '#1c2124',
352
+ background: slateGrey[3],
353
353
  };
354
354
  // ============================================================================
355
355
  // MENU TOKENS