@syntrologie/adapt-faq 0.0.0-semantically-released → 2.0.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/editor.js CHANGED
@@ -1,6 +1,147 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * Adaptive FAQ - Editor Component
4
+ *
5
+ * Review & tweak editor for AI-generated FAQ question decisions.
6
+ * Displays a scannable list of Q&A cards with trigger, rationale,
7
+ * and inline editing. Includes detection badges and hover-to-highlight.
8
+ */
9
+ import { useState, useCallback, useEffect, useRef } from 'react';
10
+ import { summarizeFAQItem, describeTrigger } from './summarize';
11
+ import { isOwnAction, } from './types';
12
+ function isRuleStrategy(s) {
13
+ return (typeof s === 'object' &&
14
+ s !== null &&
15
+ s.type === 'rules' &&
16
+ Array.isArray(s.rules));
17
+ }
18
+ function extractTargetingInfo(showWhen) {
19
+ if (!showWhen || !isRuleStrategy(showWhen)) {
20
+ return { pagePatterns: [], anchorSelectors: [], hasTargeting: false };
21
+ }
22
+ const pagePatterns = new Set();
23
+ const anchorSelectors = new Set();
24
+ for (const rule of showWhen.rules) {
25
+ for (const cond of rule.conditions) {
26
+ const c = cond;
27
+ if (c.type === 'page_url' && typeof c.url === 'string') {
28
+ pagePatterns.add(c.url);
29
+ }
30
+ else if (c.type === 'anchor_visible' && typeof c.anchorId === 'string') {
31
+ anchorSelectors.add(c.anchorId);
32
+ }
33
+ }
34
+ }
35
+ const hasTargeting = pagePatterns.size > 0 || anchorSelectors.size > 0;
36
+ return {
37
+ pagePatterns: [...pagePatterns],
38
+ anchorSelectors: [...anchorSelectors],
39
+ hasTargeting,
40
+ };
41
+ }
42
+ function extractFirstPage(showWhen) {
43
+ const info = extractTargetingInfo(showWhen);
44
+ return info.pagePatterns[0] || null;
45
+ }
46
+ function extractFirstAnchor(showWhen) {
47
+ const info = extractTargetingInfo(showWhen);
48
+ return info.anchorSelectors[0] || null;
49
+ }
2
50
  // ============================================================================
