@syntrologie/adapt-nav 2.4.1 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/NavWidget.d.ts +3 -3
  2. package/dist/NavWidget.d.ts.map +1 -1
  3. package/dist/NavWidget.js +47 -81
  4. package/dist/cdn.d.ts.map +1 -1
  5. package/dist/editor.d.ts.map +1 -1
  6. package/dist/editor.js +50 -20
  7. package/dist/runtime.d.ts +1 -1
  8. package/dist/runtime.d.ts.map +1 -1
  9. package/dist/runtime.js +5 -4
  10. package/dist/schema.d.ts +554 -95
  11. package/dist/schema.d.ts.map +1 -1
  12. package/dist/schema.js +6 -8
  13. package/dist/summarize.d.ts +2 -2
  14. package/dist/summarize.d.ts.map +1 -1
  15. package/dist/summarize.js +5 -5
  16. package/dist/types.d.ts +7 -49
  17. package/dist/types.d.ts.map +1 -1
  18. package/dist/types.js +1 -1
  19. package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +105 -2
  20. package/node_modules/@syntrologie/sdk-contracts/dist/index.js +5 -3
  21. package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +798 -1
  22. package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +21 -1
  23. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts +2 -0
  24. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts.map +1 -0
  25. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.js +224 -0
  26. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.js +102 -0
  27. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.js +58 -6
  28. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.js +18 -0
  29. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.js +61 -2
  30. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.js +478 -7
  31. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.js +54 -0
  32. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts +2 -0
  33. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts.map +1 -0
  34. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.js +257 -0
  35. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts +2 -0
  36. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts.map +1 -0
  37. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.js +1015 -0
  38. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +1 -1
  39. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts +4 -4
  40. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts.map +1 -1
  41. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +2 -2
  42. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts +2 -1
  43. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts.map +1 -1
  44. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.js +20 -3
  45. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts +10 -8
  46. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
  47. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +350 -87
  48. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +1 -1
  49. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts +3 -3
  50. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +1 -1
  51. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +1 -1
  52. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts +1 -1
  53. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +1 -1
  54. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +5 -2
  55. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts +24 -0
  56. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -0
  57. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/{useShowWhenStatus.js → useTriggerWhenStatus.js} +18 -15
  58. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +3 -3
  59. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
  60. package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -1
  61. package/package.json +1 -1
  62. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts +0 -24
  63. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts.map +0 -1
@@ -2,7 +2,7 @@
2
2
  * Adaptive Nav - NavWidget Component
3
3
  *
4
4
  * React component that renders a collapsible navigation tips accordion
5
- * with per-item conditional visibility based on showWhen decision strategies.
5
+ * with per-item conditional visibility based on triggerWhen decision strategies.
6
6
  *
7
7
  * Demonstrates the compositional action pattern where child actions
8
8
  * (nav:tip) serve as configuration data for the parent widget.
@@ -13,8 +13,8 @@ import type { NavConfig, NavWidgetProps, NavWidgetRuntime } from './types';
13
13
  *
14
14
  * This component demonstrates the compositional action pattern:
15
15
  * - Parent (NavWidget) receives `config.actions` array
16
- * - Each action has optional `showWhen` for per-item visibility
17
- * - Parent evaluates showWhen and filters visible tips
16
+ * - Each action has optional `triggerWhen` for per-item visibility
17
+ * - Parent evaluates triggerWhen and filters visible tips
18
18
  * - Parent manages expand state and re-rendering on context changes
19
19
  */
