@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.
- package/dist/FAQWidget.d.ts +4 -4
- package/dist/FAQWidget.d.ts.map +1 -1
- package/dist/FAQWidget.js +93 -84
- package/dist/cdn.d.ts +1 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +51 -21
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -3
- package/dist/schema.d.ts +1173 -127
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -8
- package/dist/summarize.d.ts +2 -2
- package/dist/summarize.d.ts.map +1 -1
- package/dist/summarize.js +5 -5
- package/dist/types.d.ts +4 -47
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +105 -2
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +5 -3
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +798 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +21 -1
- package/package.json +1 -1
package/dist/FAQWidget.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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 `
|
|
64
|
-
* - Parent evaluates
|
|
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;
|
package/dist/FAQWidget.d.ts.map
CHANGED
|
@@ -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
|
|
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
|
|
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 {
|
|
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: '
|
|
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: '
|
|
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
|
|
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: '
|
|
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
|
|
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: '
|
|
118
|
+
fontSize: 'var(--sc-content-category-font-size, 12px)',
|
|
116
119
|
fontWeight: 700,
|
|
117
120
|
textTransform: 'uppercase',
|
|
118
121
|
letterSpacing: '0.05em',
|
|
119
|
-
padding: '
|
|
120
|
-
marginTop: '
|
|
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:
|
|
159
|
-
color:
|
|
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:
|
|
168
|
-
border:
|
|
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:
|
|
176
|
+
color: 'var(--sc-content-text-color)',
|
|
176
177
|
},
|
|
177
178
|
questionHover: {
|
|
178
|
-
backgroundColor:
|
|
179
|
+
backgroundColor: 'var(--sc-content-background-hover)',
|
|
179
180
|
},
|
|
180
181
|
answer: {
|
|
181
|
-
color:
|
|
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:
|
|
200
|
-
color:
|
|
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:
|
|
209
|
-
border:
|
|
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.
|
|
211
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
213
212
|
},
|
|
214
213
|
question: {
|
|
215
214
|
backgroundColor: 'transparent',
|
|
216
|
-
color:
|
|
215
|
+
color: 'var(--sc-content-text-color)',
|
|
217
216
|
},
|
|
218
217
|
questionHover: {
|
|
219
|
-
backgroundColor:
|
|
218
|
+
backgroundColor: 'var(--sc-content-background-hover)',
|
|
220
219
|
},
|
|
221
220
|
answer: {
|
|
222
|
-
color:
|
|
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(
|
|
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: '\
|
|
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 `
|
|
284
|
-
* - Parent evaluates
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
398
|
-
if (!q.
|
|
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.
|
|
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: "
|
|
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 && (
|
|
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
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;;;;;;;;;;;;;;;;;;;;
|
|
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"}
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -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;
|
|
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,
|
|
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(
|
|
20
|
-
if (!
|
|
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
|
|
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(
|
|
44
|
-
const info = extractTargetingInfo(
|
|
43
|
+
function extractFirstPage(triggerWhen) {
|
|
44
|
+
const info = extractTargetingInfo(triggerWhen);
|
|
45
45
|
return info.pagePatterns[0] || null;
|
|
46
46
|
}
|
|
47
|
-
function extractFirstAnchor(
|
|
48
|
-
const info = extractTargetingInfo(
|
|
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.
|
|
76
|
+
trigger: describeTrigger(q.triggerWhen),
|
|
68
77
|
rationale: q.rationale,
|
|
69
|
-
firstAnchor: extractFirstAnchor(q.
|
|
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.
|
|
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
|
|
179
|
-
const
|
|
187
|
+
// Live triggerWhen status for condition diagnostics
|
|
188
|
+
const triggerWhenItems = useMemo(() => allItems.map((item) => ({
|
|
180
189
|
id: item.key,
|
|
181
|
-
|
|
190
|
+
triggerWhen: item.question.triggerWhen,
|
|
182
191
|
})), [allItems]);
|
|
183
|
-
const
|
|
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.
|
|
212
|
+
const handleTriggerClick = useCallback(async (item) => {
|
|
213
|
+
const pageUrl = extractFirstPage(item.question.triggerWhen);
|
|
205
214
|
if (pageUrl) {
|
|
206
|
-
|
|
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:
|
|
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:
|
|
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
|
|
53
|
+
* and publishes faq:question_revealed when triggerWhen transitions false → true.
|
|
53
54
|
*/
|
|
54
55
|
notifyWatchers(props: Record<string, unknown>): {
|
|
55
56
|
id: string;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -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;;;;;
|
|
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
|
|
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.
|
|
51
|
+
.filter((a) => a.notify && a.triggerWhen)
|
|
51
52
|
.map((a) => ({
|
|
52
53
|
id: `faq:${a.config.id}`,
|
|
53
|
-
strategy: a.
|
|
54
|
+
strategy: a.triggerWhen,
|
|
54
55
|
eventName: 'faq:question_revealed',
|
|
55
56
|
eventProps: {
|
|
56
57
|
questionId: a.config.id,
|