@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.
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"FAQWidget.d.ts","sourceRoot":"","sources":["../src/FAQWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,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;QAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,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;CACvF;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,2CAoOxE;AAMD;;GAEG;AACH,eAAO,MAAM,kBAAkB;qBAEhB,WAAW,WACb,SAAS,GAAG;QAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAuC3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
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: '#ffffff',
156
- color: '#111827',
157
+ backgroundColor: base.white,
158
+ color: slateGrey[1],
157
159
  },
158
160
  searchInput: {
159
- backgroundColor: '#f9fafb',
160
- border: '1px solid #e5e7eb',
161
- color: '#111827',
161
+ backgroundColor: slateGrey[12],
162
+ border: `1px solid ${slateGrey[11]}`,
163
+ color: slateGrey[1],
162
164
  },
163
165
  item: {
164
- backgroundColor: '#f9fafb',
165
- border: '1px solid #e5e7eb',
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: '#111827',
174
+ color: slateGrey[1],
173
175
  },
174
176
  questionHover: {
175
- backgroundColor: '#f3f4f6',
177
+ backgroundColor: slateGrey[12],
176
178
  },
177
179
  answer: {
178
- color: '#4b5563',
180
+ color: slateGrey[6],
179
181
  },
180
182
  category: {
181
- backgroundColor: '#e0e7ff',
182
- color: '#4338ca',
183
+ backgroundColor: purple[8],
184
+ color: purple[2],
183
185
  },
184
186
  categoryHeader: {
185
- color: '#6b7280',
187
+ color: slateGrey[7],
186
188
  },
187
189
  emptyState: {
188
- color: '#9ca3af',
190
+ color: slateGrey[8],
189
191
  },
190
192
  feedbackPrompt: {
191
- color: '#6b7280',
193
+ color: slateGrey[7],
192
194
  },
193
195
  },
194
196
  dark: {
195
197
  container: {
196
- backgroundColor: '#111827',
197
- color: '#f9fafb',
198
+ backgroundColor: slateGrey[1],
199
+ color: slateGrey[12],
198
200
  },
199
201
  searchInput: {
200
- backgroundColor: '#1f2937',
201
- border: '1px solid #374151',
202
- color: '#f9fafb',
202
+ backgroundColor: slateGrey[3],
203
+ border: `1px solid ${slateGrey[5]}`,
204
+ color: slateGrey[12],
203
205
  },
204
206
  item: {
205
- backgroundColor: '#1f2937',
206
- border: '1px solid #374151',
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: '#f9fafb',
215
+ color: slateGrey[12],
214
216
  },
215
217
  questionHover: {
216
- backgroundColor: '#374151',
218
+ backgroundColor: slateGrey[5],
217
219
  },
218
220
  answer: {
219
- color: '#9ca3af',
221
+ color: slateGrey[8],
220
222
  },
221
223
  category: {
222
- backgroundColor: '#312e81',
223
- color: '#a5b4fc',
224
+ backgroundColor: purple[0],
225
+ color: purple[6],
224
226
  },
225
227
  categoryHeader: {
226
- color: '#9ca3af',
228
+ color: slateGrey[8],
227
229
  },
228
230
  emptyState: {
229
- color: '#6b7280',
231
+ color: slateGrey[7],
230
232
  },
231
233
  feedbackPrompt: {
232
- color: '#9ca3af',
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
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
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
- }), [config.actions, runtime]);
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
- // This is a simplified mount for non-React environments
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
- // Create simple HTML fallback if no runtime
440
- if (!runtime) {
441
- const questions = faqConfig.actions || [];
442
- container.innerHTML = `
443
- <div style="font-family: system-ui; max-width: 800px;">
444
- ${questions
445
- .map((q) => `
446
- <div style="margin-bottom: 8px; padding: 16px; background: #f9fafb; border-radius: 8px;">
447
- <strong>${q.config.question}</strong>
448
- <p style="margin-top: 8px; color: #4b5563;">${getAnswerText(q.config.answer)}</p>
449
- </div>
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;AAIH;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;2BA0B6ngB,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;CAdjrgB,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;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 globalRegistry = window.__SYNOS_APP_REGISTRY__;
31
- if (globalRegistry && typeof globalRegistry.register === 'function') {
32
- globalRegistry.register(manifest);
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
- * Visual editor panel for configuring FAQ questions.
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 { EditorPanelProps } from './types';
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
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,SAAS,CAAC;AAyNjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAgUvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAe,SAAS,CAAC"}
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"}