20
20
  export declare function NavWidget({ config, runtime, instanceId }: NavWidgetProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAgB,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA4PzF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CAiOxE;AAMD;;GAEG;AACH,eAAO,MAAM,kBAAkB;qBAEhB,WAAW,WACb,SAAS,GAAG;QAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAiD3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAgB,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA8PzF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CAmMxE;AAMD;;GAEG;AACH,eAAO,MAAM,kBAAkB;qBAEhB,WAAW,WACb,SAAS,GAAG;QAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAiD3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
package/dist/NavWidget.js CHANGED
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  * Adaptive Nav - NavWidget Component
4
4
  *
5
5
  * React component that renders a collapsible navigation tips accordion
6
- * with per-item conditional visibility based on showWhen decision strategies.
6
+ * with per-item conditional visibility based on triggerWhen decision strategies.
7
7
  *
8
8
  * Demonstrates the compositional action pattern where child actions
9
9
  * (nav:tip) serve as configuration data for the parent widget.
10
10
  */
11
- import { base, purple, slateGrey } from '@syntro/design-system/tokens';
11
+ import { purple, slateGrey } from '@syntro/design-system/tokens';
12
12
  import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
13
13
  import { createRoot } from 'react-dom/client';
14
14
  // ============================================================================
@@ -27,18 +27,17 @@ function escapeHtml(str) {
27
27
  // ============================================================================
28
28
  const baseStyles = {
29
29
  container: {
30
- fontFamily: 'system-ui, -apple-system, sans-serif',
31
- padding: '8px',
30
+ fontFamily: 'var(--sc-font-family, system-ui, -apple-system, sans-serif)',
32
31
  maxWidth: '100%',
33
32
  overflow: 'hidden',
34
33
  },
35
34
  accordion: {
36
35
  display: 'flex',
37
36
  flexDirection: 'column',
38
- gap: '4px',
37
+ gap: 'var(--sc-content-item-gap, 6px)',
39
38
  },
40
39
  item: {
41
- borderRadius: '8px',
40
+ borderRadius: 'var(--sc-content-border-radius, 8px)',
42
41
  overflow: 'hidden',
43
42
  transition: 'box-shadow 0.2s ease',
44
43
  },
@@ -47,20 +46,21 @@ const baseStyles = {
47
46
  alignItems: 'center',
48
47
  gap: '8px',
49
48
  width: '100%',
50
- padding: '12px 16px',
49
+ padding: 'var(--sc-content-item-padding, 12px 16px)',
51
50
  border: 'none',
52
51
  cursor: 'pointer',
53
- fontSize: '14px',
52
+ fontSize: 'var(--sc-content-item-font-size, 15px)',
54
53
  fontWeight: 500,
55
54
  fontFamily: 'inherit',
56
55
  textAlign: 'left',
57
56
  transition: 'background-color 0.15s ease',
58
57
  },
59
58
  chevron: {
60
- fontSize: '10px',
59
+ fontSize: '20px',
61
60
  transition: 'transform 0.2s ease',
62
61
  marginLeft: 'auto',
63
62
  flexShrink: 0,
63
+ color: 'var(--sc-content-chevron-color, currentColor)',
64
64
  },
65
65
  icon: {
66
66
  fontSize: '16px',
@@ -69,10 +69,10 @@ const baseStyles = {
69
69
  body: {
70
70
  overflow: 'hidden',
71
71
  transition: 'max-height 0.25s ease, padding-bottom 0.25s ease',
72
- padding: '0 16px',
72
+ padding: 'var(--sc-content-body-padding, 0 16px 12px 16px)',
73
73
  },
74
74
  description: {
75
- fontSize: '13px',
75
+ fontSize: 'var(--sc-content-body-font-size, 14px)',
76
76
  lineHeight: '1.5',
77
77
  margin: 0,
78
78
  },
@@ -91,11 +91,11 @@ const baseStyles = {
91
91
  transition: 'background-color 0.15s ease',
92
92
  },
93
93
  categoryHeader: {
94
- fontSize: '11px',
94
+ fontSize: 'var(--sc-content-category-font-size, 12px)',
95
95
  fontWeight: 600,
96
96
  textTransform: 'uppercase',
97
97
  letterSpacing: '0.05em',
98
- padding: '12px 4px 4px',
98
+ padding: 'var(--sc-content-category-padding, 8px 4px 4px 4px)',
99
99
  },
100
100
  emptyState: {
101
101
  fontSize: '13px',
@@ -106,29 +106,29 @@ const baseStyles = {
106
106
  const themeStyles = {
107
107
  light: {
108
108
  container: {
109
- backgroundColor: base.white,
110
- color: slateGrey[1],
109
+ backgroundColor: 'transparent',
110
+ color: 'inherit',
111
111
  },
112
112
  item: {
113
- backgroundColor: slateGrey[12],
114
- border: `1px solid ${slateGrey[11]}`,
113
+ backgroundColor: 'var(--sc-content-background)',
114
+ border: 'var(--sc-content-border)',
115
115
  },
116
116
  itemExpanded: {
117
117
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
118
118
  },
119
119
  header: {
120
120
  backgroundColor: 'transparent',
121
- color: slateGrey[1],
121
+ color: 'var(--sc-content-text-color)',
122
122
  },
123
123
  headerHover: {
124
- backgroundColor: slateGrey[12],
124
+ backgroundColor: 'var(--sc-content-background-hover)',
125
125
  },
126
126
  body: {
127
- color: slateGrey[6],
127
+ color: 'var(--sc-content-text-secondary-color)',
128
128
  },
129
129
  linkButton: {
130
- backgroundColor: purple[8],
131
- color: purple[2],
130
+ backgroundColor: 'var(--sc-color-primary, #6366f1)',
131
+ color: '#ffffff',
132
132
  },
133
133
  categoryHeader: {
134
134
  color: slateGrey[7],
@@ -139,29 +139,29 @@ const themeStyles = {
139
139
  },
140
140
  dark: {
141
141
  container: {
142
- backgroundColor: slateGrey[1],
143
- color: slateGrey[12],
142
+ backgroundColor: 'transparent',
143
+ color: 'inherit',
144
144
  },
145
145
  item: {
146
- backgroundColor: slateGrey[3],
147
- border: `1px solid ${slateGrey[5]}`,
146
+ backgroundColor: 'var(--sc-content-background)',
147
+ border: 'var(--sc-content-border)',
148
148
  },
149
149
  itemExpanded: {
150
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
150
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
151
151
  },
152
152
  header: {
153
153
  backgroundColor: 'transparent',
154
- color: slateGrey[12],
154
+ color: 'var(--sc-content-text-color)',
155
155
  },
156
156
  headerHover: {
157
- backgroundColor: slateGrey[5],
157
+ backgroundColor: 'var(--sc-content-background-hover)',
158
158
  },
159
159
  body: {
160
- color: slateGrey[8],
160
+ color: 'var(--sc-content-text-secondary-color)',
161
161
  },
162
162
  linkButton: {
163
- backgroundColor: purple[0],
164
- color: purple[6],
163
+ backgroundColor: 'var(--sc-color-primary, #6366f1)',
164
+ color: '#ffffff',
165
165
  },
166
166
  categoryHeader: {
167
167
  color: slateGrey[8],
@@ -171,7 +171,7 @@ const themeStyles = {
171
171
  },
172
172
  },
173
173
  };
174
- function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
174
+ function NavTipItem({ item, isExpanded, isLast, onToggle, onNavigate, theme }) {
175
175
  const [isHovered, setIsHovered] = useState(false);
176
176
  const colors = themeStyles[theme];
177
177
  const { title, description, href, icon, external } = item.config;
@@ -179,6 +179,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
179
179
  ...baseStyles.item,
180
180
  ...colors.item,
181
181
  ...(isExpanded ? colors.itemExpanded : {}),
182
+ ...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),
182
183
  };
183
184
  const headerStyle = {
184
185
  ...baseStyles.header,
@@ -187,7 +188,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
187
188
  };
188
189
  const chevronStyle = {
189
190
  ...baseStyles.chevron,
190
- transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
191
+ transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
191
192
  };
192
193
  const bodyStyle = {
193
194
  ...baseStyles.body,
@@ -202,7 +203,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
202
203
  onNavigate(href, external ?? false);
203
204
  }
204
205
  };
205
- return (_jsxs("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx("span", { style: baseStyles.icon, children: icon }), _jsx("span", { children: title }), _jsx("span", { style: chevronStyle, children: '\u25BC' })] }), _jsxs("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx("p", { style: baseStyles.description, children: description }), href && (_jsxs("a", { href: href, onClick: handleLinkClick, style: { ...baseStyles.linkButton, ...colors.linkButton }, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, children: ["Go ", external ? '\u2197' : '\u2192'] }))] })] }));
206
+ return (_jsxs("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx("span", { style: baseStyles.icon, children: icon }), _jsx("span", { children: title }), _jsx("span", { style: chevronStyle, children: '\u203A' })] }), _jsxs("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx("p", { style: baseStyles.description, children: description }), href && (_jsxs("a", { href: href, onClick: handleLinkClick, style: { ...baseStyles.linkButton, ...colors.linkButton }, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, children: ["Go ", external ? '\u2197' : '\u2192'] }))] })] }));
206
207
  }
