@syntrologie/adapt-faq 2.1.0-canary.9 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FAQWidget.d.ts +26 -0
- package/dist/FAQWidget.d.ts.map +1 -1
- package/dist/FAQWidget.js +155 -54
- package/dist/cdn.d.ts +27 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +35 -4
- package/dist/editor.d.ts +12 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +256 -321
- package/dist/executors.d.ts +1 -1
- package/dist/executors.d.ts.map +1 -1
- package/dist/executors.js +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/schema.d.ts +136 -136
- package/dist/summarize.d.ts +14 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/summarize.js +62 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -1
- package/package.json +7 -2
package/dist/FAQWidget.d.ts
CHANGED
|
@@ -18,11 +18,37 @@ export interface FAQWidgetRuntime {
|
|
|
18
18
|
};
|
|
19
19
|
events: {
|
|
20
20
|
publish: (name: string, props?: Record<string, unknown>) => void;
|
|
21
|
+
subscribe?: (filterOrCallback: {
|
|
22
|
+
names?: string[];
|
|
23
|
+
patterns?: string[];
|
|
24
|
+
sources?: string[];
|
|
25
|
+
} | ((event: {
|
|
26
|
+
name: string;
|
|
27
|
+
props?: Record<string, unknown>;
|
|
28
|
+
ts: number;
|
|
29
|
+
}) => void), maybeCallback?: (event: {
|
|
30
|
+
name: string;
|
|
31
|
+
props?: Record<string, unknown>;
|
|
32
|
+
ts: number;
|
|
33
|
+
}) => void) => () => void;
|
|
34
|
+
getRecent?: (filter?: {
|
|
35
|
+
names?: string[];
|
|
36
|
+
patterns?: string[];
|
|
37
|
+
}, limit?: number) => Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
props?: Record<string, unknown>;
|
|
40
|
+
ts: number;
|
|
41
|
+
}>;
|
|
21
42
|
};
|
|
22
43
|
state?: {
|
|
23
44
|
get: (key: string) => unknown;
|
|
24
45
|
set: (key: string, value: unknown) => void;
|
|
25
46
|
};
|
|
47
|
+
/** Event accumulator for reactive showWhen re-evaluation */
|
|
48
|
+
accumulator?: {
|
|
49
|
+
subscribe: (callback: () => void) => () => void;
|
|
50
|
+
register: (key: string, predicate: (event: any) => boolean) => void;
|
|
51
|
+
};
|
|
26
52
|
}
|
|
27
53
|
interface FAQWidgetProps {
|
|
28
54
|
config: FAQConfig;
|
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;
|
|
1
|
+
{"version":3,"file":"FAQWidget.d.ts","sourceRoot":"","sources":["../src/FAQWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAEV,SAAS,EAIT,gBAAgB,EACjB,MAAM,SAAS,CAAC;AA0CjB,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;AA4SD;;;;;;;;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"}
|
package/dist/FAQWidget.js
CHANGED
|
@@ -8,7 +8,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
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, slateGrey, purple } from '@syntro/design-system/tokens';
|
|
11
12
|
import React, { useEffect, useReducer, useMemo, useCallback, useState } from 'react';
|
|
13
|
+
import { createRoot } from 'react-dom/client';
|
|
12
14
|
// ============================================================================
|
|
13
15
|
// Helpers
|
|
14
16
|
// ============================================================================
|
|
@@ -152,84 +154,84 @@ const baseStyles = {
|
|
|
152
154
|
const themeStyles = {
|
|
153
155
|
light: {
|
|
154
156
|
container: {
|
|
155
|
-
backgroundColor:
|
|
156
|
-
color:
|
|
157
|
+
backgroundColor: base.white,
|
|
158
|
+
color: slateGrey[1],
|
|
157
159
|
},
|
|
158
160
|
searchInput: {
|
|
159
|
-
backgroundColor:
|
|
160
|
-
border:
|
|
161
|
-
color:
|
|
161
|
+
backgroundColor: slateGrey[12],
|
|
162
|
+
border: `1px solid ${slateGrey[11]}`,
|
|
163
|
+
color: slateGrey[1],
|
|
162
164
|
},
|
|
163
165
|
item: {
|
|
164
|
-
backgroundColor:
|
|
165
|
-
border:
|
|
166
|
+
backgroundColor: slateGrey[12],
|
|
167
|
+
border: `1px solid ${slateGrey[11]}`,
|
|
166
168
|
},
|
|
167
169
|
itemExpanded: {
|
|
168
170
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
|
|
169
171
|
},
|
|
170
172
|
question: {
|
|
171
173
|
backgroundColor: 'transparent',
|
|
172
|
-
color:
|
|
174
|
+
color: slateGrey[1],
|
|
173
175
|
},
|
|
174
176
|
questionHover: {
|
|
175
|
-
backgroundColor:
|
|
177
|
+
backgroundColor: slateGrey[12],
|
|
176
178
|
},
|
|
177
179
|
answer: {
|
|
178
|
-
color:
|
|
180
|
+
color: slateGrey[6],
|
|
179
181
|
},
|
|
180
182
|
category: {
|
|
181
|
-
backgroundColor:
|
|
182
|
-
color:
|
|
183
|
+
backgroundColor: purple[8],
|
|
184
|
+
color: purple[2],
|
|
183
185
|
},
|
|
184
186
|
categoryHeader: {
|
|
185
|
-
color:
|
|
187
|
+
color: slateGrey[7],
|
|
186
188
|
},
|
|
187
189
|
emptyState: {
|
|
188
|
-
color:
|
|
190
|
+
color: slateGrey[8],
|
|
189
191
|
},
|
|
190
192
|
feedbackPrompt: {
|
|
191
|
-
color:
|
|
193
|
+
color: slateGrey[7],
|
|
192
194
|
},
|
|
193
195
|
},
|
|
194
196
|
dark: {
|
|
195
197
|
container: {
|
|
196
|
-
backgroundColor:
|
|
197
|
-
color:
|
|
198
|
+
backgroundColor: slateGrey[1],
|
|
199
|
+
color: slateGrey[12],
|
|
198
200
|
},
|
|
199
201
|
searchInput: {
|
|
200
|
-
backgroundColor:
|
|
201
|
-
border:
|
|
202
|
-
color:
|
|
202
|
+
backgroundColor: slateGrey[3],
|
|
203
|
+
border: `1px solid ${slateGrey[5]}`,
|
|
204
|
+
color: slateGrey[12],
|
|
203
205
|
},
|
|
204
206
|
item: {
|
|
205
|
-
backgroundColor:
|
|
206
|
-
border:
|
|
207
|
+
backgroundColor: slateGrey[3],
|
|
208
|
+
border: `1px solid ${slateGrey[5]}`,
|
|
207
209
|
},
|
|
208
210
|
itemExpanded: {
|
|
209
211
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
|
|
210
212
|
},
|
|
211
213
|
question: {
|
|
212
214
|
backgroundColor: 'transparent',
|
|
213
|
-
color:
|
|
215
|
+
color: slateGrey[12],
|
|
214
216
|
},
|
|
215
217
|
questionHover: {
|
|
216
|
-
backgroundColor:
|
|
218
|
+
backgroundColor: slateGrey[5],
|
|
217
219
|
},
|
|
218
220
|
answer: {
|
|
219
|
-
color:
|
|
221
|
+
color: slateGrey[8],
|
|
220
222
|
},
|
|
221
223
|
category: {
|
|
222
|
-
backgroundColor:
|
|
223
|
-
color:
|
|
224
|
+
backgroundColor: purple[0],
|
|
225
|
+
color: purple[6],
|
|
224
226
|
},
|
|
225
227
|
categoryHeader: {
|
|
226
|
-
color:
|
|
228
|
+
color: slateGrey[8],
|
|
227
229
|
},
|
|
228
230
|
emptyState: {
|
|
229
|
-
color:
|
|
231
|
+
color: slateGrey[7],
|
|
230
232
|
},
|
|
231
233
|
feedbackPrompt: {
|
|
232
|
-
color:
|
|
234
|
+
color: slateGrey[8],
|
|
233
235
|
},
|
|
234
236
|
},
|
|
235
237
|
};
|
|
@@ -282,8 +284,9 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
|
|
|
282
284
|
* - Parent manages expand state and re-rendering on context changes
|
|
283
285
|
*/
|
|
284
286
|
export function FAQWidget({ config, runtime, instanceId }) {
|
|
285
|
-
// Force re-render when context changes
|
|
286
|
-
|
|
287
|
+
// Force re-render when context/accumulator changes.
|
|
288
|
+
// renderTick is used as a useMemo dependency to invalidate cached showWhen evaluations.
|
|
289
|
+
const [renderTick, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
287
290
|
// Track expanded question IDs
|
|
288
291
|
const [expandedIds, setExpandedIds] = useState(new Set());
|
|
289
292
|
// Search query state
|
|
@@ -299,6 +302,94 @@ export function FAQWidget({ config, runtime, instanceId }) {
|
|
|
299
302
|
});
|
|
300
303
|
return unsubscribe;
|
|
301
304
|
}, [runtime.context]);
|
|
305
|
+
// Subscribe to accumulator changes for event_count-based showWhen
|
|
306
|
+
useEffect(() => {
|
|
307
|
+
if (!runtime.accumulator?.subscribe)
|
|
308
|
+
return;
|
|
309
|
+
return runtime.accumulator.subscribe(() => {
|
|
310
|
+
forceUpdate();
|
|
311
|
+
});
|
|
312
|
+
}, [runtime.accumulator]);
|
|
313
|
+
// Register accumulator predicates from scope config
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
if (!config.scope || !runtime.accumulator?.register)
|
|
316
|
+
return;
|
|
317
|
+
const { events: eventNames, urlContains, props: propFilters } = config.scope;
|
|
318
|
+
// Scan showWhen conditions for event_count keys
|
|
319
|
+
const keys = new Set();
|
|
320
|
+
for (const action of config.actions) {
|
|
321
|
+
if (action.showWhen?.type === 'rules') {
|
|
322
|
+
for (const rule of action.showWhen.rules) {
|
|
323
|
+
for (const cond of rule.conditions) {
|
|
324
|
+
if (cond.type === 'event_count' && cond.key) {
|
|
325
|
+
keys.add(cond.key);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
for (const key of keys) {
|
|
332
|
+
runtime.accumulator.register(key, (event) => {
|
|
333
|
+
if (!eventNames.includes(event.name))
|
|
334
|
+
return false;
|
|
335
|
+
if (urlContains) {
|
|
336
|
+
const pathname = String(event.props?.pathname ?? '');
|
|
337
|
+
if (!pathname.includes(urlContains))
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
if (propFilters) {
|
|
341
|
+
for (const [k, v] of Object.entries(propFilters)) {
|
|
342
|
+
if (event.props?.[k] !== v)
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}, [config.scope, config.actions, runtime.accumulator]);
|
|
350
|
+
// Subscribe to faq:open:* events from overlay CTA clicks
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (!runtime.events.subscribe)
|
|
353
|
+
return;
|
|
354
|
+
// Check EventBus history for pending faq:open events
|
|
355
|
+
// (may have fired before this widget mounted, e.g. when canvas was closed)
|
|
356
|
+
if (runtime.events.getRecent) {
|
|
357
|
+
const recentEvents = runtime.events.getRecent({ patterns: ['^action\\.tooltip_cta_clicked$', '^action\\.modal_cta_clicked$'] }, 10);
|
|
358
|
+
const pendingEvent = recentEvents
|
|
359
|
+
.filter((e) => {
|
|
360
|
+
const actionId = e.props?.actionId;
|
|
361
|
+
return typeof actionId === 'string' && actionId.startsWith('faq:open:');
|
|
362
|
+
})
|
|
363
|
+
.pop(); // Most recent
|
|
364
|
+
if (pendingEvent && Date.now() - pendingEvent.ts < 10000) {
|
|
365
|
+
const questionId = pendingEvent.props.actionId.replace('faq:open:', '');
|
|
366
|
+
setExpandedIds(new Set([questionId]));
|
|
367
|
+
// Scroll into view after render
|
|
368
|
+
requestAnimationFrame(() => {
|
|
369
|
+
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
370
|
+
if (el)
|
|
371
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Subscribe to future CTA events
|
|
376
|
+
const unsubscribe = runtime.events.subscribe({ patterns: ['^action\\.tooltip_cta_clicked$', '^action\\.modal_cta_clicked$'] }, (event) => {
|
|
377
|
+
const actionId = event.props?.actionId;
|
|
378
|
+
if (typeof actionId !== 'string' || !actionId.startsWith('faq:open:'))
|
|
379
|
+
return;
|
|
380
|
+
const questionId = actionId.replace('faq:open:', '');
|
|
381
|
+
setExpandedIds(new Set([questionId]));
|
|
382
|
+
// Scroll the question into view
|
|
383
|
+
requestAnimationFrame(() => {
|
|
384
|
+
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
385
|
+
if (el)
|
|
386
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
387
|
+
});
|
|
388
|
+
// Request canvas open (for future tile-based FAQ rendering)
|
|
389
|
+
runtime.events.publish('canvas.requestOpen');
|
|
390
|
+
});
|
|
391
|
+
return unsubscribe;
|
|
392
|
+
}, [runtime]);
|
|
302
393
|
// Filter visible questions based on per-item showWhen
|
|
303
394
|
const visibleQuestions = useMemo(() => config.actions.filter((q) => {
|
|
304
395
|
// No showWhen = always visible
|
|
@@ -307,7 +398,11 @@ export function FAQWidget({ config, runtime, instanceId }) {
|
|
|
307
398
|
// Evaluate the decision strategy
|
|
308
399
|
const result = runtime.evaluateSync(q.showWhen);
|
|
309
400
|
return result.value;
|
|
310
|
-
}),
|
|
401
|
+
}),
|
|
402
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
403
|
+
[config.actions, runtime, renderTick]);
|
|
404
|
+
// NOTE: faq:question_revealed is now published by useNotifyWatcher in
|
|
405
|
+
// ShadowCanvasOverlay (always mounted), so it fires even with drawer closed.
|
|
311
406
|
// Apply priority ordering
|
|
312
407
|
const orderedQuestions = useMemo(() => {
|
|
313
408
|
if (config.ordering === 'priority') {
|
|
@@ -339,9 +434,7 @@ export function FAQWidget({ config, runtime, instanceId }) {
|
|
|
339
434
|
return groups;
|
|
340
435
|
}, [filteredQuestions]);
|
|
341
436
|
// Check if any items have categories
|
|
342
|
-
const hasCategories = useMemo(() =>
|
|
343
|
-
return filteredQuestions.some((q) => q.config.category);
|
|
344
|
-
}, [filteredQuestions]);
|
|
437
|
+
const hasCategories = useMemo(() => filteredQuestions.some((q) => q.config.category), [filteredQuestions]);
|
|
345
438
|
// Resolve theme (auto -> detect system preference)
|
|
346
439
|
const resolvedTheme = useMemo(() => {
|
|
347
440
|
if (config.theme !== 'auto')
|
|
@@ -428,30 +521,38 @@ export function FAQWidget({ config, runtime, instanceId }) {
|
|
|
428
521
|
*/
|
|
429
522
|
export const FAQMountableWidget = {
|
|
430
523
|
mount(container, config) {
|
|
431
|
-
|
|
432
|
-
// In practice, the runtime handles React rendering
|
|
433
|
-
const { runtime, instanceId: _instanceId = 'faq-widget', ...faqConfig } = config || {
|
|
524
|
+
const { runtime, instanceId = 'faq-widget', ...faqConfig } = config || {
|
|
434
525
|
expandBehavior: 'single',
|
|
435
526
|
searchable: false,
|
|
436
527
|
theme: 'auto',
|
|
437
528
|
actions: [],
|
|
438
529
|
};
|
|
439
|
-
//
|
|
440
|
-
if (
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
`)
|
|
451
|
-
.join('')}
|
|
452
|
-
</div>
|
|
453
|
-
`;
|
|
530
|
+
// React rendering when runtime + ReactDOM are available
|
|
531
|
+
if (runtime && typeof createRoot === 'function') {
|
|
532
|
+
const root = createRoot(container);
|
|
533
|
+
root.render(React.createElement(FAQWidget, {
|
|
534
|
+
config: faqConfig,
|
|
535
|
+
runtime: runtime,
|
|
536
|
+
instanceId,
|
|
537
|
+
}));
|
|
538
|
+
return () => {
|
|
539
|
+
root.unmount();
|
|
540
|
+
};
|
|
454
541
|
}
|
|
542
|
+
// HTML fallback when React is not available
|
|
543
|
+
const questions = faqConfig.actions || [];
|
|
544
|
+
container.innerHTML = `
|
|
545
|
+
<div style="font-family: system-ui; max-width: 800px;">
|
|
546
|
+
${questions
|
|
547
|
+
.map((q) => `
|
|
548
|
+
<div style="margin-bottom: 8px; padding: 16px; background: ${slateGrey[12]}; border-radius: 8px;">
|
|
549
|
+
<strong>${q.config.question}</strong>
|
|
550
|
+
<p style="margin-top: 8px; color: ${slateGrey[6]};">${getAnswerText(q.config.answer)}</p>
|
|
551
|
+
</div>
|
|
552
|
+
`)
|
|
553
|
+
.join('')}
|
|
554
|
+
</div>
|
|
555
|
+
`;
|
|
455
556
|
return () => {
|
|
456
557
|
container.innerHTML = '';
|
|
457
558
|
};
|
package/dist/cdn.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
5
|
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
6
|
*/
|
|
7
|
+
import FAQEditor from './editor';
|
|
7
8
|
/**
|
|
8
9
|
* App manifest for registry registration.
|
|
9
10
|
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
@@ -38,6 +39,32 @@ export declare const manifest: {
|
|
|
38
39
|
icon: string;
|
|
39
40
|
};
|
|
40
41
|
}[];
|
|
42
|
+
/**
|
|
43
|
+
* Extract notify watcher entries from tile config props.
|
|
44
|
+
* The runtime evaluates these continuously (even with drawer closed)
|
|
45
|
+
* and publishes faq:question_revealed when showWhen transitions false → true.
|
|
46
|
+
*/
|
|
47
|
+
notifyWatchers(props: Record<string, unknown>): {
|
|
48
|
+
id: string;
|
|
49
|
+
strategy: import("./types").DecisionStrategy<boolean>;
|
|
50
|
+
eventName: string;
|
|
51
|
+
eventProps: {
|
|
52
|
+
questionId: string;
|
|
53
|
+
question: string;
|
|
54
|
+
title: string | undefined;
|
|
55
|
+
body: string | undefined;
|
|
56
|
+
icon: string | undefined;
|
|
57
|
+
};
|
|
58
|
+
}[];
|
|
59
|
+
};
|
|
60
|
+
editor: {
|
|
61
|
+
component: typeof FAQEditor;
|
|
62
|
+
panel: {
|
|
63
|
+
title: string;
|
|
64
|
+
icon: string;
|
|
65
|
+
description: string;
|
|
66
|
+
};
|
|
67
|
+
getActionLabel(action: Record<string, unknown>): string;
|
|
41
68
|
};
|
|
42
69
|
metadata: {
|
|
43
70
|
isBuiltIn: boolean;
|
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;
|
|
1
|
+
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAIlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;2BAwD2ynB,CAAC;8BAA8B,CAAC;;;;;;;;;QAhD51nB;;;;WAIG;8BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;+BAqBtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/cdn.js
CHANGED
|
@@ -4,19 +4,50 @@
|
|
|
4
4
|
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
5
|
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
6
|
*/
|
|
7
|
+
import FAQEditor, { editorPanel } from './editor';
|
|
7
8
|
import { runtime } from './runtime';
|
|
8
9
|
/**
|
|
9
10
|
* App manifest for registry registration.
|
|
10
11
|
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
11
12
|
*/
|
|
12
13
|
export const manifest = {
|
|
13
|
-
id: 'faq',
|
|
14
|
+
id: 'adaptive-faq',
|
|
14
15
|
version: runtime.version,
|
|
15
16
|
name: runtime.name,
|
|
16
17
|
description: runtime.description,
|
|
17
18
|
runtime: {
|
|
18
19
|
actions: runtime.executors,
|
|
19
20
|
widgets: runtime.widgets,
|
|
21
|
+
/**
|
|
22
|
+
* Extract notify watcher entries from tile config props.
|
|
23
|
+
* The runtime evaluates these continuously (even with drawer closed)
|
|
24
|
+
* and publishes faq:question_revealed when showWhen transitions false → true.
|
|
25
|
+
*/
|
|
26
|
+
notifyWatchers(props) {
|
|
27
|
+
const actions = (props.actions ?? []);
|
|
28
|
+
return actions
|
|
29
|
+
.filter((a) => a.notify && a.showWhen)
|
|
30
|
+
.map((a) => ({
|
|
31
|
+
id: `faq:${a.config.id}`,
|
|
32
|
+
strategy: a.showWhen,
|
|
33
|
+
eventName: 'faq:question_revealed',
|
|
34
|
+
eventProps: {
|
|
35
|
+
questionId: a.config.id,
|
|
36
|
+
question: a.config.question,
|
|
37
|
+
title: a.notify.title,
|
|
38
|
+
body: a.notify.body,
|
|
39
|
+
icon: a.notify.icon,
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
editor: {
|
|
45
|
+
component: FAQEditor,
|
|
46
|
+
panel: editorPanel,
|
|
47
|
+
getActionLabel(action) {
|
|
48
|
+
const config = action.config || {};
|
|
49
|
+
return config.question || action.kind || 'faq:update';
|
|
50
|
+
},
|
|
20
51
|
},
|
|
21
52
|
metadata: {
|
|
22
53
|
isBuiltIn: false,
|
|
@@ -27,9 +58,9 @@ export const manifest = {
|
|
|
27
58
|
* This happens when loaded via script tag (UMD).
|
|
28
59
|
*/
|
|
29
60
|
if (typeof window !== 'undefined') {
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
61
|
+
const registry = window.SynOS?.appRegistry;
|
|
62
|
+
if (registry && typeof registry.register === 'function') {
|
|
63
|
+
registry.register(manifest);
|
|
33
64
|
}
|
|
34
65
|
}
|
|
35
66
|
export default manifest;
|
package/dist/editor.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Adaptive FAQ - Editor Component
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Review & tweak editor for AI-generated FAQ question decisions.
|
|
5
|
+
* Displays a scannable list of Q&A cards with trigger, rationale,
|
|
6
|
+
* and inline editing. Includes detection badges and hover-to-highlight.
|
|
5
7
|
*/
|
|
6
|
-
import type
|
|
8
|
+
import { type EditorPanelProps } from './types';
|
|
7
9
|
export declare function FAQEditor({ config, onChange, editor }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
8
10
|
/**
|
|
9
11
|
* Editor panel configuration for the app registry.
|
|
@@ -13,5 +15,13 @@ export declare const editorPanel: {
|
|
|
13
15
|
icon: string;
|
|
14
16
|
description: string;
|
|
15
17
|
};
|
|
18
|
+
export declare const editor: {
|
|
19
|
+
panel: {
|
|
20
|
+
title: string;
|
|
21
|
+
icon: string;
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
24
|
+
component: typeof FAQEditor;
|
|
25
|
+
};
|
|
16
26
|
export default FAQEditor;
|
|
17
27
|
//# sourceMappingURL=editor.d.ts.map
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsBH,OAAO,EAKL,KAAK,gBAAgB,EAEtB,MAAM,SAAS,CAAC;AAiLjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAkUvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAGlB,CAAC;AAEF,eAAe,SAAS,CAAC"}
|