@syntrologie/adapt-faq 2.16.0 → 2.17.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.
@@ -1,94 +0,0 @@
1
- /**
2
- * Adaptive FAQ - Runtime Module (Lit)
3
- *
4
- * Runtime manifest for the FAQ accordion adaptive.
5
- * Uses the Lit web component mountable instead of the React one.
6
- * Provides action executors and widget registration.
7
- */
8
- import { executorDefinitions } from './executors';
9
- import './FAQWidgetLit'; // registers <syntro-faq-accordion> custom element
10
- // ============================================================================
11
- // Lit Mountable Widget
12
- // ============================================================================
13
- /**
14
- * Mountable widget interface for <syntro-faq-accordion> (Lit web component).
15
- *
16
- * Mirrors FAQMountableWidget but mounts the Lit element instead of a React
17
- * root — no React dependency required at mount time.
18
- */
19
- export const FAQWidgetLitMountable = {
20
- mount(container, config) {
21
- const { runtime, instanceId = 'faq-widget', ...faqConfig } = config ?? {
22
- expandBehavior: 'single',
23
- searchable: false,
24
- theme: 'auto',
25
- actions: [],
26
- };
27
- const el = document.createElement('syntro-faq-accordion');
28
- Object.assign(el, {
29
- faqConfig: faqConfig,
30
- runtime: runtime ?? null,
31
- instanceId,
32
- });
33
- container.appendChild(el);
34
- return () => el.remove();
35
- },
36
- };
37
- // ============================================================================
38
- // App Runtime Manifest
39
- // ============================================================================
40
- /**
41
- * Runtime manifest for adaptive-faq (Lit variant).
42
- *
43
- * Provides:
44
- * - FAQ action executors (scroll_to, toggle_item, update)
45
- * - Widget-based accordion using the Lit web component
46
- */
47
- export const runtime = {
48
- id: 'adaptive-faq',
49
- version: '2.0.0',
50
- name: 'FAQ Accordion',
51
- description: 'Collapsible Q&A accordion with actions, rich content, feedback, and personalization',
52
- /**
53
- * Action executors for programmatic FAQ interaction.
54
- */
55
- executors: executorDefinitions,
56
- /**
57
- * Widget definitions for the runtime's WidgetRegistry.
58
- */
59
- widgets: [
60
- {
61
- id: 'adaptive-faq:accordion',
62
- component: FAQWidgetLitMountable,
63
- metadata: {
64
- name: 'FAQ Accordion',
65
- description: 'Collapsible Q&A accordion with search, categories, and feedback',
66
- icon: '❓',
67
- subtitle: 'Curated just for you.',
68
- },
69
- },
70
- ],
71
- /**
72
- * Extract notify watcher entries from tile config props.
73
- * The runtime evaluates these continuously (even with drawer closed)
74
- * and publishes faq:question_revealed when triggerWhen transitions false → true.
75
- */
76
- notifyWatchers(props) {
77
- const actions = (props.actions ?? []);
78
- return actions
79
- .filter((a) => a.notify && a.triggerWhen)
80
- .map((a) => ({
81
- id: `faq:${a.config.id}`,
82
- strategy: a.triggerWhen,
83
- eventName: 'faq:question_revealed',
84
- eventProps: {
85
- questionId: a.config.id,
86
- question: a.config.question,
87
- title: a.notify.title,
88
- body: a.notify.body,
89
- icon: a.notify.icon,
90
- },
91
- }));
92
- },
93
- };
94
- export default runtime;
package/dist/state.js DELETED
@@ -1,132 +0,0 @@
1
- /**
2
- * Adaptive FAQ - Shared Widget State Store
3
- *
4
- * Lightweight reactive store that executors and the widget both read/write.
5
- * Implements an observer pattern so UI components can subscribe to changes.
6
- */
7
- // ============================================================================
8
- // Factory
9
- // ============================================================================
10
- export function createFAQStore(initialItems) {
11
- // Internal mutable state ---------------------------------------------------
12
- let state = {
13
- expandedItems: new Set(),
14
- items: [...initialItems],
15
- searchQuery: '',
16
- collapsedCategories: new Set(),
17
- feedbackState: new Map(),
18
- };
19
- const listeners = new Set();
20
- // Notify all subscribers with the current state snapshot -------------------
21
- function notify() {
22
- for (const listener of listeners) {
23
- listener(state);
24
- }
25
- }
26
- // Helpers to produce new state objects (immutable from the outside) --------
27
- function setState(patch) {
28
- state = { ...state, ...patch };
29
- notify();
30
- }
31
- // --- Public API -----------------------------------------------------------
32
- function getState() {
33
- return state;
34
- }
35
- function expand(itemId) {
36
- const next = new Set(state.expandedItems);
37
- next.add(itemId);
38
- setState({ expandedItems: next });
39
- }
40
- function collapse(itemId) {
41
- const next = new Set(state.expandedItems);
42
- next.delete(itemId);
43
- setState({ expandedItems: next });
44
- }
45
- function toggle(itemId) {
46
- if (state.expandedItems.has(itemId)) {
47
- collapse(itemId);
48
- }
49
- else {
50
- expand(itemId);
51
- }
52
- }
53
- function addItems(items, position) {
54
- const next = position === 'prepend' ? [...items, ...state.items] : [...state.items, ...items];
55
- setState({ items: next });
56
- }
57
- function removeItem(itemId) {
58
- setState({
59
- items: state.items.filter((i) => i.config.id !== itemId),
60
- });
61
- }
62
- function reorderItems(order) {
63
- const orderSet = new Set(order);
64
- const itemMap = new Map(state.items.map((i) => [i.config.id, i]));
65
- // Place items in the specified order first
66
- const ordered = [];
67
- for (const id of order) {
68
- const item = itemMap.get(id);
69
- if (item) {
70
- ordered.push(item);
71
- }
72
- }
73
- // Append any items that were not listed in the order array
74
- for (const item of state.items) {
75
- if (!orderSet.has(item.config.id)) {
76
- ordered.push(item);
77
- }
78
- }
79
- setState({ items: ordered });
80
- }
81
- function replaceItems(items) {
82
- setState({ items: [...items] });
83
- }
84
- function setFeedback(itemId, value) {
85
- const next = new Map(state.feedbackState);
86
- next.set(itemId, value);
87
- setState({ feedbackState: next });
88
- }
89
- function setSearchQuery(query) {
90
- setState({ searchQuery: query });
91
- }
92
- function toggleCategory(category) {
93
- const next = new Set(state.collapsedCategories);
94
- if (next.has(category)) {
95
- next.delete(category);
96
- }
97
- else {
98
- next.add(category);
99
- }
100
- setState({ collapsedCategories: next });
101
- }
102
- function findByQuestion(text) {
103
- const needle = text.toLowerCase();
104
- for (const item of state.items) {
105
- if (item.config.question.toLowerCase().includes(needle)) {
106
- return item;
107
- }
108
- }
109
- return null;
110
- }
111
- function subscribe(listener) {
112
- listeners.add(listener);
113
- return () => {
114
- listeners.delete(listener);
115
- };
116
- }
117
- return {
118
- getState,
119
- expand,
120
- collapse,
121
- toggle,
122
- addItems,
123
- removeItem,
124
- reorderItems,
125
- replaceItems,
126
- setFeedback,
127
- setSearchQuery,
128
- toggleCategory,
129
- findByQuestion,
130
- subscribe,
131
- };
132
- }
package/dist/summarize.js DELETED
@@ -1,62 +0,0 @@
1
- /**
2
- * Human-readable summary generation for FAQ items.
3
- * Pure functions — no DOM access, just string formatting.
4
- */
5
- const MAX_Q_LEN = 50;
6
- const MAX_A_LEN = 40;
7
- function truncate(text, max) {
8
- if (text.length <= max)
9
- return text;
10
- return `${text.slice(0, max).trimEnd()}...`;
11
- }
12
- function stripHtml(html) {
13
- return html.replace(/<[^>]*>/g, '').trim();
14
- }
15
- function getAnswerPreview(answer) {
16
- if (typeof answer === 'string')
17
- return answer;
18
- if (answer.type === 'rich')
19
- return stripHtml(answer.html);
20
- return answer.content.replace(/[*_#`]/g, '').trim();
21
- }
22
- function isRuleStrategy(s) {
23
- return (typeof s === 'object' &&
24
- s !== null &&
25
- s.type === 'rules' &&
26
- Array.isArray(s.rules));
27
- }
28
- /**
29
- * Parse a triggerWhen strategy into a human-readable trigger description.
30
- */
31
- export function describeTrigger(triggerWhen) {
32
- if (!triggerWhen)
33
- return 'All pages';
34
- if (!isRuleStrategy(triggerWhen))
35
- return 'All pages';
36
- const pages = [];
37
- const anchors = [];
38
- for (const rule of triggerWhen.rules) {
39
- for (const condition of rule.conditions) {
40
- if (condition.type === 'page_url' && typeof condition.url === 'string') {
41
- pages.push(condition.url);
42
- }
43
- if (condition.type === 'anchor_visible' && typeof condition.anchorId === 'string') {
44
- anchors.push(condition.anchorId);
45
- }
46
- }
47
- }
48
- const parts = [];
49
- if (pages.length > 0)
50
- parts.push(pages[0]);
51
- if (anchors.length > 0)
52
- parts.push(anchors[0]);
53
- return parts.length > 0 ? parts.join(' \u00b7 ') : 'All pages';
54
- }
55
- /**
56
- * Generate a one-liner summary for an FAQ item.
57
- */
58
- export function summarizeFAQItem(item) {
59
- const q = truncate(item.config.question, MAX_Q_LEN);
60
- const a = truncate(getAnswerPreview(item.config.answer), MAX_A_LEN);
61
- return `Q: "${q}" \u2014 ${a}`;
62
- }
package/dist/types.js DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * Adaptive FAQ - Types
3
- *
4
- * Type definitions for the FAQ accordion adaptive.
5
- * Demonstrates compositional action pattern with per-item triggerWhen.
6
- */
7
- // ============================================================================
8
- // Action Namespace
9
- // ============================================================================
10
- export const ACTION_NAMESPACE = 'faq';
11
- /**
12
- * Returns true if the action belongs to this adaptive package.
13
- * Uses prefix matching so new kinds (e.g. faq:category) are automatically included.
14
- */
15
- export function isOwnAction(action) {
16
- return action.kind.startsWith(`${ACTION_NAMESPACE}:`);
17
- }
@@ -1,129 +0,0 @@
1
- /**
2
- * @syntrologie/sdk-contracts
3
- *
4
- * Shared TypeScript contracts between runtime-sdk and adaptive packages.
5
- * Executor types + shared Zod schemas for decision strategies and conditions.
6
- */
7
- export { AnchorIdZ, AnchorVisibleConditionZ, COUNTABLE_EVENTS, type ConditionSchema, ConditionZ, CooldownActiveConditionZ, CountableEventZ, CounterDefZ, type DecisionStrategySchema, DecisionStrategyZ, DismissedConditionZ, ELEMENT_MATCH_FIELDS, EventCountConditionZ, EventOccurredConditionZ, type EventScopeSchema, EventScopeZ, ExternalStrategyZ, FrequencyLimitConditionZ, MATCH_FIELD_DOCS, MatchOpZ, ModelStrategyZ, NotifyZ, PageUrlConditionZ, RouteConditionZ, type RuleSchema, RuleStrategyZ, RuleZ, ScoreStrategyZ, SESSION_METRIC_KEYS, SessionMetricConditionZ, SessionMetricKeyZ, StateEqualsConditionZ, TRIGGER_EXAMPLES, type TriggerWhenSchema, TriggerWhenZ, ViewportConditionZ, } from './schemas.js';
8
- /** Route-scoped anchor identifier */
9
- export interface AnchorId {
10
- /** CSS selector for the anchor element */
11
- selector: string;
12
- /** Route pattern(s) where this anchor applies */
13
- route: string | string[];
14
- }
15
- /** Highlight style configuration */
16
- export interface HighlightStyle {
17
- /** Ring color (default: "#5b8cff") */
18
- color?: string;
19
- /** Backdrop scrim opacity 0-1 (default: 0.55) */
20
- scrimOpacity?: number;
21
- /** Padding between element and ring (default: 12px) */
22
- paddingPx?: number;
23
- /** Ring corner radius (default: 12px) */
24
- radiusPx?: number;
25
- }
26
- /** Badge position relative to anchor */
27
- export type BadgePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
28
- /** CTA button configuration for tooltips/modals */
29
- export interface CtaButton {
30
- label: string;
31
- actionId: string;
32
- primary?: boolean;
33
- }
34
- /** Modal content configuration */
35
- export interface ModalContent {
36
- title?: string;
37
- body: string;
38
- }
39
- /** Tooltip content configuration (action typed as unknown — packages with ActionStep keep local narrowing) */
40
- export interface TooltipContent {
41
- title?: string;
42
- body: string;
43
- cta?: {
44
- label: string;
45
- action: unknown;
46
- };
47
- ctaButtons?: CtaButton[];
48
- }
49
- /** Tooltip trigger mode */
50
- export type TooltipTrigger = 'immediate' | 'hover' | 'click';
51
- /** Tooltip lifecycle — when the action completes (for sequences) */
52
- export type TooltipWaitFor = 'dismissed' | 'cta-click' | `timeout:${number}`;
53
- /** Modal lifecycle — when the action completes (for sequences) */
54
- export type ModalWaitFor = 'dismissed' | 'cta-click' | `timeout:${number}`;
55
- /** Position where HTML/content should be inserted */
56
- export type InsertPosition = 'before' | 'after' | 'prepend' | 'append' | 'replace';
57
- /** Deep-link target for opening a specific tile/item in the canvas sidebar */
58
- export interface DeepLink {
59
- tileId: string;
60
- itemId?: string;
61
- }
62
- /**
63
- * Scoped event filtering for event_count conditions.
64
- * The adaptive registers a predicate built from this scope.
65
- */
66
- export interface EventScope {
67
- /** Event names to count */
68
- events: string[];
69
- /** URL path substring filter */
70
- urlContains?: string;
71
- /** Exact prop matching (e.g. { tagName: "a" }) */
72
- props?: Record<string, string | number | boolean>;
73
- }
74
- /** Props passed to editor panel components by the ModularEditorPanel host. */
75
- export interface EditorPanelProps {
76
- config: Record<string, unknown>;
77
- onChange: (config: Record<string, unknown>) => void;
78
- editor: {
79
- setDirty: (dirty: boolean) => void;
80
- navigateHome: () => Promise<boolean>;
81
- save: () => Promise<void>;
82
- publish: (captureScreenshot?: boolean) => Promise<void>;
83
- /** Navigate the target page to a different route (preserves editor params). */
84
- navigateTo: (route: string) => Promise<void>;
85
- /** Show a persistent blue highlight on the element matching this selector. */
86
- highlightElement: (selector: string) => void;
87
- /** Remove the persistent element highlight. */
88
- clearHighlight: () => void;
89
- /** Get the current page route (pathname). */
90
- getCurrentRoute: () => string;
91
- /** Push a temporary config to the live page preview without saving to state. */
92
- previewConfig: (config: Record<string, unknown>) => void;
93
- /** Global before/after preview mode set by the panel's toggle. */
94
- previewMode?: 'before' | 'after';
95
- /** Flat action index to open in edit mode (from accordion navigation). */
96
- initialEditKey?: string;
97
- /** Open the editor in create mode. */
98
- initialCreate?: boolean;
99
- /** Clear the initial navigation state (call after consuming). */
100
- clearInitialState?: () => void;
101
- /** Get dismissed keys persisted in navigation context. */
102
- getDismissedKeys?: () => Set<string>;
103
- /** Sync dismissed keys back to navigation context. */
104
- setDismissedKeys?: (keys: Set<string>) => void;
105
- /** Register a back handler shown in the panel header. Pass null to clear. */
106
- setBackHandler?: (handler: (() => void) | null) => void;
107
- };
108
- platformClient?: unknown;
109
- }
110
- /** Cleanup function returned by executors */
111
- export type ExecutorCleanup = () => void | Promise<void>;
112
- /** Update function for actions that support in-place updates */
113
- export type ExecutorUpdate = (changes: Record<string, unknown>) => void | Promise<void>;
114
- /** Result returned by action executors */
115
- export interface ExecutorResult {
116
- cleanup: ExecutorCleanup;
117
- updateFn?: ExecutorUpdate;
118
- }
119
- /** Context passed to action executors by the runtime */
120
- export interface ExecutorContext {
121
- overlayRoot: HTMLElement;
122
- resolveAnchor: (anchorId: AnchorId) => HTMLElement | null;
123
- waitForAnchor?: (anchorId: AnchorId, timeoutMs?: number) => Promise<HTMLElement | null>;
124
- generateId: () => string;
125
- publishEvent: (name: string, props?: Record<string, unknown>) => void;
126
- adaptiveId?: string;
127
- }
128
- /** Executor function signature */
129
- export type ActionExecutor<T = unknown> = (action: T, context: ExecutorContext) => Promise<ExecutorResult>;
@@ -1,17 +0,0 @@
1
- /**
2
- * @syntrologie/sdk-contracts
3
- *
4
- * Shared TypeScript contracts between runtime-sdk and adaptive packages.
5
- * Executor types + shared Zod schemas for decision strategies and conditions.
6
- */
7
- export {
8
- // Anchor ID schema
9
- AnchorIdZ, AnchorVisibleConditionZ,
10
- // Trigger vocabulary — canonical enums, examples, and match field docs
11
- COUNTABLE_EVENTS, ConditionZ, CooldownActiveConditionZ, CountableEventZ, CounterDefZ, DecisionStrategyZ, DismissedConditionZ, ELEMENT_MATCH_FIELDS, EventCountConditionZ, EventOccurredConditionZ,
12
- // Shared schemas
13
- EventScopeZ, ExternalStrategyZ, FrequencyLimitConditionZ, MATCH_FIELD_DOCS, MatchOpZ, ModelStrategyZ, NotifyZ,
14
- // Condition schemas
15
- PageUrlConditionZ, RouteConditionZ, RuleStrategyZ,
16
- // Strategy schemas
17
- RuleZ, ScoreStrategyZ, SESSION_METRIC_KEYS, SessionMetricConditionZ, SessionMetricKeyZ, StateEqualsConditionZ, TRIGGER_EXAMPLES, TriggerWhenZ, ViewportConditionZ, } from './schemas.js';