207
208
  // ============================================================================
208
209
  // NavWidget Component
@@ -212,8 +213,8 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
212
213
  *
213
214
  * This component demonstrates the compositional action pattern:
214
215
  * - Parent (NavWidget) receives `config.actions` array
215
- * - Each action has optional `showWhen` for per-item visibility
216
- * - Parent evaluates showWhen and filters visible tips
216
+ * - Each action has optional `triggerWhen` for per-item visibility
217
+ * - Parent evaluates triggerWhen and filters visible tips
217
218
  * - Parent manages expand state and re-rendering on context changes
218
219
  */
219
220
  export function NavWidget({ config, runtime, instanceId }) {
@@ -228,7 +229,7 @@ export function NavWidget({ config, runtime, instanceId }) {
228
229
  });
229
230
  return unsubscribe;
230
231
  }, [runtime.context]);
231
- // Subscribe to accumulator changes for event_count-based showWhen
232
+ // Subscribe to accumulator changes for event_count-based triggerWhen
232
233
  useEffect(() => {
233
234
  if (!runtime.accumulator?.subscribe)
234
235
  return;
@@ -236,50 +237,13 @@ export function NavWidget({ config, runtime, instanceId }) {
236
237
  forceUpdate();
237
238
  });
238
239
  }, [runtime.accumulator]);