3
- // Editor Styles
51
+ // Helpers
52
+ // ============================================================================
53
+ function getAnswerText(answer) {
54
+ if (typeof answer === 'string')
55
+ return answer;
56
+ if (answer.type === 'rich')
57
+ return answer.html;
58
+ return answer.content;
59
+ }
60
+ function flattenItems(config) {
61
+ const actions = (config.actions || []).filter(isOwnAction);
62
+ return actions.map((q, i) => ({
63
+ key: String(i),
64
+ index: i,
65
+ summary: summarizeFAQItem(q),
66
+ trigger: describeTrigger(q.showWhen),
67
+ rationale: q.rationale,
68
+ firstAnchor: extractFirstAnchor(q.showWhen),
69
+ question: q,
70
+ }));
71
+ }
72
+ function filterConfig(config, dismissedKeys) {
73
+ const ownActions = (config.actions || []).filter(isOwnAction);
74
+ return {
75
+ ...config,
76
+ actions: ownActions.filter((_, i) => !dismissedKeys.has(String(i))),
77
+ };
78
+ }
79
+ function useDetection(items, getCurrentRoute) {
80
+ const [detectionMap, setDetectionMap] = useState(new Map());
81
+ const itemsRef = useRef(items);
82
+ itemsRef.current = items;
83
+ useEffect(() => {
84
+ const runDetection = () => {
85
+ const map = new Map();
86
+ const currentPath = getCurrentRoute();
87
+ for (const item of itemsRef.current) {
88
+ const targeting = extractTargetingInfo(item.question.showWhen);
89
+ // Check page match
90
+ let pageMatch = true;
91
+ if (targeting.pagePatterns.length > 0) {
92
+ pageMatch = targeting.pagePatterns.some((pattern) => {
93
+ const regex = new RegExp(`^${pattern.replace(/\*\*/g, '.*').replace(/(?<!\.)(\*)/g, '[^/]*')}$`);
94
+ return regex.test(currentPath);
95
+ });
96
+ }
97
+ // Check anchor presence
98
+ let anchorFound = false;
99
+ let element = null;
100
+ if (item.firstAnchor) {
101
+ try {
102
+ element = document.querySelector(item.firstAnchor);
103
+ anchorFound = element !== null;
104
+ }
105
+ catch {
106
+ // Invalid selector
107
+ }
108
+ }
109
+ else {
110
+ // No anchor to check — if page matches, consider found
111
+ anchorFound = pageMatch;
112
+ }
113
+ map.set(item.key, {
114
+ found: pageMatch && anchorFound,
115
+ element,
116
+ });
117
+ }
118
+ setDetectionMap(map);
119
+ };
120
+ runDetection();
121
+ const interval = setInterval(runDetection, 2000);
122
+ window.addEventListener('popstate', runDetection);
123
+ return () => {
124
+ clearInterval(interval);
125
+ window.removeEventListener('popstate', runDetection);
126
+ };
127
+ }, [getCurrentRoute]);
128
+ return detectionMap;
129
+ }
130
+ // ============================================================================
131
+ // Detection Badge
132
+ // ============================================================================
133
+ function DetectionBadge({ found }) {
134
+ return (_jsx("span", { style: {
135
+ width: '8px',
136
+ height: '8px',
137
+ borderRadius: '50%',
138
+ backgroundColor: found ? '#22c55e' : '#475569',
139
+ flexShrink: 0,
140
+ display: 'inline-block',
141
+ }, title: found ? 'Found on this page' : 'Not found on this page' }));
142
+ }
143
+ // ============================================================================
144
+ // Styles
4
145
  // ============================================================================
