@syntrologie/adapt-faq 2.4.0 → 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.
@@ -2,7 +2,7 @@
2
2
  * Adaptive FAQ - FAQWidget Component
3
3
  *
4
4
  * React component that renders a collapsible Q&A accordion with per-item
5
- * conditional visibility based on showWhen decision strategies.
5
+ * conditional visibility based on triggerWhen decision strategies.
6
6
  *
7
7
  * Demonstrates the compositional action pattern where child actions
8
8
  * (faq:question) serve as configuration data for the parent widget.
@@ -44,7 +44,7 @@ export interface FAQWidgetRuntime {
44
44
  get: (key: string) => unknown;
45
45
  set: (key: string, value: unknown) => void;
46
46
  };
47
- /** Event accumulator for reactive showWhen re-evaluation */
47
+ /** Event accumulator for reactive triggerWhen re-evaluation */
48
48
  accumulator?: {
49
49
  subscribe: (callback: () => void) => () => void;
50
50
  register: (key: string, predicate: (event: any) => boolean) => void;
@@ -60,8 +60,8 @@ interface FAQWidgetProps {
60
60
  *
61
61
  * This component demonstrates the compositional action pattern:
62
62
  * - Parent (FAQWidget) receives `config.actions` array
63
- * - Each action has optional `showWhen` for per-item visibility
64
- * - Parent evaluates showWhen and filters visible questions
63
+ * - Each action has optional `triggerWhen` for per-item visibility
64
+ * - Parent evaluates triggerWhen and filters visible questions
65
65
  * - Parent manages expand state and re-rendering on context changes
66
66
  */
67
67
  export declare function FAQWidget({ config, runtime, instanceId }: FAQWidgetProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"FAQWidget.d.ts","sourceRoot":"","sources":["../src/FAQWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,gBAAgB,EAEhB,SAAS,EAIV,MAAM,SAAS,CAAC;AA2CjB,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,OAAO,EAAE;QAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;KAAE,CAAC;IAC7D,MAAM,EAAE;QACN,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE,SAAS,CAAC,EAAE,CACV,gBAAgB,EACZ;YAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,GAC7D,CAAC,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC,EACpF,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,KAC3F,MAAM,IAAI,CAAC;QAChB,SAAS,CAAC,EAAE,CACV,MAAM,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,EAClD,KAAK,CAAC,EAAE,MAAM,KACX,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3E,CAAC;IACF,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE,CAAC;IACtF,4DAA4D;IAC5D,WAAW,CAAC,EAAE;QACZ,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;QAChD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,KAAK,IAAI,CAAC;KACrE,CAAC;CACH;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AA+SD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CA2UxE;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":"FAQWidget.d.ts","sourceRoot":"","sources":["../src/FAQWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,gBAAgB,EAEhB,SAAS,EAIV,MAAM,SAAS,CAAC;AA2CjB,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,OAAO,EAAE;QAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;KAAE,CAAC;IAC7D,MAAM,EAAE;QACN,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE,SAAS,CAAC,EAAE,CACV,gBAAgB,EACZ;YAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,GAC7D,CAAC,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC,EACpF,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,KAC3F,MAAM,IAAI,CAAC;QAChB,SAAS,CAAC,EAAE,CACV,MAAM,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,EAClD,KAAK,CAAC,EAAE,MAAM,KACX,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3E,CAAC;IACF,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE,CAAC;IACtF,+DAA+D;IAC/D,WAAW,CAAC,EAAE;QACZ,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;QAChD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,KAAK,IAAI,CAAC;KACrE,CAAC;CACH;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAyTD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CA8VxE;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/FAQWidget.js CHANGED
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  * Adaptive FAQ - FAQWidget Component
4
4
  *
5
5
  * React component that renders a collapsible Q&A accordion with per-item
6
- * conditional visibility based on showWhen decision strategies.
6
+ * conditional visibility based on triggerWhen decision strategies.
7
7
  *
8
8
  * Demonstrates the compositional action pattern where child actions
9
9
  * (faq:question) 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
  // ============================================================================
@@ -52,12 +52,12 @@ function getFeedbackPrompt(feedbackConfig) {
52
52
  // ============================================================================
53
53
  const baseStyles = {
54
54
  container: {
55
- fontFamily: 'system-ui, -apple-system, sans-serif',
55
+ fontFamily: 'var(--sc-font-family, system-ui, -apple-system, sans-serif)',
56
56
  maxWidth: '800px',
57
57
  margin: '0 auto',
58
58
  },
59
59
  searchWrapper: {
60
- marginBottom: '16px',
60
+ marginBottom: '8px',
61
61
  },
62
62
  searchInput: {
63
63
  width: '100%',
@@ -66,37 +66,40 @@ const baseStyles = {
66
66
  fontSize: '14px',
67
67
  outline: 'none',
68
68
  transition: 'border-color 0.15s ease',
69
+ backgroundColor: 'var(--sc-content-search-background)',
70
+ color: 'var(--sc-content-search-color)',
69
71
  },
70
72
  accordion: {
71
73
  display: 'flex',
72
74
  flexDirection: 'column',
73
- gap: '8px',
75
+ gap: 'var(--sc-content-item-gap, 6px)',
74
76
  },
75
77
  item: {
76
- borderRadius: '8px',
78
+ borderRadius: 'var(--sc-content-border-radius, 8px)',
77
79
  overflow: 'hidden',
78
80
  transition: 'box-shadow 0.15s ease',
79
81
  },
80
82
  question: {
81
83
  width: '100%',
82
- padding: '16px 20px',
84
+ padding: 'var(--sc-content-item-padding, 12px 16px)',
83
85
  display: 'flex',
84
86
  alignItems: 'center',
85
87
  justifyContent: 'space-between',
86
88
  border: 'none',
87
89
  cursor: 'pointer',
88
- fontSize: '15px',
90
+ fontSize: 'var(--sc-content-item-font-size, 15px)',
89
91
  fontWeight: 500,
90
92
  textAlign: 'left',
91
93
  transition: 'background-color 0.15s ease',
92
94
  },
93
95
  chevron: {
94
- fontSize: '18px',
96
+ fontSize: '20px',
95
97
  transition: 'transform 0.2s ease',
98
+ color: 'var(--sc-content-chevron-color, currentColor)',
96
99
  },
97
100
  answer: {
98
- padding: '0 20px 16px 20px',
99
- fontSize: '14px',
101
+ padding: 'var(--sc-content-body-padding, 0 16px 12px 16px)',
102
+ fontSize: 'var(--sc-content-body-font-size, 14px)',
100
103
  lineHeight: 1.6,
101
104
  overflow: 'hidden',
102
105
  transition: 'max-height 0.2s ease, padding 0.2s ease',
@@ -112,12 +115,12 @@ const baseStyles = {
112
115
  marginBottom: '8px',
113
116
  },
114
117
  categoryHeader: {
115
- fontSize: '13px',
118
+ fontSize: 'var(--sc-content-category-font-size, 12px)',
116
119
  fontWeight: 700,
117
120
  textTransform: 'uppercase',
118
121
  letterSpacing: '0.05em',
119
- padding: '12px 4px 6px 4px',
120
- marginTop: '8px',
122
+ padding: 'var(--sc-content-category-padding, 8px 4px 4px 4px)',
123
+ marginTop: 'var(--sc-content-category-gap, 4px)',
121
124
  },
122
125
  feedback: {
123
126
  display: 'flex',
@@ -155,30 +158,28 @@ const baseStyles = {
155
158
  const themeStyles = {
156
159
  light: {
157
160
  container: {
158
- backgroundColor: base.white,
159
- color: slateGrey[1],
161
+ backgroundColor: 'transparent',
162
+ color: 'inherit',
160
163
  },
161
164
  searchInput: {
162
- backgroundColor: slateGrey[12],
163
165
  border: `1px solid ${slateGrey[11]}`,
164
- color: slateGrey[1],
165
166
  },
166
167
  item: {
167
- backgroundColor: slateGrey[12],
168
- border: `1px solid ${slateGrey[11]}`,
168
+ backgroundColor: 'var(--sc-content-background)',
169
+ border: 'var(--sc-content-border)',
169
170
  },
170
171
  itemExpanded: {
171
172
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
172
173
  },
173
174
  question: {
174
175
  backgroundColor: 'transparent',
175
- color: slateGrey[1],
176
+ color: 'var(--sc-content-text-color)',
176
177
  },
177
178
  questionHover: {
178
- backgroundColor: slateGrey[12],
179
+ backgroundColor: 'var(--sc-content-background-hover)',
179
180
  },
180
181
  answer: {
181
- color: slateGrey[6],
182
+ color: 'var(--sc-content-text-secondary-color)',
182
183
  },
183
184
  category: {
184
185
  backgroundColor: purple[8],
@@ -196,30 +197,28 @@ const themeStyles = {
196
197
  },
197
198
  dark: {
198
199
  container: {
199
- backgroundColor: slateGrey[1],
200
- color: slateGrey[12],
200
+ backgroundColor: 'transparent',
201
+ color: 'inherit',
201
202
  },
202
203
  searchInput: {
203
- backgroundColor: slateGrey[3],
204
204
  border: `1px solid ${slateGrey[5]}`,
205
- color: slateGrey[12],
206
205
  },
207
206
  item: {
208
- backgroundColor: slateGrey[3],
209
- border: `1px solid ${slateGrey[5]}`,
207
+ backgroundColor: 'var(--sc-content-background)',
208
+ border: 'var(--sc-content-border)',
210
209
  },
211
210
  itemExpanded: {
212
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
211
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
213
212
  },
214
213
  question: {
215
214
  backgroundColor: 'transparent',
216
- color: slateGrey[12],
215
+ color: 'var(--sc-content-text-color)',
217
216
  },
218
217
  questionHover: {
219
- backgroundColor: slateGrey[5],
218
+ backgroundColor: 'var(--sc-content-background-hover)',
220
219
  },
221
220
  answer: {
222
- color: slateGrey[8],
221
+ color: 'var(--sc-content-text-secondary-color)',
223
222
  },
224
223
  category: {
225
224
  backgroundColor: purple[0],
@@ -236,7 +235,7 @@ const themeStyles = {
236
235
  },
237
236
  },
238
237
  };
239
- function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackValue, onFeedback, }) {
238
+ function FAQItem({ item, isExpanded, isHighlighted, isLast, onToggle, theme, feedbackConfig, feedbackValue, onFeedback, }) {
240
239
  const [isHovered, setIsHovered] = useState(false);
241
240
  const colors = themeStyles[theme];
242
241
  const { question, answer } = item.config;
@@ -244,6 +243,13 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
244
243
  ...baseStyles.item,
245
244
  ...colors.item,
246
245
  ...(isExpanded ? colors.itemExpanded : {}),
246
+ ...(isHighlighted
247
+ ? {
248
+ boxShadow: '0 0 0 2px #6366f1, 0 0 12px rgba(99, 102, 241, 0.4)',
249
+ transition: 'box-shadow 0.3s ease',
250
+ }
251
+ : {}),
252
+ ...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),
247
253
  };
248
254
  const questionStyle = {
249
255
  ...baseStyles.question,
@@ -252,7 +258,7 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
252
258
  };
253
259
  const chevronStyle = {
254
260
  ...baseStyles.chevron,
255
- transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
261
+ transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
256
262
  };
257
263
  const answerStyle = {
258
264
  ...baseStyles.answer,
@@ -264,7 +270,7 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
264
270
  ...baseStyles.feedback,
265
271
  ...colors.feedbackPrompt,
266
272
  };
267
- return (_jsxs("div", { style: itemStyle, "data-faq-item-id": item.config.id, children: [_jsxs("button", { type: "button", style: questionStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [_jsx("span", { children: question }), _jsx("span", { style: chevronStyle, children: '\u25BC' })] }), _jsxs("div", { style: answerStyle, "aria-hidden": !isExpanded, children: [renderAnswer(answer), isExpanded && feedbackConfig && (_jsxs("div", { style: feedbackStyle, children: [_jsx("span", { children: getFeedbackPrompt(feedbackConfig) }), _jsx("button", { type: "button", style: {
273
+ return (_jsxs("div", { style: itemStyle, "data-faq-item-id": item.config.id, children: [_jsxs("button", { type: "button", style: questionStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [_jsx("span", { children: question }), _jsx("span", { style: chevronStyle, children: '\u203A' })] }), _jsxs("div", { style: answerStyle, "aria-hidden": !isExpanded, children: [renderAnswer(answer), isExpanded && feedbackConfig && (_jsxs("div", { style: feedbackStyle, children: [_jsx("span", { children: getFeedbackPrompt(feedbackConfig) }), _jsx("button", { type: "button", style: {
268
274
  ...baseStyles.feedbackButton,
269
275
  ...(feedbackValue === 'up' ? baseStyles.feedbackButtonSelected : {}),
270
276
  }, "aria-label": "Thumbs up", onClick: () => onFeedback(item.config.id, question, 'up'), children: '\uD83D\uDC4D' }), _jsx("button", { type: "button", style: {
@@ -280,16 +286,18 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
280
286
  *
281
287
  * This component demonstrates the compositional action pattern:
282
288
  * - Parent (FAQWidget) receives `config.actions` array
283
- * - Each action has optional `showWhen` for per-item visibility
284
- * - Parent evaluates showWhen and filters visible questions
289
+ * - Each action has optional `triggerWhen` for per-item visibility
290
+ * - Parent evaluates triggerWhen and filters visible questions
285
291
  * - Parent manages expand state and re-rendering on context changes
286
292
  */
287
293
  export function FAQWidget({ config, runtime, instanceId }) {
288
294
  // Force re-render when context/accumulator changes.
289
- // renderTick is used as a useMemo dependency to invalidate cached showWhen evaluations.
295
+ // renderTick is used as a useMemo dependency to invalidate cached triggerWhen evaluations.
290
296
  const [renderTick, forceUpdate] = useReducer((x) => x + 1, 0);
291
297
  // Track expanded question IDs
292
298
  const [expandedIds, setExpandedIds] = useState(new Set());
299
+ // Track which item is flash-highlighted from a deep-link
300
+ const [highlightId, setHighlightId] = useState(null);
293
301
  // Search query state
294
302
  const [searchQuery, setSearchQuery] = useState('');
295
303
  // Track feedback state per item
@@ -303,7 +311,7 @@ export function FAQWidget({ config, runtime, instanceId }) {
303
311
  });
304
312
  return unsubscribe;
305
313
  }, [runtime.context]);
306
- // Subscribe to accumulator changes for event_count-based showWhen
314
+ // Subscribe to accumulator changes for event_count-based triggerWhen
307
315
  useEffect(() => {
308
316
  if (!runtime.accumulator?.subscribe)
309
317
  return;
@@ -311,43 +319,6 @@ export function FAQWidget({ config, runtime, instanceId }) {
311
319
  forceUpdate();
312
320
  });
313
321
  }, [runtime.accumulator]);
314
- // Register accumulator predicates from scope config
315
- useEffect(() => {
316
- if (!config.scope || !runtime.accumulator?.register)
317
- return;
318
- const { events: eventNames, urlContains, props: propFilters } = config.scope;
319
- // Scan showWhen conditions for event_count keys
320
- const keys = new Set();
321
- for (const action of config.actions) {
322
- if (action.showWhen?.type === 'rules') {
323
- for (const rule of action.showWhen.rules) {
324
- for (const cond of rule.conditions) {
325
- if (cond.type === 'event_count' && cond.key) {
326
- keys.add(cond.key);
327
- }
328
- }
329
- }
330
- }
331
- }
332
- for (const key of keys) {
333
- runtime.accumulator.register(key, (event) => {
334
- if (!eventNames.includes(event.name))
335
- return false;
336
- if (urlContains) {
337
- const pathname = String(event.props?.pathname ?? '');
338
- if (!pathname.includes(urlContains))
339
- return false;
340
- }
341
- if (propFilters) {
342
- for (const [k, v] of Object.entries(propFilters)) {
343
- if (event.props?.[k] !== v)
344
- return false;
345
- }
346
- }
347
- return true;
348
- });
349
- }
350
- }, [config.scope, config.actions, runtime.accumulator]);
351
322
  // Subscribe to faq:open:* events from overlay CTA clicks
352
323
  useEffect(() => {
353
324
  if (!runtime.events.subscribe)
@@ -391,14 +362,52 @@ export function FAQWidget({ config, runtime, instanceId }) {
391
362
  });
392
363
  return unsubscribe;
393
364
  }, [runtime]);
394
- // Filter visible questions based on per-item showWhen
365
+ // Subscribe to notification.deep_link events (from insertHtml deepLink clicks + notification toasts)
366
+ useEffect(() => {
367
+ if (!runtime.events.subscribe)
368
+ return;
369
+ const handleDeepLink = (event) => {
370
+ const tileId = event.props?.tileId;
371
+ const itemId = event.props?.itemId;
372
+ // Only handle if this deep link targets our tile
373
+ if (tileId !== instanceId)
374
+ return;
375
+ if (!itemId)
376
+ return;
377
+ // Expand the target item
378
+ setExpandedIds(new Set([itemId]));
379
+ // Flash-highlight the item
380
+ setHighlightId(itemId);
381
+ setTimeout(() => setHighlightId(null), 1500);
382
+ // Scroll into view after render
383
+ requestAnimationFrame(() => {
384
+ const el = document.querySelector(`[data-faq-item-id="${itemId}"]`);
385
+ if (el)
386
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
387
+ });
388
+ };
389
+ // Check recent events (may have fired before widget mounted, e.g. canvas was closed)
390
+ if (runtime.events.getRecent) {
391
+ const recent = runtime.events.getRecent({ names: ['notification.deep_link'] }, 5);
392
+ const pending = recent
393
+ .filter((e) => e.props?.tileId === instanceId && e.props?.itemId)
394
+ .pop();
395
+ if (pending && Date.now() - pending.ts < 10000) {
396
+ handleDeepLink(pending);
397
+ }
398
+ }
399
+ // Subscribe to future events
400
+ const unsubscribe = runtime.events.subscribe({ names: ['notification.deep_link'] }, handleDeepLink);
401
+ return unsubscribe;
402
+ }, [runtime, instanceId]);
403
+ // Filter visible questions based on per-item triggerWhen
395
404
  // biome-ignore lint/correctness/useExhaustiveDependencies: renderTick is intentionally included to force re-evaluation when the runtime's mutable context changes (subscribed above via forceUpdate)
396
405
  const visibleQuestions = useMemo(() => config.actions.filter((q) => {
397
- // No showWhen = always visible
398
- if (!q.showWhen)
406
+ // No triggerWhen = always visible
407
+ if (!q.triggerWhen)
399
408
  return true;
400
409
  // Evaluate the decision strategy
401
- const result = runtime.evaluateSync(q.showWhen);
410
+ const result = runtime.evaluateSync(q.triggerWhen);
402
411
  return result.value;
403
412
  }), [config.actions, runtime, renderTick]);
404
413
  // NOTE: faq:question_revealed is now published by useNotifyWatcher in
@@ -504,12 +513,12 @@ export function FAQWidget({ config, runtime, instanceId }) {
504
513
  ...themeStyles[resolvedTheme].categoryHeader,
505
514
  };
506
515
  // Render a list of FAQ items
507
- const renderItems = (items) => items.map((q) => (_jsx(FAQItem, { item: q, isExpanded: expandedIds.has(q.config.id), onToggle: () => handleToggle(q.config.id), theme: resolvedTheme, feedbackConfig: feedbackConfig, feedbackValue: feedbackState.get(q.config.id), onFeedback: handleFeedback }, q.config.id)));
516
+ const renderItems = (items) => items.map((q, index) => (_jsx(FAQItem, { item: q, isExpanded: expandedIds.has(q.config.id), isHighlighted: highlightId === q.config.id, isLast: index === items.length - 1, onToggle: () => handleToggle(q.config.id), theme: resolvedTheme, feedbackConfig: feedbackConfig, feedbackValue: feedbackState.get(q.config.id), onFeedback: handleFeedback }, q.config.id)));
508
517
  // Empty state (no visible questions at all)
509
518
  if (visibleQuestions.length === 0) {
510
- return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: _jsx("div", { style: emptyStateStyle, children: "No FAQ questions available." }) }));
519
+ return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: _jsx("div", { style: emptyStateStyle, children: "You're all set for now! We'll surface answers here when they're relevant to what you're doing." }) }));
511
520
  }
512
- return (_jsxs("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: [config.searchable && (_jsx("div", { style: baseStyles.searchWrapper, children: _jsx("input", { type: "text", placeholder: "Search questions...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: searchInputStyle }) })), _jsx("div", { style: baseStyles.accordion, children: hasCategories
521
+ return (_jsxs("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: [config.searchable && (_jsxs("div", { style: baseStyles.searchWrapper, children: [_jsx("style", { children: `[data-adaptive-id="${instanceId}"] input::placeholder { color: var(--sc-content-search-color, inherit); opacity: 0.7; }` }), _jsx("input", { type: "text", placeholder: "Search questions...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: searchInputStyle })] })), _jsx("div", { style: baseStyles.accordion, children: hasCategories
513
522
  ? 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')))
514
523
  : renderItems(filteredQuestions) }), config.searchable && filteredQuestions.length === 0 && searchQuery && (_jsxs("div", { style: { ...baseStyles.noResults, ...themeStyles[resolvedTheme].emptyState }, children: ["No questions found matching \"", searchQuery, "\""] }))] }));
515
524
  }
package/dist/cdn.d.ts CHANGED
@@ -37,6 +37,7 @@ export declare const manifest: {
37
37
  name: string;
38
38
  description: string;
39
39
  icon: string;
40
+ subtitle: string;
40
41
  };
41
42
  }[];
42
43
  notifyWatchers: (props: Record<string, unknown>) => {
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;;;;;;;;;;;;;;;;;;;;2BAoC23pB,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtBr5pB,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;;;;;;;;;;;;;;;;;;;;2BAoCyjtB,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtBnltB,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;AAsBH,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACd,KAAK,iBAAiB,EAGvB,MAAM,SAAS,CAAC;AAoEjB,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,CAW1D;AAwFD,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAyVvE;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;AAsBH,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EACd,KAAK,iBAAiB,EAGvB,MAAM,SAAS,CAAC;AA6EjB,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,CAW1D;AAwFD,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAgXvE;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 Q&A cards with trigger, rationale,
7
7
  * and inline editing. Includes detection badges and hover-to-highlight.
8
8
  */
9
- import { ConditionStatusLine, DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EditorTextarea, EmptyState, GroupHeader, TriggerJourney, useShowWhenStatus, } from '@syntrologie/shared-editor-ui';
9
+ import { ConditionStatusLine, DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EditorTextarea, EmptyState, GroupHeader, TriggerJourney, useTriggerWhenStatus, } from '@syntrologie/shared-editor-ui';
10
10
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
11
11
  import { describeTrigger, summarizeFAQItem } 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,14 +40,23 @@ 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
  // ============================================================================
52
61
  // Helpers
53
62
  // ============================================================================
@@ -64,9 +73,9 @@ export function flattenItems(config) {
64
73
  key: String(i),
65
74
  index: i,
66
75
  summary: summarizeFAQItem(q),
67
- trigger: describeTrigger(q.showWhen),
76
+ trigger: describeTrigger(q.triggerWhen),
68
77
  rationale: q.rationale,
69
- firstAnchor: extractFirstAnchor(q.showWhen),
78
+ firstAnchor: extractFirstAnchor(q.triggerWhen),
70
79
  question: q,
71
80
  }));
72
81
  }
@@ -86,7 +95,7 @@ function useDetection(items, getCurrentRoute) {
86
95
  const map = new Map();
87
96
  const currentPath = getCurrentRoute();
88
97
  for (const item of itemsRef.current) {
89
- const targeting = extractTargetingInfo(item.question.showWhen);
98
+ const targeting = extractTargetingInfo(item.question.triggerWhen);
90
99
  // Check page match
91
100
  let pageMatch = true;
92
101
  if (targeting.pagePatterns.length > 0) {
@@ -175,12 +184,12 @@ export function FAQEditor({ config, onChange, editor }) {
175
184
  const totalQuestions = activeItems.length;
176
185
  const detectionMap = useDetection(allItems, editor.getCurrentRoute);
177
186
  const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
178
- // Live showWhen status for condition diagnostics
179
- const showWhenItems = useMemo(() => allItems.map((item) => ({
187
+ // Live triggerWhen status for condition diagnostics
188
+ const triggerWhenItems = useMemo(() => allItems.map((item) => ({
180
189
  id: item.key,
181
- showWhen: item.question.showWhen,
190
+ triggerWhen: item.question.triggerWhen,
182
191
  })), [allItems]);
183
- const showWhenStatuses = useShowWhenStatus(showWhenItems);
192
+ const triggerWhenStatuses = useTriggerWhenStatus(triggerWhenItems);
184
193
  const handleDismiss = useCallback((key) => {
185
194
  setDismissedKeys((prev) => {
186
195
  const next = new Set(prev);
@@ -200,10 +209,12 @@ export function FAQEditor({ config, onChange, editor }) {
200
209
  const handleCardBodyClick = useCallback((item) => {
201
210
  setEditingKey(item.key);
202
211
  }, []);
203
- const handleTriggerClick = useCallback((item) => {
204
- const pageUrl = extractFirstPage(item.question.showWhen);
212
+ const handleTriggerClick = useCallback(async (item) => {
213
+ const pageUrl = extractFirstPage(item.question.triggerWhen);
205
214
  if (pageUrl) {
206
- editor.navigateTo(pageUrl);
215
+ if (item.firstAnchor)
216
+ savePendingHighlight(item.firstAnchor);
217
+ await editor.navigateTo(pageUrl);
207
218
  }
208
219
  if (item.firstAnchor) {
209
220
  editor.highlightElement(item.firstAnchor);
@@ -237,6 +248,25 @@ export function FAQEditor({ config, onChange, editor }) {
237
248
  }
238
249
  editor.publish();
239
250
  }, [dismissedKeys, typedConfig, onChange, editor]);
251
+ const handleBadgeClick = useCallback(async (item) => {
252
+ const detection = detectionMap.get(item.key);
253
+ if (detection?.found && item.firstAnchor) {
254
+ editor.highlightElement(item.firstAnchor);
255
+ }
256
+ else {
257
+ const pageUrl = extractFirstPage(item.question.triggerWhen);
258
+ if (pageUrl) {
259
+ if (item.firstAnchor)
260
+ savePendingHighlight(item.firstAnchor);
261
+ await editor.navigateTo(pageUrl);
262
+ if (item.firstAnchor)
263
+ editor.highlightElement(item.firstAnchor);
264
+ }
265
+ else if (item.firstAnchor) {
266
+ editor.highlightElement(item.firstAnchor);
267
+ }
268
+ }
269
+ }, [editor, detectionMap]);
240
270
  const handleCardHover = useCallback((item) => {
241
271
  setHoveredKey(item.key);
242
272
  if (item.firstAnchor) {
@@ -254,7 +284,7 @@ export function FAQEditor({ config, onChange, editor }) {
254
284
  if (!q)
255
285
  return null;
256
286
  const item = allItems.find((it) => it.key === String(index));
257
- return (_jsxs("div", { className: "se-py-1", children: [item && item.trigger !== 'All pages' && (_jsxs("button", { type: "button", "data-trigger": true, className: "se-flex se-items-center se-gap-1 se-text-[11px] se-text-slate-grey-8 se-cursor-pointer se-mb-1 se-border-none se-bg-transparent se-p-0 se-text-left", onClick: () => handleTriggerClick(item), children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsx(EditorInput, { label: "Question", value: q.config.question, onChange: (e) => handleFieldChange(index, 'question', e.target.value) }), _jsx(EditorTextarea, { label: "Answer", value: getAnswerText(q.config.answer), onChange: (e) => handleFieldChange(index, 'answer', e.target.value) }), _jsx(EditorInput, { label: "Category", value: q.config.category || '', onChange: (e) => handleFieldChange(index, 'category', e.target.value || undefined), placeholder: "e.g., Billing, Account" }), _jsxs("div", { children: [_jsx("span", { className: "se-text-[11px] se-font-semibold se-text-slate-grey-7 se-mb-1 se-block", children: "AI Rationale" }), _jsx("div", { className: "se-p-2 se-rounded se-border se-border-dashed se-border-white/15 se-bg-white/[0.02] se-text-slate-grey-8 se-text-xs se-mb-2", children: q.rationale ? q.rationale.why : 'N/A' })] }), _jsx(TriggerJourney, { status: showWhenStatuses.get(String(index)) ?? null })] }));
287
+ return (_jsxs("div", { className: "se-py-1", children: [item && item.trigger !== 'All pages' && (_jsxs("button", { type: "button", "data-trigger": true, className: "se-flex se-items-center se-gap-1 se-text-[11px] se-text-slate-grey-8 se-cursor-pointer se-mb-1 se-border-none se-bg-transparent se-p-0 se-text-left", onClick: () => handleTriggerClick(item), children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsx(EditorInput, { label: "Question", value: q.config.question, onChange: (e) => handleFieldChange(index, 'question', e.target.value) }), _jsx(EditorTextarea, { label: "Answer", value: getAnswerText(q.config.answer), onChange: (e) => handleFieldChange(index, 'answer', e.target.value) }), _jsx(EditorInput, { label: "Category", value: q.config.category || '', onChange: (e) => handleFieldChange(index, 'category', e.target.value || undefined), placeholder: "e.g., Billing, Account" }), _jsxs("div", { children: [_jsx("span", { className: "se-text-[11px] se-font-semibold se-text-slate-grey-7 se-mb-1 se-block", children: "AI Rationale" }), _jsx("div", { className: "se-p-2 se-rounded se-border se-border-dashed se-border-white/15 se-bg-white/[0.02] se-text-slate-grey-8 se-text-xs se-mb-2", children: q.rationale ? q.rationale.why : 'N/A' })] }), _jsx(TriggerJourney, { status: triggerWhenStatuses.get(String(index)) ?? null })] }));
258
288
  };
259
289
  return (_jsxs(EditorLayout, { children: [_jsx(EditorHeader, { title: "Review Questions", subtitle: `${totalQuestions} question${totalQuestions !== 1 ? 's' : ''}${totalQuestions > 0 ? ` (${foundCount} found on this page)` : ''}`, onBack: () => editor.navigateHome() }), _jsx(EditorBody, { children: editingKey !== null ? (
260
290
  /* ---- Edit mode ---- */
@@ -272,10 +302,10 @@ export function FAQEditor({ 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 question", 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(ConditionStatusLine, { status: showWhenStatuses.get(item.key) ?? null })] }, item.key));
308
+ }, title: "Dismiss this question", 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(ConditionStatusLine, { 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
@@ -44,12 +44,13 @@ export declare const runtime: {
44
44
  name: string;
45
45
  description: string;
46
46
  icon: string;
47
+ subtitle: string;
47
48
  };
48
49
  }[];
49
50
  /**
50
51
  * Extract notify watcher entries from tile config props.
51
52
  * The runtime evaluates these continuously (even with drawer closed)
52
- * and publishes faq:question_revealed when showWhen transitions false → true.
53
+ * and publishes faq:question_revealed when triggerWhen transitions false → true.
53
54
  */
54
55
  notifyWatchers(props: Record<string, unknown>): {
55
56
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAOlB;;OAEG;;;;;;;;;;;IAGH;;OAEG;;;;;uBAsCwtoB,CAAC;0BAA8B,CAAC;;;;;;;;;IAzB3voB;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;CAiB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAOlB;;OAEG;;;;;;;;;;;IAGH;;OAEG;;;;;uBAuCk2rB,CAAC;0BAA8B,CAAC;;;;;;;;;;IAzBr4rB;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;CAiB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
package/dist/runtime.js CHANGED
@@ -36,21 +36,22 @@ export const runtime = {
36
36
  name: 'FAQ Accordion',
37
37
  description: 'Collapsible Q&A accordion with search, categories, and feedback',
38
38
  icon: '❓',
39
+ subtitle: 'Curated just for you.',
39
40
  },
40
41
  },
41
42
  ],
42
43
  /**
43
44
  * Extract notify watcher entries from tile config props.
44
45
  * The runtime evaluates these continuously (even with drawer closed)
45
- * and publishes faq:question_revealed when showWhen transitions false → true.
46
+ * and publishes faq:question_revealed when triggerWhen transitions false → true.
46
47
  */
47
48
  notifyWatchers(props) {
48
49
  const actions = (props.actions ?? []);
49
50
  return actions
50
- .filter((a) => a.notify && a.showWhen)
51
+ .filter((a) => a.notify && a.triggerWhen)
51
52
  .map((a) => ({
52
53
  id: `faq:${a.config.id}`,
53
- strategy: a.showWhen,
54
+ strategy: a.triggerWhen,
54
55
  eventName: 'faq:question_revealed',
55
56
  eventProps: {
56
57
  questionId: a.config.id,