239
- // Register accumulator predicates from scope config
240
- useEffect(() => {
241
- if (!config.scope || !runtime.accumulator?.register)
242
- return;
243
- const { events: eventNames, urlContains, props: propFilters } = config.scope;
244
- // Scan showWhen conditions for event_count keys
245
- const keys = new Set();
246
- for (const action of config.actions) {
247
- if (action.showWhen?.type === 'rules') {
248
- for (const rule of action.showWhen.rules) {
249
- for (const cond of rule.conditions) {
250
- if (cond.type === 'event_count' && cond.key) {
251
- keys.add(cond.key);
252
- }
253
- }
254
- }
255
- }
256
- }
257
- for (const key of keys) {
258
- runtime.accumulator.register(key, (event) => {
259
- if (!eventNames.includes(event.name))
260
- return false;
261
- if (urlContains) {
262
- const pathname = String(event.props?.pathname ?? '');
263
- if (!pathname.includes(urlContains))
264
- return false;
265
- }
266
- if (propFilters) {
267
- for (const [k, v] of Object.entries(propFilters)) {
268
- if (event.props?.[k] !== v)
269
- return false;
270
- }
271
- }
272
- return true;
273
- });
274
- }
275
- }, [config.scope, config.actions, runtime.accumulator]);
276
- // Filter visible tips based on per-item showWhen
240
+ // Filter visible tips based on per-item triggerWhen
277
241
  // biome-ignore lint/correctness/useExhaustiveDependencies: renderTick is intentionally included to force re-evaluation when the runtime's mutable context changes (subscribed above via forceUpdate)
278
242
  const visibleTips = useMemo(() => config.actions.filter((tip) => {
279
- if (!tip.showWhen)
243
+ if (!tip.triggerWhen)
280
244
  return true;
281
245
  try {
282
- const result = runtime.evaluateSync(tip.showWhen);
246
+ const result = runtime.evaluateSync(tip.triggerWhen);
283
247
  return result.value;
284
248
  }
285
249
  catch {
@@ -365,7 +329,9 @@ export function NavWidget({ config, runtime, instanceId }) {
365
329
  window.open(href, '_blank', 'noopener,noreferrer');
366
330
  }
367
331
  else {
368
- window.location.href = href;
332
+ const url = new URL(href, window.location.origin);
333
+ url.search = window.location.search;
334
+ window.location.href = url.toString();
369
335
  }
370
336
  }, [runtime.events, instanceId]);
371
337
  // Compute container styles
@@ -382,10 +348,10 @@ export function NavWidget({ config, runtime, instanceId }) {
382
348
  ...themeStyles[resolvedTheme].emptyState,
383
349
  };
384
350
  // Render a list of nav tip items
385
- const renderItems = (items) => items.map((tip) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id)));
351
+ const renderItems = (items) => items.map((tip, index) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), isLast: index === items.length - 1, onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id)));
386
352
  // Empty state