5
146
  const styles = {
6
147
  container: {
@@ -36,121 +177,101 @@ const styles = {
36
177
  fontSize: '11px',
37
178
  color: '#64748b',
38
179
  },
39
- content: {
180
+ body: {
40
181
  flex: 1,
41
182
  overflow: 'auto',
42
183
  padding: '16px',
43
184
  },
44
- section: {
45
- marginBottom: '24px',
46
- },
47
- sectionTitle: {
48
- fontSize: '12px',
49
- fontWeight: 600,
50
- color: '#94a3b8',
185
+ groupHeader: {
186
+ fontSize: '11px',
187
+ fontWeight: 700,
188
+ color: '#64748b',
51
189
  textTransform: 'uppercase',
52
- letterSpacing: '0.05em',
53
- marginBottom: '12px',
190
+ letterSpacing: '0.06em',
191
+ padding: '4px 0 8px 0',
192
+ display: 'flex',
193
+ alignItems: 'center',
194
+ justifyContent: 'space-between',
54
195
  },
55
- select: {
56
- width: '100%',
57
- padding: '10px 12px',
196
+ groupCount: {
197
+ fontSize: '10px',
198
+ color: '#475569',
199
+ backgroundColor: 'rgba(255,255,255,0.06)',
200
+ padding: '2px 6px',
201
+ borderRadius: '8px',
202
+ },
203
+ card: {
204
+ padding: '8px 10px',
58
205
  borderRadius: '6px',
59
- border: '1px solid rgba(255,255,255,0.1)',
60
- backgroundColor: 'rgba(255,255,255,0.05)',
61
- color: '#f8fafc',
206
+ border: '1px solid rgba(255,255,255,0.06)',
207
+ background: 'rgba(255,255,255,0.02)',
208
+ marginBottom: '4px',
62
209
  fontSize: '13px',
63
- cursor: 'pointer',
210
+ color: '#e2e8f0',
64
211
  },
65
- checkbox: {
212
+ triggerLine: {
66
213
  display: 'flex',
67
214
  alignItems: 'center',
68
- gap: '8px',
69
- fontSize: '13px',
70
- color: '#d1d5db',
71
- marginTop: '8px',
72
- },
73
- addButton: {
74
- width: '100%',
75
- padding: '12px',
76
- borderRadius: '8px',
77
- border: '1px dashed rgba(139, 92, 246, 0.3)',
78
- background: 'rgba(139, 92, 246, 0.05)',
79
- color: '#8b5cf6',
80
- fontSize: '13px',
81
- fontWeight: 600,
215
+ gap: '4px',
216
+ fontSize: '11px',
217
+ color: '#94a3b8',
82
218
  cursor: 'pointer',
83
- marginBottom: '16px',
84
- },
85
- questionCard: {
86
- padding: '12px',
87
- borderRadius: '8px',
88
- border: '1px solid rgba(255,255,255,0.08)',
89
- background: 'rgba(255,255,255,0.02)',
90
- marginBottom: '8px',
219
+ marginBottom: '4px',
91
220
  },
92
- questionHeader: {
221
+ cardBody: {
93
222
  display: 'flex',
94
- alignItems: 'flex-start',
95
- justifyContent: 'space-between',
96
- marginBottom: '12px',
223
+ alignItems: 'center',
224
+ gap: '8px',
225
+ cursor: 'pointer',
97
226
  },
98
- questionPreview: {
227
+ cardText: {
99
228
  flex: 1,
100
- },
101
- questionText: {
102
- fontSize: '14px',
103
- fontWeight: 500,
104
- color: '#f8fafc',
105
- marginBottom: '4px',
106
- },
107
- answerPreview: {
108
- fontSize: '12px',
109
- color: '#64748b',
110
229
  overflow: 'hidden',
111
230
  textOverflow: 'ellipsis',
112
231
  whiteSpace: 'nowrap',
113
- maxWidth: '200px',
114
232
  },
115
- removeButton: {
116
- padding: '4px 8px',
233
+ rationaleLine: {
234
+ fontSize: '10px',
235
+ color: '#64748b',
236
+ marginTop: '4px',
237
+ },
238
+ dismissButton: {
239
+ padding: '2px 6px',
117
240
  borderRadius: '4px',
118
241
  border: 'none',
119
- background: 'rgba(239, 68, 68, 0.1)',
120
- color: '#ef4444',
121
- fontSize: '12px',
242
+ background: 'transparent',
243
+ color: '#64748b',
244
+ fontSize: '14px',
122
245
  cursor: 'pointer',
123
246
  flexShrink: 0,
247
+ lineHeight: 1,
124
248
  },
125
- inputGroup: {
126
- marginBottom: '8px',
249
+ dismissedSection: {
250
+ marginTop: '16px',
251
+ cursor: 'pointer',
252
+ userSelect: 'none',
127
253
  },
128
- inputLabel: {
129
- display: 'block',
254
+ dismissedHeader: {
130
255
  fontSize: '11px',
131
- color: '#94a3b8',
132
- marginBottom: '4px',
133
- },
134
- input: {
135
- width: '100%',
136
- padding: '8px 10px',
137
- borderRadius: '4px',
138
- border: '1px solid rgba(255,255,255,0.1)',
139
- backgroundColor: 'rgba(255,255,255,0.05)',
140
- color: '#f8fafc',
141
- fontSize: '13px',
256
+ fontWeight: 600,
257
+ color: '#475569',
258
+ display: 'flex',
259
+ alignItems: 'center',
260
+ gap: '6px',
142
261
  },
143
- textarea: {
144
- width: '100%',
145
- padding: '8px 10px',
146
- borderRadius: '4px',
147
- border: '1px solid rgba(255,255,255,0.1)',
148
- backgroundColor: 'rgba(255,255,255,0.05)',
149
- color: '#f8fafc',
150
- fontSize: '13px',
151
- minHeight: '80px',
152
- resize: 'vertical',
153
- fontFamily: 'inherit',
262
+ dismissedCard: {
263
+ display: 'flex',
264
+ alignItems: 'center',
265
+ gap: '8px',
266
+ padding: '6px 10px',
267
+ borderRadius: '6px',
268
+ border: '1px solid rgba(255,255,255,0.03)',
269
+ background: 'transparent',
270
+ marginBottom: '3px',
271
+ cursor: 'pointer',
272
+ fontSize: '12px',
273
+ color: '#475569',
274
+ opacity: 0.6,
154
275
  },
155
276
  emptyState: {
156
277
  textAlign: 'center',
@@ -186,95 +307,239 @@ const styles = {
186
307
  fontWeight: 600,
187
308
  cursor: 'pointer',
188
309
  },
310
+ // Edit form styles
311
+ editForm: {
312
+ padding: '4px 0',
313
+ },
314
+ editHeader: {
315
+ display: 'flex',
316
+ alignItems: 'center',
317
+ gap: '8px',
318
+ marginBottom: '12px',
319
+ fontSize: '13px',
320
+ fontWeight: 600,
321
+ color: '#e2e8f0',
322
+ },
323
+ editLabel: {
324
+ fontSize: '11px',
325
+ fontWeight: 600,
326
+ color: '#64748b',
327
+ marginBottom: '4px',
328
+ display: 'block',
329
+ },
330
+ editInput: {
331
+ width: '100%',
332
+ padding: '6px 8px',
333
+ borderRadius: '4px',
334
+ border: '1px solid rgba(255,255,255,0.1)',
335
+ background: 'rgba(255,255,255,0.04)',
336
+ color: '#e2e8f0',
337
+ fontSize: '12px',
338
+ fontFamily: 'inherit',
339
+ marginBottom: '8px',
340
+ boxSizing: 'border-box',
341
+ },
342
+ editTextarea: {
343
+ width: '100%',
344
+ padding: '6px 8px',
345
+ borderRadius: '4px',
346
+ border: '1px solid rgba(255,255,255,0.1)',
347
+ background: 'rgba(255,255,255,0.04)',
348
+ color: '#e2e8f0',
349
+ fontSize: '12px',
350
+ fontFamily: 'inherit',
351
+ marginBottom: '8px',
352
+ resize: 'vertical',
353
+ minHeight: '60px',
354
+ boxSizing: 'border-box',
355
+ },
356
+ editRationale: {
357
+ padding: '8px',
358
+ borderRadius: '4px',
359
+ border: '1px dashed rgba(255,255,255,0.15)',
360
+ background: 'rgba(255,255,255,0.02)',
361
+ color: '#94a3b8',
362
+ fontSize: '12px',
363
+ marginBottom: '8px',
364
+ },
365
+ editBackButton: {
366
+ padding: '6px 12px',
367
+ borderRadius: '6px',
368
+ border: '1px solid rgba(255,255,255,0.1)',
369
+ background: 'transparent',
370
+ color: '#94a3b8',
371
+ fontSize: '12px',
372
+ cursor: 'pointer',
373
+ marginTop: '8px',
374
+ },
189
375
  };
190
376
  // ============================================================================
191
377
  // FAQEditor Component
192
378
  // ============================================================================
193
379
  export function FAQEditor({ config, onChange, editor }) {
194
380
  const typedConfig = config;
195
- const questions = typedConfig.actions || [];
196
- // Generate unique ID
197
- const generateId = () => `q-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
198
- // Add a new question
199
- const handleAddQuestion = () => {
200
- const newQuestion = {
201
- kind: 'faq:question',
202
- config: {
203
- id: generateId(),
204
- question: 'New Question',
205
- answer: 'Enter your answer here...',
206
- category: undefined,
207
- },
208
- showWhen: null,
209
- };
210
- onChange({
211
- ...config,
212
- actions: [...questions, newQuestion],
213
- });
214
- editor.setDirty(true);
215
- };
216
- // Remove a question
217
- const handleRemoveQuestion = (index) => {
218
- const newQuestions = questions.filter((_, i) => i !== index);
219
- onChange({
220
- ...config,
221
- actions: newQuestions,
222
- });
223
- editor.setDirty(true);
224
- };
225
- // Update a question
226
- const handleUpdateQuestion = (index, updates) => {
227
- const newQuestions = questions.map((q, i) => {
228
- if (i !== index)
229
- return q;
230
- return {
231
- ...q,
232
- config: {
233
- ...q.config,
234
- ...updates,
235
- },
236
- };
381
+ const [dismissedKeys, setDismissedKeys] = useState(new Set());
382
+ const [dismissedOpen, setDismissedOpen] = useState(false);
383
+ const [editingKey, setEditingKey] = useState(null);
384
+ const [previewMode, setPreviewMode] = useState('after');
385
+ const [_hoveredKey, setHoveredKey] = useState(null);
386
+ // Consume initial navigation payload on mount (FAQ is a widget mount —
387
+ // initialEditKey points to the mount action, no sub-item to pre-select)
388
+ const initialConsumed = useRef(false);
389
+ useEffect(() => {
390
+ if (!initialConsumed.current && (editor.initialEditKey != null || editor.initialCreate)) {
391
+ initialConsumed.current = true;
392
+ editor.clearInitialState?.();
393
+ }
394
+ }, [editor]);
395
+ const allItems = flattenItems(typedConfig);
396
+ const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
397
+ const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
398
+ const totalQuestions = activeItems.length;
399
+ const detectionMap = useDetection(allItems, editor.getCurrentRoute);
400
+ const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
401
+ const handleDismiss = useCallback((key) => {
402
+ setDismissedKeys((prev) => {
403
+ const next = new Set(prev);
404
+ next.add(key);
405
+ return next;
237
406
  });
238
- onChange({
239
- ...config,
240
- actions: newQuestions,
407
+ if (editingKey === key)
408
+ setEditingKey(null);
409
+ }, [editingKey]);
410
+ const handleRestore = useCallback((key) => {
411
+ setDismissedKeys((prev) => {
412
+ const next = new Set(prev);
413
+ next.delete(key);
414
+ return next;
241
415
  });
416
+ }, []);
417
+ const handleCardBodyClick = useCallback((item) => {
418
+ setEditingKey(item.key);
419
+ }, []);
420
+ const handleTriggerClick = useCallback((item) => {
421
+ const pageUrl = extractFirstPage(item.question.showWhen);
422
+ if (pageUrl) {
423
+ editor.navigateTo(pageUrl);
424
+ }
425
+ if (item.firstAnchor) {
426
+ editor.highlightElement(item.firstAnchor);
427
+ }
428
+ }, [editor]);
429
+ const handleBackToList = useCallback(() => {
430
+ setEditingKey(null);
431
+ setPreviewMode('after');
432
+ editor.previewConfig(config);
433
+ editor.clearHighlight();
434
+ }, [editor, config]);
435
+ const handleBeforeAfter = useCallback((mode) => {
436
+ setPreviewMode(mode);
437
+ if (mode === 'before') {
438
+ const filtered = filterConfig(typedConfig, new Set([editingKey]));
439
+ editor.previewConfig(filtered);
440
+ }
441
+ else {
442
+ editor.previewConfig(config);
443
+ }
444
+ }, [typedConfig, editingKey, editor, config]);
445
+ const handleFieldChange = useCallback((index, field, value) => {
446
+ const ownActions = (typedConfig.actions || []).filter(isOwnAction).slice();
447
+ const q = { ...ownActions[index], config: { ...ownActions[index].config } };
448
+ q.config[field] = value;
449
+ ownActions[index] = q;
450
+ const otherActions = (typedConfig.actions || []).filter((a) => !isOwnAction(a));
451
+ const updated = { ...typedConfig, actions: [...otherActions, ...ownActions] };
452
+ onChange(updated);
242
453
  editor.setDirty(true);
454
+ }, [typedConfig, onChange, editor]);
455
+ const handlePublish = useCallback(() => {
456
+ if (dismissedKeys.size > 0) {
457
+ const filtered = filterConfig(typedConfig, dismissedKeys);
458
+ onChange(filtered);
459
+ }
460
+ editor.publish();
461
+ }, [dismissedKeys, typedConfig, onChange, editor]);
462
+ const handleCardHover = useCallback((item) => {
463
+ setHoveredKey(item.key);
464
+ if (item.firstAnchor) {
465
+ editor.highlightElement(item.firstAnchor);
466
+ }
467
+ }, [editor]);
468
+ const handleCardLeave = useCallback(() => {
469
+ setHoveredKey(null);
470
+ editor.clearHighlight();
471
+ }, [editor]);
472
+ // ---- Edit form renderer ----
473
+ const renderEditFields = (index) => {
474
+ const actions = (typedConfig.actions || []).filter(isOwnAction);
475
+ const q = actions[index];
476
+ if (!q)
477
+ return null;
478
+ const item = allItems.find((it) => it.key === String(index));
479
+ return (_jsxs("div", { style: styles.editForm, children: [item && item.trigger !== 'All pages' && (_jsxs("div", { "data-trigger": true, style: styles.triggerLine, onClick: () => handleTriggerClick(item), children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsx("label", { style: styles.editLabel, children: "Question" }), _jsx("input", { style: styles.editInput, value: q.config.question, onChange: (e) => handleFieldChange(index, 'question', e.target.value) }), _jsx("label", { style: styles.editLabel, children: "Answer" }), _jsx("textarea", { style: styles.editTextarea, value: getAnswerText(q.config.answer), onChange: (e) => handleFieldChange(index, 'answer', e.target.value) }), _jsx("label", { style: styles.editLabel, children: "Category" }), _jsx("input", { style: styles.editInput, value: q.config.category || '', onChange: (e) => handleFieldChange(index, 'category', e.target.value || undefined), placeholder: "e.g., Billing, Account" }), _jsx("label", { style: styles.editLabel, children: "AI Rationale" }), _jsx("div", { style: styles.editRationale, children: q.rationale ? q.rationale.why : 'N/A' })] }));
243
480
  };
244
- // Update expand behavior
245
- const handleExpandBehaviorChange = (e) => {
246
- onChange({
247
- ...config,
248
- expandBehavior: e.target.value,
249
- });
250
- editor.setDirty(true);
251
- };
252
- // Update theme
253
- const handleThemeChange = (e) => {
254
- onChange({
255
- ...config,
256
- theme: e.target.value,
257
- });
258
- editor.setDirty(true);
259
- };
260
- // Update searchable
261
- const handleSearchableChange = (e) => {
262
- onChange({
263
- ...config,
264
- searchable: e.target.checked,
265
- });
266
- editor.setDirty(true);
267
- };
268
- return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: () => editor.navigateHome(), style: styles.backButton, children: "\u2190 Back" }), _jsxs("div", { children: [_jsx("h2", { style: styles.title, children: "FAQ Accordion" }), _jsx("p", { style: styles.subtitle, children: "Configure questions with per-item visibility" })] })] }), _jsxs("div", { style: styles.content, children: [_jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.sectionTitle, children: "Expand Behavior" }), _jsxs("select", { value: typedConfig.expandBehavior || 'single', onChange: handleExpandBehaviorChange, style: styles.select, children: [_jsx("option", { value: "single", children: "Single (one at a time)" }), _jsx("option", { value: "multiple", children: "Multiple (any number)" })] })] }), _jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.sectionTitle, children: "Theme" }), _jsxs("select", { value: typedConfig.theme || 'auto', onChange: handleThemeChange, style: styles.select, children: [_jsx("option", { value: "auto", children: "Auto (System)" }), _jsx("option", { value: "light", children: "Light" }), _jsx("option", { value: "dark", children: "Dark" })] }), _jsxs("label", { style: styles.checkbox, children: [_jsx("input", { type: "checkbox", checked: typedConfig.searchable || false, onChange: handleSearchableChange }), "Enable search/filter"] })] }), _jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.sectionTitle, children: "Questions" }), _jsx("button", { onClick: handleAddQuestion, style: styles.addButton, children: "+ Add Question" }), questions.map((q, index) => (_jsxs("div", { style: styles.questionCard, children: [_jsxs("div", { style: styles.questionHeader, children: [_jsxs("div", { style: styles.questionPreview, children: [_jsx("div", { style: styles.questionText, children: q.config.question }), _jsx("div", { style: styles.answerPreview, children: q.config.answer })] }), _jsx("button", { onClick: () => handleRemoveQuestion(index), style: styles.removeButton, children: "Remove" })] }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Question" }), _jsx("input", { type: "text", value: q.config.question, onChange: (e) => handleUpdateQuestion(index, { question: e.target.value }), style: styles.input })] }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Answer" }), _jsx("textarea", { value: q.config.answer, onChange: (e) => handleUpdateQuestion(index, { answer: e.target.value }), style: styles.textarea })] }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Category (optional)" }), _jsx("input", { type: "text", value: q.config.category || '', onChange: (e) => handleUpdateQuestion(index, {
269
- category: e.target.value || undefined,
270
- }), style: styles.input, placeholder: "e.g., Billing, Account, Features" })] })] }, q.config.id || index))), questions.length === 0 && (_jsx("div", { style: styles.emptyState, children: "No questions configured. Click the button above to add one." }))] })] }), _jsxs("div", { style: styles.footer, children: [_jsx("button", { onClick: () => editor.save(), style: styles.saveButton, children: "Save Draft" }), _jsx("button", { onClick: () => editor.publish(), style: styles.publishButton, children: "Publish" })] })] }));
481
+ return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: () => editor.navigateHome(), style: styles.backButton, children: "\u2190 Back" }), _jsxs("div", { children: [_jsx("h2", { style: styles.title, children: "Review Questions" }), _jsxs("p", { style: styles.subtitle, children: [totalQuestions, " question", totalQuestions !== 1 ? 's' : '', totalQuestions > 0 && ` (${foundCount} found on this page)`] })] })] }), _jsx("div", { style: styles.body, children: editingKey !== null ? (
482
+ /* ---- Edit mode ---- */
483
+ (() => {
484
+ const editIndex = Number(editingKey);
485
+ const editItem = allItems.find((it) => it.key === editingKey);
486
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.editHeader, children: [_jsx("span", { children: '\u2753' }), _jsx("span", { children: editItem?.summary })] }), _jsxs("div", { style: {
487
+ display: 'flex',
488
+ gap: '0',
489
+ marginBottom: '12px',
490
+ borderRadius: '6px',
491
+ overflow: 'hidden',
492
+ border: '1px solid rgba(255,255,255,0.1)',
493
+ }, children: [_jsx("button", { onClick: () => handleBeforeAfter('before'), style: {
494
+ flex: 1,
495
+ padding: '6px 12px',
496
+ border: 'none',
497
+ fontSize: '12px',
498
+ fontWeight: 600,
499
+ cursor: 'pointer',
500
+ background: previewMode === 'before' ? 'rgba(59,130,246,0.2)' : 'transparent',
501
+ color: previewMode === 'before' ? '#3b82f6' : '#64748b',
502
+ }, children: "Before" }), _jsx("button", { onClick: () => handleBeforeAfter('after'), style: {
503
+ flex: 1,
504
+ padding: '6px 12px',
505
+ border: 'none',
506
+ borderLeft: '1px solid rgba(255,255,255,0.1)',
507
+ fontSize: '12px',
508
+ fontWeight: 600,
509
+ cursor: 'pointer',
510
+ background: previewMode === 'after' ? 'rgba(59,130,246,0.2)' : 'transparent',
511
+ color: previewMode === 'after' ? '#3b82f6' : '#64748b',
512
+ }, children: "After" })] }), renderEditFields(editIndex), _jsx("button", { style: styles.editBackButton, onClick: handleBackToList, children: "\u2190 List" })] }));
513
+ })()) : (
514
+ /* ---- List mode ---- */
515
+ _jsxs(_Fragment, { children: [allItems.length === 0 && (_jsx("div", { style: styles.emptyState, children: "No FAQ questions configured." })), activeItems.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.groupHeader, children: [_jsx("span", { children: "FAQ" }), _jsx("span", { style: styles.groupCount, children: activeItems.length })] }), activeItems.map((item) => {
516
+ const detection = detectionMap.get(item.key);
517
+ return (_jsxs("div", { style: styles.card, "data-item-key": item.key, onMouseEnter: () => handleCardHover(item), onMouseLeave: handleCardLeave, children: [item.trigger !== 'All pages' && (_jsxs("div", { "data-trigger": true, style: styles.triggerLine, onClick: (e) => {
518
+ e.stopPropagation();
519
+ handleTriggerClick(item);
520
+ }, children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsxs("div", { "data-card-body": true, style: styles.cardBody, onClick: () => handleCardBodyClick(item), children: [_jsx(DetectionBadge, { found: detection?.found ?? false }), _jsx("span", { style: styles.cardText, children: item.summary }), _jsx("button", { style: styles.dismissButton, onClick: (e) => {
521
+ e.stopPropagation();
522
+ handleDismiss(item.key);
523
+ }, title: "Dismiss this question", children: "\u00D7" })] }), _jsxs("div", { style: styles.rationaleLine, children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] })] }, item.key));
524
+ })] })), dismissedItems.length > 0 && (_jsxs("div", { style: styles.dismissedSection, children: [_jsxs("div", { style: { ...styles.dismissedHeader, cursor: 'pointer' }, onClick: () => setDismissedOpen(!dismissedOpen), children: [_jsx("span", { children: dismissedOpen ? '\u25be' : '\u25b8' }), _jsxs("span", { children: ["Dismissed (", dismissedItems.length, ")"] })] }), dismissedOpen && (_jsx("div", { style: { marginTop: '6px' }, children: dismissedItems.map((item) => (_jsxs("div", { style: styles.dismissedCard, children: [_jsx("span", { style: { ...styles.cardText, textDecoration: 'line-through' }, children: item.summary }), _jsx("button", { style: {
525
+ ...styles.dismissButton,
526
+ color: '#3b82f6',
527
+ fontSize: '11px',
528
+ }, onClick: (e) => {
529
+ e.stopPropagation();
530
+ handleRestore(item.key);
531
+ }, children: "Restore" })] }, item.key))) }))] }))] })) }), _jsxs("div", { style: styles.footer, children: [_jsx("button", { onClick: () => editor.save(), style: styles.saveButton, children: "Save Draft" }), _jsx("button", { onClick: handlePublish, style: styles.publishButton, children: "Publish" })] })] }));
271
532
  }
272
533
  /**
273
534
  * Editor panel configuration for the app registry.
274
535
  */
275
536
  export const editorPanel = {
276
537
  title: 'FAQ',
277
- icon: '',
538
+ icon: '\u2753',
278
539
  description: 'FAQ accordion with per-item visibility',
279
540
  };
541
+ export const editor = {
542
+ panel: editorPanel,
543
+ component: FAQEditor,
544
+ };
280
545
  export default FAQEditor;