@syntrologie/adapt-faq 2.15.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.
- package/dist/editor.d.ts +29 -33
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +194 -317
- package/dist/editor.js.map +7 -0
- package/dist/runtime.d.ts +3 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1248 -93
- package/dist/runtime.js.map +7 -0
- package/dist/schema.d.ts +670 -138
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +442 -206
- package/dist/schema.js.map +7 -0
- package/package.json +4 -20
- package/dist/FAQWidget.d.ts +0 -33
- package/dist/FAQWidget.d.ts.map +0 -1
- package/dist/FAQWidget.js +0 -375
- package/dist/FAQWidgetLit.js +0 -534
- package/dist/cdn.d.ts +0 -70
- package/dist/cdn.d.ts.map +0 -1
- package/dist/cdn.js +0 -46
- package/dist/editor-lit.d.ts +0 -37
- package/dist/editor-lit.d.ts.map +0 -1
- package/dist/editor-lit.js +0 -195
- package/dist/executors.js +0 -150
- package/dist/faq-styles.js +0 -204
- package/dist/faq-types.js +0 -7
- package/dist/runtime-lit.d.ts +0 -85
- package/dist/runtime-lit.d.ts.map +0 -1
- package/dist/runtime-lit.js +0 -94
- package/dist/state.js +0 -132
- package/dist/summarize.js +0 -62
- package/dist/types.js +0 -17
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +0 -129
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +0 -17
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +0 -2296
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +0 -361
- package/node_modules/@syntrologie/sdk-contracts/package.json +0 -33
package/dist/schema.js
CHANGED
|
@@ -1,215 +1,451 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
// ../../sdk-contracts/dist/schemas.js
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var AnchorIdZ = z.object({
|
|
4
|
+
selector: z.string(),
|
|
5
|
+
route: z.union([z.string(), z.array(z.string())])
|
|
6
|
+
}).strict();
|
|
7
|
+
var AuthoringFieldsZ = {
|
|
8
|
+
title: z.string().max(200).optional().describe("Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK."),
|
|
9
|
+
description: z.string().max(1e3).optional().describe("Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK."),
|
|
10
|
+
validation: z.array(z.string().max(500)).max(10).optional().describe("Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.")
|
|
11
|
+
};
|
|
12
|
+
var COUNTABLE_EVENTS = [
|
|
13
|
+
// User interactions (from PostHog autocapture normalization)
|
|
14
|
+
"ui.click",
|
|
15
|
+
"ui.scroll",
|
|
16
|
+
"ui.input",
|
|
17
|
+
"ui.change",
|
|
18
|
+
"ui.submit",
|
|
19
|
+
// Behavioral detectors (from event-processor)
|
|
20
|
+
"ui.hover",
|
|
21
|
+
"ui.idle",
|
|
22
|
+
"ui.scroll_thrash",
|
|
23
|
+
"ui.focus_bounce",
|
|
24
|
+
// Navigation
|
|
25
|
+
"nav.page_view",
|
|
26
|
+
"nav.page_leave",
|
|
27
|
+
// Derived behavioral signals
|
|
28
|
+
"behavior.rage_click",
|
|
29
|
+
"behavior.hesitation",
|
|
30
|
+
"behavior.confusion"
|
|
31
|
+
];
|
|
32
|
+
var CountableEventZ = z.enum(COUNTABLE_EVENTS).describe("Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals.");
|
|
33
|
+
var SESSION_METRIC_KEYS = ["time_on_page", "page_views", "scroll_depth"];
|
|
34
|
+
var SessionMetricKeyZ = z.enum(SESSION_METRIC_KEYS).describe("Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.");
|
|
35
|
+
var PageUrlConditionZ = z.object({
|
|
36
|
+
type: z.literal("page_url"),
|
|
37
|
+
url: z.string().describe('URL path to match (e.g. "/pricing", "/dashboard")')
|
|
38
|
+
}).describe('Fires when the current page URL matches. Use for page-specific actions. Example: {"type": "page_url", "url": "/pricing"}');
|
|
39
|
+
var RouteConditionZ = z.object({
|
|
40
|
+
type: z.literal("route"),
|
|
41
|
+
routeId: z.string().describe("Named route ID from the route filter")
|
|
42
|
+
}).describe("Fires when the current route matches a named route ID.");
|
|
43
|
+
var AnchorVisibleConditionZ = z.object({
|
|
44
|
+
type: z.literal("anchor_visible"),
|
|
45
|
+
anchorId: z.string().describe("CSS selector of the anchor element"),
|
|
46
|
+
state: z.enum(["visible", "present", "absent"]).describe('"visible" = in viewport, "present" = in DOM, "absent" = not in DOM')
|
|
47
|
+
}).describe(`Fires based on a DOM element's visibility state. Example: {"type": "anchor_visible", "anchorId": "#cta-button", "state": "visible"}`);
|
|
48
|
+
var EventOccurredConditionZ = z.object({
|
|
49
|
+
type: z.literal("event_occurred"),
|
|
50
|
+
eventName: z.string().describe('Event name (e.g. "ui.click", "$pageview")'),
|
|
51
|
+
withinMs: z.number().optional().describe("Time window in ms. Omit = any time this session.")
|
|
52
|
+
}).describe('Fires when a specific event has occurred during this session. Example: {"type": "event_occurred", "eventName": "ui.click", "withinMs": 5000}');
|
|
53
|
+
var StateEqualsConditionZ = z.object({
|
|
54
|
+
type: z.literal("state_equals"),
|
|
55
|
+
key: z.string().describe("Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."),
|
|
56
|
+
value: z.unknown().describe("Expected value to match against")
|
|
57
|
+
}).describe("Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.");
|
|
58
|
+
var ViewportConditionZ = z.object({
|
|
59
|
+
type: z.literal("viewport"),
|
|
60
|
+
minWidth: z.number().optional().describe("Minimum viewport width in pixels"),
|
|
61
|
+
maxWidth: z.number().optional().describe("Maximum viewport width in pixels"),
|
|
62
|
+
minHeight: z.number().optional().describe("Minimum viewport height in pixels"),
|
|
63
|
+
maxHeight: z.number().optional().describe("Maximum viewport height in pixels")
|
|
64
|
+
}).describe('Fires based on viewport (screen) size. Use for responsive behavior. Example: {"type": "viewport", "minWidth": 768} \u2014 fires on tablet and larger.');
|
|
65
|
+
var SessionMetricConditionZ = z.object({
|
|
66
|
+
type: z.literal("session_metric"),
|
|
67
|
+
key: SessionMetricKeyZ,
|
|
68
|
+
operator: z.enum(["gte", "lte", "eq", "gt", "lt"]),
|
|
69
|
+
threshold: z.number().describe("Numeric threshold to compare against")
|
|
70
|
+
}).describe('Fires when a session metric crosses a threshold. Valid keys: "time_on_page" (seconds), "page_views" (count), "scroll_depth" (0-100). Example: {"type": "session_metric", "key": "time_on_page", "operator": "gte", "threshold": 30}');
|
|
71
|
+
var DismissedConditionZ = z.object({
|
|
72
|
+
type: z.literal("dismissed"),
|
|
73
|
+
key: z.string().describe("Dismissal key (usually a tile or action ID)"),
|
|
74
|
+
inverted: z.boolean().optional().describe("When true, fires if NOT dismissed (default behavior)")
|
|
75
|
+
}).describe("Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.");
|
|
76
|
+
var CooldownActiveConditionZ = z.object({
|
|
77
|
+
type: z.literal("cooldown_active"),
|
|
78
|
+
key: z.string().describe("Cooldown key"),
|
|
79
|
+
inverted: z.boolean().optional().describe("When true, fires if cooldown is NOT active")
|
|
80
|
+
}).describe("Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.");
|
|
81
|
+
var FrequencyLimitConditionZ = z.object({
|
|
82
|
+
type: z.literal("frequency_limit"),
|
|
83
|
+
key: z.string().describe("Frequency counter key"),
|
|
84
|
+
limit: z.number().describe("Maximum allowed count"),
|
|
85
|
+
inverted: z.boolean().optional().describe("When true, fires if limit NOT reached")
|
|
86
|
+
}).describe("Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.");
|
|
87
|
+
var MatchOpZ = z.object({
|
|
88
|
+
equals: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
89
|
+
contains: z.string().optional()
|
|
90
|
+
}).describe("Match operator for counter filters. Exactly one of equals or contains must be specified.");
|
|
91
|
+
var CounterDefZ = z.object({
|
|
92
|
+
events: z.array(CountableEventZ).min(1).describe("Event names to count. Use values from the countable events enum."),
|
|
93
|
+
match: z.record(z.string(), MatchOpZ).optional().describe("Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together.")
|
|
94
|
+
}).describe("Defines what events to count. Registered as an accumulator predicate at config-load time.");
|
|
95
|
+
var EventCountConditionZ = z.object({
|
|
96
|
+
type: z.literal("event_count"),
|
|
97
|
+
key: z.string().describe("Unique key for this counter (used for accumulator registration)"),
|
|
98
|
+
operator: z.enum(["gte", "lte", "eq", "gt", "lt"]),
|
|
99
|
+
count: z.number().int().min(0).describe("Target count threshold"),
|
|
100
|
+
withinMs: z.number().positive().optional().describe("Time window in ms. Omit = count across entire session."),
|
|
101
|
+
counter: CounterDefZ.optional().describe("Inline counter definition. Defines what events to count.")
|
|
102
|
+
}).describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {"type": "event_count", "key": "pricing-clicks", "operator": "gte", "count": 3, "counter": {"events": ["ui.click"], "match": {"attr__data-cta": {"contains": "pricing"}}}}');
|
|
103
|
+
var ConditionZ = z.discriminatedUnion("type", [
|
|
104
|
+
PageUrlConditionZ,
|
|
105
|
+
RouteConditionZ,
|
|
106
|
+
AnchorVisibleConditionZ,
|
|
107
|
+
EventOccurredConditionZ,
|
|
108
|
+
StateEqualsConditionZ,
|
|
109
|
+
ViewportConditionZ,
|
|
110
|
+
SessionMetricConditionZ,
|
|
111
|
+
DismissedConditionZ,
|
|
112
|
+
CooldownActiveConditionZ,
|
|
113
|
+
FrequencyLimitConditionZ,
|
|
114
|
+
EventCountConditionZ
|
|
115
|
+
]);
|
|
116
|
+
var RuleZ = z.object({
|
|
117
|
+
conditions: z.array(ConditionZ).describe("Array of conditions \u2014 ALL must match (AND logic) for this rule to fire."),
|
|
118
|
+
value: z.unknown().describe("Value returned when all conditions match. For triggerWhen: true = fire the action.")
|
|
119
|
+
}).describe("A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom \u2014 first rule where all conditions match wins and returns its value.");
|
|
120
|
+
var RuleStrategyZ = z.object({
|
|
121
|
+
type: z.literal("rules"),
|
|
122
|
+
rules: z.array(RuleZ).describe("Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins."),
|
|
123
|
+
default: z.unknown().describe("Fallback value when no rule matches. For triggerWhen: false = do not fire by default.")
|
|
124
|
+
}).describe("Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false.");
|
|
125
|
+
var ScoreStrategyZ = z.object({
|
|
126
|
+
type: z.literal("score"),
|
|
127
|
+
field: z.string(),
|
|
128
|
+
threshold: z.number(),
|
|
129
|
+
above: z.unknown(),
|
|
130
|
+
below: z.unknown()
|
|
131
|
+
}).describe("Score-based strategy. Compares a field value against a threshold.");
|
|
132
|
+
var ModelStrategyZ = z.object({
|
|
133
|
+
type: z.literal("model"),
|
|
134
|
+
modelId: z.string(),
|
|
135
|
+
inputs: z.array(z.string()),
|
|
136
|
+
outputMapping: z.record(z.string(), z.unknown()),
|
|
137
|
+
default: z.unknown()
|
|
138
|
+
}).describe("ML model strategy. Sends inputs to a model and maps outputs.");
|
|
139
|
+
var ExternalStrategyZ = z.object({
|
|
140
|
+
type: z.literal("external"),
|
|
141
|
+
endpoint: z.string(),
|
|
142
|
+
method: z.enum(["GET", "POST"]).optional(),
|
|
143
|
+
default: z.unknown(),
|
|
144
|
+
timeoutMs: z.number().optional()
|
|
145
|
+
}).describe("External API strategy. Calls an endpoint to determine the value.");
|
|
146
|
+
var DecisionStrategyZ = z.discriminatedUnion("type", [
|
|
147
|
+
RuleStrategyZ,
|
|
148
|
+
ScoreStrategyZ,
|
|
149
|
+
ModelStrategyZ,
|
|
150
|
+
ExternalStrategyZ
|
|
151
|
+
]);
|
|
152
|
+
var TriggerWhenZ = DecisionStrategyZ.nullable().optional();
|
|
153
|
+
var EventScopeZ = z.object({
|
|
154
|
+
events: z.array(z.string()),
|
|
155
|
+
urlContains: z.string().optional(),
|
|
156
|
+
props: z.record(z.union([z.string(), z.number(), z.boolean()])).optional()
|
|
104
157
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
var NotifyZ = z.object({
|
|
159
|
+
title: z.string().optional(),
|
|
160
|
+
body: z.string().optional(),
|
|
161
|
+
icon: z.string().optional()
|
|
162
|
+
}).nullable().optional();
|
|
163
|
+
|
|
164
|
+
// src/schema.ts
|
|
165
|
+
import { z as z2 } from "zod";
|
|
166
|
+
var AssetZ = z2.object({
|
|
167
|
+
id: z2.string().describe("Unique asset ID referenced inside the markdown content via `asset:<id>`."),
|
|
168
|
+
type: z2.enum(["image", "video"]).describe('"image" for static images, "video" for embeddable video clips.'),
|
|
169
|
+
src: z2.string().describe("Absolute URL of the media asset."),
|
|
170
|
+
alt: z2.string().optional().describe("Optional alt text for images (accessibility)."),
|
|
171
|
+
width: z2.number().optional().describe("Optional display width in pixels."),
|
|
172
|
+
height: z2.number().optional().describe("Optional display height in pixels.")
|
|
173
|
+
}).describe(
|
|
174
|
+
"A single embedded media asset (image or video) referenced by ID in the markdown content."
|
|
175
|
+
);
|
|
176
|
+
var RichHTMLAnswerZ = z2.object({
|
|
177
|
+
type: z2.literal("rich").describe("Use this variant when the answer is pre-rendered HTML."),
|
|
178
|
+
html: z2.string().describe("Sanitized HTML string rendered directly as the answer body.")
|
|
179
|
+
}).describe("Rich HTML answer: use when the answer content is pre-rendered HTML markup.");
|
|
180
|
+
var EnhancedMarkdownAnswerZ = z2.object({
|
|
181
|
+
type: z2.literal("markdown").describe(
|
|
182
|
+
"Use this variant for markdown with embedded images or video via the assets array."
|
|
183
|
+
),
|
|
184
|
+
content: z2.string().describe(
|
|
185
|
+
"Markdown string. Reference assets using the `asset:<id>` URI scheme, e.g. ``."
|
|
186
|
+
),
|
|
187
|
+
assets: z2.array(AssetZ).optional().describe("Optional media assets referenced in the markdown via `asset:<id>` URIs.")
|
|
188
|
+
}).describe(
|
|
189
|
+
"Enhanced markdown answer: use when the answer mixes formatted text with images or video assets."
|
|
190
|
+
);
|
|
191
|
+
var FAQAnswerZ = z2.union([z2.string(), RichHTMLAnswerZ, EnhancedMarkdownAnswerZ]).describe(
|
|
192
|
+
'Answer content. Use a plain string for simple text, "rich" for pre-rendered HTML, or "markdown" for formatted text with embedded assets.'
|
|
193
|
+
);
|
|
194
|
+
var FeedbackConfigZ = z2.object({
|
|
195
|
+
style: z2.enum(["thumbs", "rating"]).describe('"thumbs" shows a thumbs-up/down pair; "rating" shows a numeric scale.'),
|
|
196
|
+
prompt: z2.string().optional().describe(
|
|
197
|
+
'Optional prompt text shown above the feedback controls (e.g. "Was this helpful?").'
|
|
198
|
+
)
|
|
199
|
+
}).describe("Detailed feedback widget configuration specifying style and optional prompt text.");
|
|
200
|
+
var FeedbackZ = z2.union([z2.boolean(), FeedbackConfigZ]).describe(
|
|
201
|
+
"Per-item feedback widget. Pass true for default thumbs feedback, false to disable, or a FeedbackConfig object for custom style and prompt."
|
|
202
|
+
);
|
|
203
|
+
var SegmentOrderingZ = z2.object({
|
|
204
|
+
type: z2.literal("segment").describe("Segment-based ordering: item order varies per user segment."),
|
|
205
|
+
segmentWeights: z2.record(z2.array(z2.string())).describe(
|
|
206
|
+
"Map of segment ID \u2192 ordered list of question IDs. Each segment gets its own item order."
|
|
207
|
+
)
|
|
208
|
+
}).describe(
|
|
209
|
+
"Segment-based ordering strategy. Each key is a segment ID and the value is the ordered list of item IDs shown to users in that segment."
|
|
210
|
+
);
|
|
211
|
+
var OrderingStrategyZ = z2.union([z2.enum(["static", "priority"]), SegmentOrderingZ]).describe(
|
|
212
|
+
'"static" = config order, "priority" = sort by item priority field descending, or a segment object for per-segment ordering.'
|
|
213
|
+
);
|
|
214
|
+
var AnswerStrategyZ = z2.object({
|
|
215
|
+
endpoint: z2.string().min(1).describe("Backend endpoint URL that generates the AI answer for this question."),
|
|
216
|
+
context: z2.array(z2.string()).optional().describe("Optional list of context keys passed to the endpoint to ground the answer."),
|
|
217
|
+
cache: z2.enum(["session", "none"]).optional().describe(
|
|
218
|
+
`"session" caches the generated answer for the user's session; "none" regenerates on each expand.`
|
|
219
|
+
),
|
|
220
|
+
fallback: z2.string().optional().describe("Static fallback answer text shown if the AI endpoint fails or times out.")
|
|
221
|
+
}).describe(
|
|
222
|
+
"AI answer generation strategy. When present, the answer is generated dynamically by calling the specified endpoint."
|
|
223
|
+
);
|
|
224
|
+
var InjectionRuleZ = z2.object({
|
|
225
|
+
trigger: DecisionStrategyZ.describe(
|
|
226
|
+
"Decision strategy that evaluates to true when the items should be injected (e.g. page URL matches checkout)."
|
|
227
|
+
),
|
|
228
|
+
items: z2.array(z2.lazy(() => FAQQuestionSchema)).describe("FAQ question items to inject when the trigger fires."),
|
|
229
|
+
position: z2.enum(["prepend", "append"]).optional().describe('"prepend" inserts injected items at the top; "append" adds them at the bottom.'),
|
|
230
|
+
once: z2.boolean().optional().describe("When true, items are injected only on the first trigger match and not again.")
|
|
231
|
+
}).describe(
|
|
232
|
+
"Dynamic injection rule: adds contextual FAQ items when a trigger condition is met (e.g. user is on the checkout page)."
|
|
233
|
+
);
|
|
234
|
+
var FAQQuestionSchema = z2.object({
|
|
235
|
+
...AuthoringFieldsZ,
|
|
236
|
+
kind: z2.literal("faq:question").describe(
|
|
237
|
+
"Compositional action type for a single FAQ accordion item. Rendered by the adaptive-faq:accordion widget."
|
|
238
|
+
),
|
|
239
|
+
config: z2.object({
|
|
240
|
+
/** Unique identifier for this question */
|
|
241
|
+
id: z2.string().min(1, "ID is required"),
|
|
242
|
+
/** The question text */
|
|
243
|
+
question: z2.string().min(1, "Question is required").describe("The question text shown in the accordion header."),
|
|
244
|
+
/** The answer content (plain string, rich HTML, or enhanced markdown) */
|
|
245
|
+
answer: FAQAnswerZ,
|
|
246
|
+
/** Optional category for grouping */
|
|
247
|
+
category: z2.string().optional().describe(
|
|
248
|
+
"Optional grouping label. Questions with the same category are collapsible under a shared section header."
|
|
249
|
+
),
|
|
250
|
+
/** Optional priority for ordering */
|
|
251
|
+
priority: z2.number().optional().describe(
|
|
252
|
+
'Numeric priority used when ordering="priority". Higher values appear first. Omit for equal priority.'
|
|
253
|
+
),
|
|
254
|
+
/** Optional AI answer generation strategy */
|
|
255
|
+
answerStrategy: AnswerStrategyZ.optional().describe(
|
|
256
|
+
"Optional AI answer generation config. When set, the answer field is used as a fallback only."
|
|
257
|
+
)
|
|
258
|
+
}).describe(
|
|
259
|
+
"Per-question configuration including the question text, answer content, and optional metadata."
|
|
260
|
+
),
|
|
261
|
+
/** Per-item activation strategy (null = always show) */
|
|
262
|
+
triggerWhen: TriggerWhenZ.describe(
|
|
263
|
+
"Conditional visibility strategy. When null or omitted, the question is always shown. Use a rules strategy to show/hide based on page URL, event counts, or session metrics."
|
|
264
|
+
),
|
|
265
|
+
/** Toast config when triggerWhen transitions false → true. Required when triggerWhen is set (use null to opt out). */
|
|
266
|
+
notify: NotifyZ.describe(
|
|
267
|
+
"Toast notification shown when triggerWhen transitions false \u2192 true. Required when triggerWhen is set \u2014 pass null to opt out of the toast."
|
|
268
|
+
)
|
|
269
|
+
}).refine((data) => !data.triggerWhen || data.notify !== void 0, {
|
|
270
|
+
message: "notify is required when triggerWhen is present (use null to opt out of notifications)"
|
|
128
271
|
});
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.
|
|
145
|
-
|
|
272
|
+
var configSchema = z2.object({
|
|
273
|
+
/** Display title for the FAQ widget */
|
|
274
|
+
title: z2.string().optional().describe("Optional header text shown above the accordion list."),
|
|
275
|
+
/** Whether only one or multiple questions can be expanded at once */
|
|
276
|
+
expandBehavior: z2.enum(["single", "multiple"]).default("single").describe(
|
|
277
|
+
'"single" collapses any open item before expanding another; "multiple" allows any number open at once.'
|
|
278
|
+
),
|
|
279
|
+
/** Whether to show a search/filter input */
|
|
280
|
+
searchable: z2.boolean().default(false).describe("When true, renders a text input that filters questions by keyword."),
|
|
281
|
+
/** Color theme */
|
|
282
|
+
theme: z2.enum(["light", "dark", "auto"]).default("auto").describe(
|
|
283
|
+
`"auto" follows the user's OS preference; use "light" or "dark" to pin the theme.`
|
|
284
|
+
),
|
|
285
|
+
/** FAQ questions (compositional actions) */
|
|
286
|
+
actions: z2.array(FAQQuestionSchema).default([]).describe(
|
|
287
|
+
"The FAQ items rendered by the accordion. Each item is a faq:question compositional action \u2014 these are configuration data rendered by the widget, not actions executed by the runtime. This compositional pattern enables per-item conditional visibility via triggerWhen, category grouping, and dynamic injection."
|
|
288
|
+
),
|
|
289
|
+
/** Feedback widget configuration */
|
|
290
|
+
feedback: FeedbackZ.optional().describe(
|
|
291
|
+
"Optional per-item feedback widget. Enable with true (default thumbs style) or a FeedbackConfig for custom style."
|
|
292
|
+
),
|
|
293
|
+
/** Question ordering strategy */
|
|
294
|
+
ordering: OrderingStrategyZ.optional().describe(
|
|
295
|
+
'Controls the display order of items. Omit for static (config) order, "priority" to sort by priority field, or a segment object for per-segment ordering.'
|
|
296
|
+
),
|
|
297
|
+
/** Dynamic FAQ injection rules */
|
|
298
|
+
injections: z2.array(InjectionRuleZ).optional().describe(
|
|
299
|
+
"Optional injection rules that dynamically add FAQ items when trigger conditions are met (e.g. inject checkout FAQs on the checkout page)."
|
|
300
|
+
)
|
|
301
|
+
}).describe(
|
|
302
|
+
"Props for the adaptive-faq:accordion tile widget. Configures the FAQ accordion including items, expand behavior, search, feedback, and ordering."
|
|
303
|
+
);
|
|
304
|
+
var ScrollToFaqSchema = z2.object({
|
|
305
|
+
...AuthoringFieldsZ,
|
|
306
|
+
kind: z2.literal("faq:scroll_to").describe("Scrolls the viewport to a specific FAQ item and optionally expands it."),
|
|
307
|
+
itemId: z2.string().optional().describe("Target question ID. Use this or itemQuestion (at least one required)."),
|
|
308
|
+
itemQuestion: z2.string().optional().describe(
|
|
309
|
+
"Target question text for fuzzy matching. Use this or itemId (at least one required)."
|
|
310
|
+
),
|
|
311
|
+
expand: z2.boolean().optional().describe("When true (default), expands the item after scrolling into view."),
|
|
312
|
+
behavior: z2.enum(["smooth", "instant", "auto"]).optional().describe('"smooth" animates the scroll; "instant" jumps; "auto" defers to browser default.')
|
|
313
|
+
}).refine((data) => data.itemId || data.itemQuestion, {
|
|
314
|
+
message: "Either itemId or itemQuestion is required"
|
|
146
315
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
})
|
|
158
|
-
.refine((data) => data.itemId || data.itemQuestion, {
|
|
159
|
-
message: 'Either itemId or itemQuestion is required',
|
|
316
|
+
var ToggleFaqItemSchema = z2.object({
|
|
317
|
+
...AuthoringFieldsZ,
|
|
318
|
+
kind: z2.literal("faq:toggle_item").describe("Opens, closes, or toggles the expanded state of a specific FAQ item."),
|
|
319
|
+
itemId: z2.string().optional().describe("Target question ID. Use this or itemQuestion (at least one required)."),
|
|
320
|
+
itemQuestion: z2.string().optional().describe(
|
|
321
|
+
"Target question text for fuzzy matching. Use this or itemId (at least one required)."
|
|
322
|
+
),
|
|
323
|
+
state: z2.enum(["open", "closed", "toggle"]).default("toggle").describe('"open" expands, "closed" collapses, "toggle" flips the current state.')
|
|
324
|
+
}).refine((data) => data.itemId || data.itemQuestion, {
|
|
325
|
+
message: "Either itemId or itemQuestion is required"
|
|
160
326
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
327
|
+
var UpdateFaqSchema = z2.object({
|
|
328
|
+
...AuthoringFieldsZ,
|
|
329
|
+
kind: z2.literal("faq:update").describe("Dynamically adds, removes, reorders, or replaces FAQ items at runtime."),
|
|
330
|
+
operation: z2.enum(["add", "remove", "reorder", "replace"]).describe(
|
|
331
|
+
'"add" inserts items, "remove" deletes one by ID, "reorder" applies a new ID order, "replace" swaps all items.'
|
|
332
|
+
),
|
|
333
|
+
items: z2.array(FAQQuestionSchema).optional().describe(
|
|
334
|
+
'Items to act on. Required for "add" (the items to insert) and "replace" (the full replacement set; pass [] to intentionally clear all items).'
|
|
335
|
+
),
|
|
336
|
+
itemId: z2.string().optional().describe('ID of the item to remove. Required for "remove".'),
|
|
337
|
+
order: z2.array(z2.string()).optional().describe(
|
|
338
|
+
'New display order as an ordered list of item IDs. Required for "reorder". To intentionally clear the list, use operation "replace" with items: [].'
|
|
339
|
+
),
|
|
340
|
+
position: z2.enum(["prepend", "append", "before", "after"]).optional().describe("Where to insert new items relative to existing ones or relative to anchorId."),
|
|
341
|
+
anchorId: AnchorIdZ.optional().describe(
|
|
342
|
+
'Reference item for "before" or "after" positioning. Identifies an existing FAQ item as the insertion anchor.'
|
|
343
|
+
)
|
|
344
|
+
}).describe(
|
|
345
|
+
"Runtime mutation action for the FAQ item list. Use to add contextual questions, remove outdated ones, or reorder by priority."
|
|
346
|
+
).superRefine((data, ctx) => {
|
|
347
|
+
switch (data.operation) {
|
|
348
|
+
case "add":
|
|
349
|
+
if (data.items === void 0) {
|
|
350
|
+
ctx.addIssue({
|
|
351
|
+
code: z2.ZodIssueCode.custom,
|
|
352
|
+
path: ["items"],
|
|
353
|
+
message: 'items is required when operation is "add"'
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
case "remove":
|
|
358
|
+
if (data.itemId === void 0) {
|
|
359
|
+
ctx.addIssue({
|
|
360
|
+
code: z2.ZodIssueCode.custom,
|
|
361
|
+
path: ["itemId"],
|
|
362
|
+
message: 'itemId is required when operation is "remove"'
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
case "reorder":
|
|
367
|
+
if (data.order === void 0) {
|
|
368
|
+
ctx.addIssue({
|
|
369
|
+
code: z2.ZodIssueCode.custom,
|
|
370
|
+
path: ["order"],
|
|
371
|
+
message: 'order is required when operation is "reorder" (use operation "replace" with items: [] to intentionally clear the list)'
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
case "replace":
|
|
376
|
+
if (data.items === void 0) {
|
|
377
|
+
ctx.addIssue({
|
|
378
|
+
code: z2.ZodIssueCode.custom,
|
|
379
|
+
path: ["items"],
|
|
380
|
+
message: 'items is required when operation is "replace" (pass [] to clear the list)'
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
172
385
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// ============================================================================
|
|
176
|
-
/**
|
|
177
|
-
* Validate a FAQ question action.
|
|
178
|
-
*/
|
|
179
|
-
export function validateFAQQuestion(data) {
|
|
180
|
-
return FAQQuestionSchema.safeParse(data);
|
|
386
|
+
function validateFAQQuestion(data) {
|
|
387
|
+
return FAQQuestionSchema.safeParse(data);
|
|
181
388
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
*/
|
|
185
|
-
export function validateFAQConfig(data) {
|
|
186
|
-
return configSchema.safeParse(data);
|
|
389
|
+
function validateFAQConfig(data) {
|
|
390
|
+
return configSchema.safeParse(data);
|
|
187
391
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
* The build script reads this array to merge adaptive actions into the
|
|
194
|
-
* unified canvas-config.schema.json.
|
|
195
|
-
*
|
|
196
|
-
* Note: `.innerType()` strips the `.refine()` wrapper since JSON Schema
|
|
197
|
-
* cannot express cross-field refinements; runtime Zod handles that.
|
|
198
|
-
*/
|
|
199
|
-
export const actionStepSchemas = [
|
|
200
|
-
{ defName: 'faqQuestion', schema: FAQQuestionSchema.innerType() },
|
|
201
|
-
{ defName: 'faqScrollTo', schema: ScrollToFaqSchema.innerType() },
|
|
202
|
-
{ defName: 'faqToggleItem', schema: ToggleFaqItemSchema.innerType() },
|
|
203
|
-
{ defName: 'faqUpdate', schema: UpdateFaqSchema },
|
|
392
|
+
var actionStepSchemas = [
|
|
393
|
+
{ defName: "faqQuestion", schema: FAQQuestionSchema.innerType() },
|
|
394
|
+
{ defName: "faqScrollTo", schema: ScrollToFaqSchema.innerType() },
|
|
395
|
+
{ defName: "faqToggleItem", schema: ToggleFaqItemSchema.innerType() },
|
|
396
|
+
{ defName: "faqUpdate", schema: UpdateFaqSchema.innerType() }
|
|
204
397
|
];
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// ============================================================================
|
|
208
|
-
/**
|
|
209
|
-
* Tile widget definitions for unified JSON Schema generation.
|
|
210
|
-
* Maps widget IDs to their props validation schema so the build script
|
|
211
|
-
* can inject if/then constraints on tile.props.
|
|
212
|
-
*/
|
|
213
|
-
export const tileWidgets = [
|
|
214
|
-
{ widget: 'adaptive-faq:accordion', defName: 'faqAccordionProps', propsSchema: configSchema },
|
|
398
|
+
var tileWidgets = [
|
|
399
|
+
{ widget: "adaptive-faq:accordion", defName: "faqAccordionProps", propsSchema: configSchema }
|
|
215
400
|
];
|
|
401
|
+
var CAPABILITIES_DOCUMENTATION = {
|
|
402
|
+
packageId: "adaptive-faq",
|
|
403
|
+
description: "Collapsible Q&A accordion with actions, rich content, feedback, and personalization.",
|
|
404
|
+
whenToUse: [
|
|
405
|
+
{
|
|
406
|
+
goal: "Add an FAQ accordion widget",
|
|
407
|
+
action: 'Add a tile with widget: "adaptive-faq:accordion"'
|
|
408
|
+
},
|
|
409
|
+
{ goal: "Scroll to and expand a specific FAQ item", action: "faq:scroll_to" },
|
|
410
|
+
{ goal: "Open, close, or toggle a FAQ item", action: "faq:toggle_item" },
|
|
411
|
+
{ goal: "Add, remove, reorder, or replace FAQ items", action: "faq:update" }
|
|
412
|
+
],
|
|
413
|
+
conventions: [
|
|
414
|
+
{
|
|
415
|
+
name: "Compositional action pattern",
|
|
416
|
+
description: "faq:question actions are configuration data rendered by the accordion widget, not actions executed by the runtime. This allows per-item conditional visibility via triggerWhen, category grouping under collapsible section headers, and dynamic injection of contextual items."
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "Rich answer content formats",
|
|
420
|
+
description: 'FAQ answers support three formats: plain string (simple text with basic markdown), rich HTML ({ "type": "rich", "html": "<p>...</p>" } for pre-rendered content), or enhanced markdown ({ "type": "markdown", "content": "...", "assets": [...] } with embedded media via asset:<id> URIs).'
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: "Companion overlay tooltips",
|
|
424
|
+
description: 'FAQ questions can be surfaced proactively using overlays:tooltip with ctaButtons. Set the CTA actionId to "faq:open:<questionId>" \u2014 the FAQ widget listens for action.tooltip_cta_clicked events with this pattern and expands the matching question. Late-mount support: if the widget mounts after the click, it checks EventBus history (within 10 seconds) and auto-expands.'
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: "Feedback events",
|
|
428
|
+
description: "When feedback is enabled, user feedback events are published via context.publishEvent for analytics integration."
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
events: [
|
|
432
|
+
{
|
|
433
|
+
name: "faq:feedback_submitted",
|
|
434
|
+
when: "User submits feedback on a FAQ answer",
|
|
435
|
+
props: "{ questionId, style, value }"
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
};
|
|
439
|
+
export {
|
|
440
|
+
CAPABILITIES_DOCUMENTATION,
|
|
441
|
+
FAQQuestionSchema,
|
|
442
|
+
ScrollToFaqSchema,
|
|
443
|
+
ToggleFaqItemSchema,
|
|
444
|
+
UpdateFaqSchema,
|
|
445
|
+
actionStepSchemas,
|
|
446
|
+
configSchema,
|
|
447
|
+
tileWidgets,
|
|
448
|
+
validateFAQConfig,
|
|
449
|
+
validateFAQQuestion
|
|
450
|
+
};
|
|
451
|
+
//# sourceMappingURL=schema.js.map
|