387
353
  if (visibleTips.length === 0) {
388
- return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: emptyStateStyle, children: "No navigation tips available." }) }));
354
+ return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: emptyStateStyle, children: "You're all set for now! We'll share helpful tips here when they're relevant to what you're doing." }) }));
389
355
  }
390
356
  return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: baseStyles.accordion, children: hasCategories
391
357
  ? Array.from(categoryGroups.entries()).map(([category, items]) => (_jsxs(React.Fragment, { children: [category && (_jsx("div", { style: categoryHeaderStyle, "data-category-header": category, children: category })), renderItems(items)] }, category ?? '__ungrouped')))
package/dist/cdn.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;2BAsC07Z,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtBp9Z,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;2BAsCgpZ,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtB1qZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,SAAS,CAAC;AA0KjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CA6XvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAGlB,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,SAAS,CAAC;AAmLjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAoZvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAGlB,CAAC;AAEF,eAAe,SAAS,CAAC"}
package/dist/editor.js CHANGED
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
6
6
  * Displays a scannable list of tip cards with trigger, rationale,
7
7
  * and inline editing. Includes detection badges and hover-to-highlight.
8
8
  */
9
- import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EmptyState, GroupHeader, TriggerJourney, useShowWhenStatus, } from '@syntrologie/shared-editor-ui';
9
+ import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EmptyState, GroupHeader, TriggerJourney, useTriggerWhenStatus, } from '@syntrologie/shared-editor-ui';
10
10
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
11
11
  import { describeTrigger, summarizeNavItem } from './summarize';
12
12
  import { isOwnAction, } from './types';
@@ -16,13 +16,13 @@ function isRuleStrategy(s) {
16
16
  s.type === 'rules' &&
17
17
  Array.isArray(s.rules));
18
18
  }
19
- function extractTargetingInfo(showWhen) {
20
- if (!showWhen || !isRuleStrategy(showWhen)) {
19
+ function extractTargetingInfo(triggerWhen) {
20
+ if (!triggerWhen || !isRuleStrategy(triggerWhen)) {
21
21
  return { pagePatterns: [], anchorSelectors: [], hasTargeting: false };
22
22
  }
23
23
  const pagePatterns = new Set();
24
24
  const anchorSelectors = new Set();
25
- for (const rule of showWhen.rules) {
25
+ for (const rule of triggerWhen.rules) {
26
26
  for (const cond of rule.conditions) {
27
27
  const c = cond;
28
28
  if (c.type === 'page_url' && typeof c.url === 'string') {
@@ -40,23 +40,32 @@ function extractTargetingInfo(showWhen) {
40
40
  hasTargeting,
41
41
  };
42
42
  }
43
- function extractFirstPage(showWhen) {
44
- const info = extractTargetingInfo(showWhen);
43
+ function extractFirstPage(triggerWhen) {
44
+ const info = extractTargetingInfo(triggerWhen);
45
45
  return info.pagePatterns[0] || null;
46
46
  }
47
- function extractFirstAnchor(showWhen) {
48
- const info = extractTargetingInfo(showWhen);
47
+ function extractFirstAnchor(triggerWhen) {
48
+ const info = extractTargetingInfo(triggerWhen);
49
49
  return info.anchorSelectors[0] || null;
50
50
  }
51
+ /** Save a pending highlight selector to sessionStorage (inlined to avoid cross-package import). */
52
+ function savePendingHighlight(selector) {
53
+ try {
54
+ sessionStorage.setItem('syntro:editor:pending-highlight', selector);
55
+ }
56
+ catch {
57
+ // Silently ignore
58
+ }
59
+ }
51
60
  function flattenItems(config) {
52
61
  const actions = (config.actions || []).filter(isOwnAction);
53
62
  return actions.map((tip, i) => ({
54
63
  key: String(i),
55
64
  index: i,
56
65
  summary: summarizeNavItem(tip),
57
- trigger: describeTrigger(tip.showWhen),
66
+ trigger: describeTrigger(tip.triggerWhen),
58
67
  rationale: tip.rationale,
59
- firstAnchor: extractFirstAnchor(tip.showWhen),
68
+ firstAnchor: extractFirstAnchor(tip.triggerWhen),
60
69
  tip,
61
70
  }));
62
71
  }
@@ -76,7 +85,7 @@ function useDetection(items, getCurrentRoute) {
76
85
  const map = new Map();
77
86
  const currentPath = getCurrentRoute();
78
87
  for (const item of itemsRef.current) {
79
- const targeting = extractTargetingInfo(item.tip.showWhen);
88
+ const targeting = extractTargetingInfo(item.tip.triggerWhen);
80
89
  // Check page match
81
90
  let pageMatch = true;
82
91
  if (targeting.pagePatterns.length > 0) {
@@ -162,12 +171,12 @@ export function NavEditor({ config, onChange, editor }) {
162
171
  const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
163
172
  const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
164
173
  const totalLinks = activeItems.length;
165
- // Live showWhen status for condition diagnostics
166
- const showWhenItems = useMemo(() => allItems.map((item) => ({
174
+ // Live triggerWhen status for condition diagnostics
175
+ const triggerWhenItems = useMemo(() => allItems.map((item) => ({
167
176
  id: item.key,
168
- showWhen: item.tip.showWhen,
177
+ triggerWhen: item.tip.triggerWhen,
169
178
  })), [allItems]);
170
- const showWhenStatuses = useShowWhenStatus(showWhenItems);
179
+ const triggerWhenStatuses = useTriggerWhenStatus(triggerWhenItems);
171
180
  const detectionMap = useDetection(allItems, editor.getCurrentRoute);
172
181
  const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
173
182
  const handleDismiss = useCallback((key) => {
@@ -189,10 +198,12 @@ export function NavEditor({ config, onChange, editor }) {
189
198
  const handleCardBodyClick = useCallback((item) => {
190
199
  setEditingKey(item.key);
191
200
  }, []);
192
- const handleTriggerClick = useCallback((item) => {
193
- const pageUrl = extractFirstPage(item.tip.showWhen);
201
+ const handleTriggerClick = useCallback(async (item) => {
202
+ const pageUrl = extractFirstPage(item.tip.triggerWhen);
194
203
  if (pageUrl) {
195
- editor.navigateTo(pageUrl);
204
+ if (item.firstAnchor)
205
+ savePendingHighlight(item.firstAnchor);
206
+ await editor.navigateTo(pageUrl);
196
207
  }
197
208
  if (item.firstAnchor) {
198
209
  editor.highlightElement(item.firstAnchor);
@@ -236,6 +247,25 @@ export function NavEditor({ config, onChange, editor }) {
236
247
  }
237
248
  editor.publish();
238
249
  }, [dismissedKeys, typedConfig, onChange, editor]);
250
+ const handleBadgeClick = useCallback(async (item) => {
251
+ const detection = detectionMap.get(item.key);
252
+ if (detection?.found && item.firstAnchor) {
253
+ editor.highlightElement(item.firstAnchor);
254
+ }
255
+ else {
256
+ const pageUrl = extractFirstPage(item.tip.triggerWhen);
257
+ if (pageUrl) {
258
+ if (item.firstAnchor)
259
+ savePendingHighlight(item.firstAnchor);
260
+ await editor.navigateTo(pageUrl);
261
+ if (item.firstAnchor)
262
+ editor.highlightElement(item.firstAnchor);
263
+ }
264
+ else if (item.firstAnchor) {
265
+ editor.highlightElement(item.firstAnchor);
266
+ }
267
+ }
268
+ }, [editor, detectionMap]);
239
269
  const handleCardHover = useCallback((item) => {
240
270
  setHoveredKey(item.key);
241
271
  if (item.firstAnchor) {
@@ -272,10 +302,10 @@ export function NavEditor({ config, onChange, editor }) {
272
302
  }, children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsxs("div", { "data-card-body": true, role: "button", tabIndex: 0, className: "se-flex se-items-center se-gap-2 se-cursor-pointer", onClick: () => handleCardBodyClick(item), onKeyDown: (e) => {
273
303
  if (e.key === 'Enter' || e.key === ' ')
274
304
  handleCardBodyClick(item);
275
- }, children: [_jsx(DetectionBadge, { found: detection?.found ?? false }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", 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) => {
305
+ }, children: [_jsx(DetectionBadge, { found: detection?.found ?? false, onClick: () => handleBadgeClick(item) }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", 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) => {
276
306
  e.stopPropagation();
277
307
  handleDismiss(item.key);
278
- }, title: "Dismiss this tip", children: "\u00D7" })] }), _jsxs("div", { className: "se-text-[10px] se-text-slate-grey-7 se-mt-1", children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] }), _jsx(TriggerJourney, { status: showWhenStatuses.get(item.key) ?? null })] }, item.key));
308
+ }, title: "Dismiss this tip", children: "\u00D7" })] }), _jsxs("div", { className: "se-text-[10px] se-text-slate-grey-7 se-mt-1", children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] }), _jsx(TriggerJourney, { status: triggerWhenStatuses.get(item.key) ?? null })] }, item.key));
279
309
  })] })), 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-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) => {
280
310
  e.stopPropagation();
281
311
  handleRestore(item.key);
package/dist/runtime.d.ts CHANGED
@@ -67,7 +67,7 @@ export declare const runtime: {
67
67
  /**
68
68
  * Extract notify watcher entries from tile config props.
69
69
  * The runtime evaluates these continuously (even with drawer closed)
70
- * and publishes nav:tip_revealed when showWhen transitions false → true.
70
+ * and publishes nav:tip_revealed when triggerWhen transitions false → true.
71
71
  */
72
72
  notifyWatchers(props: Record<string, unknown>): {
73
73
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA4B1D,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAoC1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;EAGZ,CAAC;AAMX;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAMlB;;OAEG;;;;;;;;IAGH;;OAEG;;;;;uBAqCu1S,CAAC;0BAA8B,CAAC;;;;;;;;;IAxB13S;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;CAgB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA+B1D,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAoC1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;EAGZ,CAAC;AAMX;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAMlB;;OAEG;;;;;;;;IAGH;;OAEG;;;;;uBAqCy8R,CAAC;0BAA8B,CAAC;;;;;;;;;IAxB5+R;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;CAgB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
package/dist/runtime.js CHANGED
@@ -15,7 +15,8 @@ import { NavMountableWidget } from './NavWidget';
15
15
  export const executeScrollTo = async (action, context) => {
16
16
  const anchorEl = context.resolveAnchor(action.anchorId);
17
17
  if (!anchorEl) {
18
- throw new Error(`Anchor not found: ${action.anchorId}`);
18
+ console.error(`[adaptive-nav] Anchor not found for scrollTo, skipping: ${action.anchorId.selector}`);
19
+ return { cleanup: () => { } };
19
20
  }
20
21
  // Scroll to element
21
22
  anchorEl.scrollIntoView({
@@ -131,15 +132,15 @@ export const runtime = {
131
132
  /**
132
133
  * Extract notify watcher entries from tile config props.
133
134
  * The runtime evaluates these continuously (even with drawer closed)
134
- * and publishes nav:tip_revealed when showWhen transitions false → true.
135
+ * and publishes nav:tip_revealed when triggerWhen transitions false → true.
135
136
  */
136
137
  notifyWatchers(props) {
137
138
  const actions = (props.actions ?? []);
138
139
  return actions
139
- .filter((a) => a.notify && a.showWhen)
140
+ .filter((a) => a.notify && a.triggerWhen)
140
141
  .map((a) => ({
141
142
  id: `nav:${a.config.id}`,
142
- strategy: a.showWhen,
143
+ strategy: a.triggerWhen,
143
144
  eventName: 'nav:tip_revealed',
144
145
  eventProps: {
145
146
  tipId: a.config.id,