@syntrologie/runtime-sdk 2.2.0-canary.8 → 2.2.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/README.md +2 -1
- package/dist/actions/types.d.ts +7 -0
- package/dist/antiFlicker.d.ts +2 -0
- package/dist/apps/builtinRuntimeModules.generated.d.ts +20 -0
- package/dist/{chunk-MEBUEMEZ.js → chunk-V4MDQX67.js} +2868 -1417
- package/dist/chunk-V4MDQX67.js.map +7 -0
- package/dist/configFetcher.d.ts +3 -1
- package/dist/context/ContextManager.d.ts +4 -0
- package/dist/diagnostics/service-worker-check.d.ts +23 -0
- package/dist/editorLoader.d.ts +8 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1563 -12
- package/dist/index.js.map +4 -4
- package/dist/integrations/gtm-bridge.d.ts +36 -0
- package/dist/navigation/NavigationMonitor.d.ts +45 -0
- package/dist/overlays/runtime/utils/AnchorWatcher.d.ts +22 -0
- package/dist/overlays/types.d.ts +2 -0
- package/dist/react.js +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/smart-canvas.esm.js +62 -36
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +15828 -23049
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +62 -36
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/telemetry/adapters/posthog.d.ts +19 -0
- package/dist/telemetry/consent.d.ts +62 -0
- package/dist/version.d.ts +1 -1
- package/dist/widgets/WidgetRegistry.d.ts +10 -0
- package/package.json +13 -4
- package/schema/canvas-config.schema.json +124 -22
- package/scripts/syntroReactPlugin.mjs +113 -0
- package/dist/adaptives/adaptive-chatbot/index.js +0 -9
- package/dist/adaptives/adaptive-chatbot/index.js.map +0 -7
- package/dist/adaptives/adaptive-content/index.js +0 -22
- package/dist/adaptives/adaptive-content/index.js.map +0 -7
- package/dist/adaptives/adaptive-faq/index.js +0 -28
- package/dist/adaptives/adaptive-faq/index.js.map +0 -7
- package/dist/adaptives/adaptive-gamification/index.js +0 -2
- package/dist/adaptives/adaptive-gamification/index.js.map +0 -7
- package/dist/adaptives/adaptive-nav/index.js +0 -27
- package/dist/adaptives/adaptive-nav/index.js.map +0 -7
- package/dist/adaptives/adaptive-overlays/index.js +0 -94
- package/dist/adaptives/adaptive-overlays/index.js.map +0 -7
- package/dist/chunk-MEBUEMEZ.js.map +0 -7
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
EventBus,
|
|
10
10
|
ExecutorRegistry,
|
|
11
11
|
MAX_VISIBLE_TOASTS,
|
|
12
|
+
NavigationMonitor,
|
|
12
13
|
NotificationToastStack,
|
|
13
14
|
RUNTIME_VERSION,
|
|
14
15
|
RuntimeProvider,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
WidgetRegistry,
|
|
29
30
|
appRegistry,
|
|
30
31
|
applyStaticSlotStyles,
|
|
32
|
+
base,
|
|
31
33
|
border,
|
|
32
34
|
brand,
|
|
33
35
|
cleanupAppContext,
|
|
@@ -73,6 +75,8 @@ import {
|
|
|
73
75
|
normalizePostHogEvent,
|
|
74
76
|
playEnterAnimation,
|
|
75
77
|
playExitAnimation,
|
|
78
|
+
purple,
|
|
79
|
+
red,
|
|
76
80
|
registerConfigPredicates,
|
|
77
81
|
registerSmartCanvasElement,
|
|
78
82
|
resolveConfigUri,
|
|
@@ -97,7 +101,7 @@ import {
|
|
|
97
101
|
validateAction,
|
|
98
102
|
validateActions,
|
|
99
103
|
widgetRegistry
|
|
100
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-V4MDQX67.js";
|
|
101
105
|
import {
|
|
102
106
|
AddClassZ,
|
|
103
107
|
BadgePositionZ,
|
|
@@ -133,33 +137,1573 @@ import {
|
|
|
133
137
|
} from "./chunk-AYTRRBR5.js";
|
|
134
138
|
|
|
135
139
|
// src/index.ts
|
|
136
|
-
import
|
|
140
|
+
import React4 from "react";
|
|
137
141
|
import { createPortal as createPortal2, flushSync } from "react-dom";
|
|
138
142
|
import * as ReactDOMClient from "react-dom/client";
|
|
139
143
|
|
|
144
|
+
// ../adaptives/adaptive-chatbot/dist/ChatAssistant.js
|
|
145
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
146
|
+
import React, { useEffect, useRef as useRef2 } from "react";
|
|
147
|
+
import { createRoot } from "react-dom/client";
|
|
148
|
+
|
|
149
|
+
// ../adaptives/adaptive-chatbot/dist/useChat.js
|
|
150
|
+
import { useCallback, useRef, useState } from "react";
|
|
151
|
+
|
|
152
|
+
// ../adaptives/adaptive-chatbot/dist/actionParser.js
|
|
153
|
+
var JSON_FENCE_RE = /```json\s*\n([\s\S]*?)```/g;
|
|
154
|
+
function parseActions(input) {
|
|
155
|
+
const actions = [];
|
|
156
|
+
let displayText = input;
|
|
157
|
+
const matches = [];
|
|
158
|
+
let match;
|
|
159
|
+
JSON_FENCE_RE.lastIndex = 0;
|
|
160
|
+
while ((match = JSON_FENCE_RE.exec(input)) !== null) {
|
|
161
|
+
matches.push({
|
|
162
|
+
fullMatch: match[0],
|
|
163
|
+
json: match[1],
|
|
164
|
+
index: match.index
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
168
|
+
const { fullMatch, json } = matches[i];
|
|
169
|
+
let parsed;
|
|
170
|
+
try {
|
|
171
|
+
parsed = JSON.parse(json);
|
|
172
|
+
} catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (typeof parsed === "object" && parsed !== null && "kind" in parsed && typeof parsed.kind === "string") {
|
|
176
|
+
actions.unshift(parsed);
|
|
177
|
+
displayText = displayText.replace(fullMatch, "");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
displayText = displayText.replace(/\n{3,}/g, "\n\n").trim();
|
|
181
|
+
return { displayText, actions };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ../adaptives/adaptive-chatbot/dist/apiClient.js
|
|
185
|
+
var AuthError = class extends Error {
|
|
186
|
+
constructor(message) {
|
|
187
|
+
super(message);
|
|
188
|
+
this.name = "AuthError";
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
function getAuthHeaders() {
|
|
192
|
+
const cookieMatch = document.cookie.match(/stytch_session_jwt=([^;]*)/);
|
|
193
|
+
if (!cookieMatch || !cookieMatch[1]) {
|
|
194
|
+
throw new AuthError("No authentication token found");
|
|
195
|
+
}
|
|
196
|
+
const workspaceId = localStorage.getItem("syntrologie_workspace_id");
|
|
197
|
+
if (!workspaceId) {
|
|
198
|
+
throw new AuthError("No workspace ID found");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
Authorization: `Bearer ${cookieMatch[1]}`,
|
|
202
|
+
"X-Workspace-Id": workspaceId
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async function sendMessage(url, request) {
|
|
206
|
+
const authHeaders = getAuthHeaders();
|
|
207
|
+
const response = await fetch(url, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: {
|
|
210
|
+
"Content-Type": "application/json",
|
|
211
|
+
...authHeaders
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify(request)
|
|
214
|
+
});
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
if (response.status === 401) {
|
|
217
|
+
throw new AuthError("Session expired or unauthorized");
|
|
218
|
+
}
|
|
219
|
+
throw new Error(`Chat request failed: ${response.status} ${response.statusText}`);
|
|
220
|
+
}
|
|
221
|
+
return response.json();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ../adaptives/adaptive-chatbot/dist/useChat.js
|
|
225
|
+
var nextId = 0;
|
|
226
|
+
function generateId() {
|
|
227
|
+
return `msg-${Date.now()}-${++nextId}`;
|
|
228
|
+
}
|
|
229
|
+
function useChat(options) {
|
|
230
|
+
const { backendUrl, tileId, runtime: runtime7, greeting, maxHistory = 20, mlflowRunId, config } = options;
|
|
231
|
+
const [messages, setMessages] = useState(() => {
|
|
232
|
+
if (greeting) {
|
|
233
|
+
return [
|
|
234
|
+
{
|
|
235
|
+
id: generateId(),
|
|
236
|
+
role: "assistant",
|
|
237
|
+
text: greeting,
|
|
238
|
+
timestamp: Date.now()
|
|
239
|
+
}
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
return [];
|
|
243
|
+
});
|
|
244
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
245
|
+
const [error, setError] = useState(null);
|
|
246
|
+
const batchHandleRef = useRef(null);
|
|
247
|
+
const send = useCallback(async (text) => {
|
|
248
|
+
var _a;
|
|
249
|
+
const trimmed = text.trim();
|
|
250
|
+
if (!trimmed)
|
|
251
|
+
return;
|
|
252
|
+
setError(null);
|
|
253
|
+
const userMessage = {
|
|
254
|
+
id: generateId(),
|
|
255
|
+
role: "user",
|
|
256
|
+
text: trimmed,
|
|
257
|
+
timestamp: Date.now()
|
|
258
|
+
};
|
|
259
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
260
|
+
setIsLoading(true);
|
|
261
|
+
try {
|
|
262
|
+
const currentMessages = [...messages, userMessage];
|
|
263
|
+
const historySlice = currentMessages.slice(-maxHistory).map((m) => ({
|
|
264
|
+
role: m.role,
|
|
265
|
+
content: m.text
|
|
266
|
+
}));
|
|
267
|
+
const response = await sendMessage(backendUrl, {
|
|
268
|
+
message: trimmed,
|
|
269
|
+
history: historySlice,
|
|
270
|
+
mlflow_run_id: mlflowRunId,
|
|
271
|
+
current_config: config
|
|
272
|
+
});
|
|
273
|
+
const { displayText, actions } = parseActions(response.response);
|
|
274
|
+
const assistantMessage = {
|
|
275
|
+
id: generateId(),
|
|
276
|
+
role: "assistant",
|
|
277
|
+
text: displayText,
|
|
278
|
+
timestamp: Date.now()
|
|
279
|
+
};
|
|
280
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
281
|
+
if (actions.length > 0) {
|
|
282
|
+
if ((_a = batchHandleRef.current) == null ? void 0 : _a.isApplied()) {
|
|
283
|
+
await batchHandleRef.current.revertAll();
|
|
284
|
+
}
|
|
285
|
+
batchHandleRef.current = await runtime7.actions.applyBatch(actions);
|
|
286
|
+
runtime7.events.publish("chatbot.actions_applied", {
|
|
287
|
+
count: actions.length,
|
|
288
|
+
kinds: actions.map((a) => a.kind),
|
|
289
|
+
tileId
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
} catch (err) {
|
|
293
|
+
const message = err instanceof Error ? err.message : "An unexpected error occurred";
|
|
294
|
+
setError(message);
|
|
295
|
+
} finally {
|
|
296
|
+
setIsLoading(false);
|
|
297
|
+
}
|
|
298
|
+
}, [backendUrl, messages, maxHistory, mlflowRunId, config, runtime7, tileId]);
|
|
299
|
+
const clearMessages = useCallback(() => {
|
|
300
|
+
var _a;
|
|
301
|
+
setMessages([]);
|
|
302
|
+
setError(null);
|
|
303
|
+
if ((_a = batchHandleRef.current) == null ? void 0 : _a.isApplied()) {
|
|
304
|
+
batchHandleRef.current.revertAll();
|
|
305
|
+
batchHandleRef.current = null;
|
|
306
|
+
}
|
|
307
|
+
sessionStorage.removeItem(`syntro:chatbot:history:${tileId}`);
|
|
308
|
+
}, [tileId]);
|
|
309
|
+
return {
|
|
310
|
+
messages,
|
|
311
|
+
isLoading,
|
|
312
|
+
error,
|
|
313
|
+
sendMessage: send,
|
|
314
|
+
clearMessages
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ../adaptives/adaptive-chatbot/dist/ChatAssistant.js
|
|
319
|
+
var styles = {
|
|
320
|
+
container: {
|
|
321
|
+
display: "flex",
|
|
322
|
+
flexDirection: "column",
|
|
323
|
+
height: "100%",
|
|
324
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
325
|
+
fontSize: "14px",
|
|
326
|
+
touchAction: "none"
|
|
327
|
+
},
|
|
328
|
+
messageList: {
|
|
329
|
+
flex: 1,
|
|
330
|
+
overflowY: "auto",
|
|
331
|
+
padding: "12px",
|
|
332
|
+
display: "flex",
|
|
333
|
+
flexDirection: "column",
|
|
334
|
+
gap: "8px"
|
|
335
|
+
},
|
|
336
|
+
messageBubble: {
|
|
337
|
+
maxWidth: "85%",
|
|
338
|
+
padding: "8px 12px",
|
|
339
|
+
borderRadius: "12px",
|
|
340
|
+
lineHeight: 1.5,
|
|
341
|
+
wordBreak: "break-word"
|
|
342
|
+
},
|
|
343
|
+
userMessage: {
|
|
344
|
+
alignSelf: "flex-end",
|
|
345
|
+
backgroundColor: purple[4],
|
|
346
|
+
color: base.white,
|
|
347
|
+
borderBottomRightRadius: "4px"
|
|
348
|
+
},
|
|
349
|
+
assistantMessage: {
|
|
350
|
+
alignSelf: "flex-start",
|
|
351
|
+
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
|
352
|
+
color: slateGrey[10],
|
|
353
|
+
borderBottomLeftRadius: "4px"
|
|
354
|
+
},
|
|
355
|
+
loadingDots: {
|
|
356
|
+
alignSelf: "flex-start",
|
|
357
|
+
padding: "8px 16px",
|
|
358
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)",
|
|
359
|
+
borderRadius: "12px",
|
|
360
|
+
color: slateGrey[8],
|
|
361
|
+
fontSize: "13px"
|
|
362
|
+
},
|
|
363
|
+
errorBanner: {
|
|
364
|
+
padding: "8px 12px",
|
|
365
|
+
backgroundColor: "rgba(239, 68, 68, 0.1)",
|
|
366
|
+
color: red[4],
|
|
367
|
+
fontSize: "13px",
|
|
368
|
+
borderRadius: "8px",
|
|
369
|
+
margin: "0 12px"
|
|
370
|
+
},
|
|
371
|
+
inputForm: {
|
|
372
|
+
display: "flex",
|
|
373
|
+
gap: "8px",
|
|
374
|
+
padding: "12px",
|
|
375
|
+
borderTop: "1px solid rgba(255, 255, 255, 0.06)"
|
|
376
|
+
},
|
|
377
|
+
input: {
|
|
378
|
+
flex: 1,
|
|
379
|
+
padding: "8px 12px",
|
|
380
|
+
borderRadius: "8px",
|
|
381
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
382
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
383
|
+
color: slateGrey[12],
|
|
384
|
+
fontSize: "14px",
|
|
385
|
+
outline: "none",
|
|
386
|
+
fontFamily: "inherit"
|
|
387
|
+
},
|
|
388
|
+
sendButton: {
|
|
389
|
+
padding: "8px 16px",
|
|
390
|
+
borderRadius: "8px",
|
|
391
|
+
border: "none",
|
|
392
|
+
backgroundColor: purple[4],
|
|
393
|
+
color: base.white,
|
|
394
|
+
fontWeight: 600,
|
|
395
|
+
fontSize: "13px",
|
|
396
|
+
cursor: "pointer",
|
|
397
|
+
whiteSpace: "nowrap"
|
|
398
|
+
},
|
|
399
|
+
sendButtonDisabled: {
|
|
400
|
+
opacity: 0.5,
|
|
401
|
+
cursor: "not-allowed"
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
function MessageBubble({ message }) {
|
|
405
|
+
const isUser = message.role === "user";
|
|
406
|
+
const bubbleStyle = {
|
|
407
|
+
...styles.messageBubble,
|
|
408
|
+
...isUser ? styles.userMessage : styles.assistantMessage
|
|
409
|
+
};
|
|
410
|
+
return _jsx("div", { style: bubbleStyle, children: message.text });
|
|
411
|
+
}
|
|
412
|
+
function ChatAssistant({ config, runtime: runtime7, tileId }) {
|
|
413
|
+
const { messages, isLoading, error, sendMessage: sendMessage2, clearMessages: _clearMessages } = useChat({
|
|
414
|
+
backendUrl: config.backendUrl,
|
|
415
|
+
tileId,
|
|
416
|
+
runtime: runtime7,
|
|
417
|
+
greeting: config.greeting,
|
|
418
|
+
maxHistory: config.maxHistory,
|
|
419
|
+
mlflowRunId: config.mlflowRunId
|
|
420
|
+
});
|
|
421
|
+
const messageListRef = useRef2(null);
|
|
422
|
+
const inputRef = useRef2(null);
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
if (messageListRef.current) {
|
|
425
|
+
messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
|
|
426
|
+
}
|
|
427
|
+
}, []);
|
|
428
|
+
const handleSubmit = (e) => {
|
|
429
|
+
e.preventDefault();
|
|
430
|
+
const input = inputRef.current;
|
|
431
|
+
if (!input || !input.value.trim() || isLoading)
|
|
432
|
+
return;
|
|
433
|
+
const text = input.value;
|
|
434
|
+
input.value = "";
|
|
435
|
+
sendMessage2(text);
|
|
436
|
+
};
|
|
437
|
+
return _jsxs("div", { style: styles.container, "data-testid": "chat-assistant", children: [_jsxs("div", { ref: messageListRef, style: styles.messageList, children: [messages.map((msg) => _jsx(MessageBubble, { message: msg }, msg.id)), isLoading && _jsx("div", { style: styles.loadingDots, children: "Thinking..." })] }), error && _jsx("div", { style: styles.errorBanner, children: error }), _jsxs("form", { onSubmit: handleSubmit, style: styles.inputForm, children: [_jsx("input", { ref: inputRef, style: styles.input, placeholder: "Ask anything...", disabled: isLoading, "data-testid": "chat-input" }), _jsx("button", { type: "submit", disabled: isLoading, style: {
|
|
438
|
+
...styles.sendButton,
|
|
439
|
+
...isLoading ? styles.sendButtonDisabled : {}
|
|
440
|
+
}, "data-testid": "chat-send", children: "Send" })] })] });
|
|
441
|
+
}
|
|
442
|
+
var ChatAssistantMountableWidget = {
|
|
443
|
+
mount(container, mountConfig) {
|
|
444
|
+
const { config, runtime: runtime7, tileId = "chatbot-widget" } = mountConfig || {};
|
|
445
|
+
if (config && runtime7 && typeof createRoot === "function") {
|
|
446
|
+
const root = createRoot(container);
|
|
447
|
+
root.render(React.createElement(ChatAssistant, {
|
|
448
|
+
config,
|
|
449
|
+
runtime: runtime7,
|
|
450
|
+
tileId
|
|
451
|
+
}));
|
|
452
|
+
return () => {
|
|
453
|
+
root.unmount();
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (!config || !runtime7) {
|
|
457
|
+
container.innerHTML = `<div style="padding: 16px; color: ${slateGrey[8]};">Chat widget requires config and runtime.</div>`;
|
|
458
|
+
return () => {
|
|
459
|
+
container.innerHTML = "";
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
container.innerHTML = `
|
|
463
|
+
<div style="padding: 16px; font-family: system-ui; color: ${slateGrey[10]};">
|
|
464
|
+
<p style="margin: 0 0 8px; color: ${slateGrey[8]};">${config.greeting || "Hi! How can I help?"}</p>
|
|
465
|
+
<p style="margin: 0; font-size: 12px; color: ${slateGrey[7]};">Chat widget mounted. Awaiting React renderer.</p>
|
|
466
|
+
</div>
|
|
467
|
+
`;
|
|
468
|
+
return () => {
|
|
469
|
+
container.innerHTML = "";
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// ../adaptives/adaptive-chatbot/dist/runtime.js
|
|
475
|
+
var runtime3 = {
|
|
476
|
+
id: "adaptive-chatbot",
|
|
477
|
+
version: "1.0.0",
|
|
478
|
+
name: "Chat Assistant",
|
|
479
|
+
description: "AI chat assistant with action execution capabilities",
|
|
480
|
+
/** No action executors — chatbot uses existing action kinds via applyBatch */
|
|
481
|
+
executors: [],
|
|
482
|
+
/** Widget definitions for the runtime's WidgetRegistry */
|
|
483
|
+
widgets: [
|
|
484
|
+
{
|
|
485
|
+
id: "adaptive-chatbot:assistant",
|
|
486
|
+
component: ChatAssistantMountableWidget,
|
|
487
|
+
metadata: {
|
|
488
|
+
name: "Chat Assistant",
|
|
489
|
+
description: "AI-powered chat assistant that can execute DOM actions",
|
|
490
|
+
icon: "\u{1F4AC}"
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// ../adaptives/adaptive-faq/dist/executors.js
|
|
497
|
+
function resolveItem(store, itemId, itemQuestion) {
|
|
498
|
+
if (itemId) {
|
|
499
|
+
const found = store.getState().items.find((i) => i.config.id === itemId);
|
|
500
|
+
if (found)
|
|
501
|
+
return found;
|
|
502
|
+
}
|
|
503
|
+
if (itemQuestion) {
|
|
504
|
+
const found = store.findByQuestion(itemQuestion);
|
|
505
|
+
if (found)
|
|
506
|
+
return found;
|
|
507
|
+
}
|
|
508
|
+
throw new Error("FAQ item not found");
|
|
509
|
+
}
|
|
510
|
+
async function executeScrollToFaq(action, context, store) {
|
|
511
|
+
var _a;
|
|
512
|
+
const item = resolveItem(store, action.itemId, action.itemQuestion);
|
|
513
|
+
const { id } = item.config;
|
|
514
|
+
if (action.expand !== false) {
|
|
515
|
+
store.expand(id);
|
|
516
|
+
}
|
|
517
|
+
const el = document.querySelector(`[data-faq-item-id="${id}"]`);
|
|
518
|
+
if (el) {
|
|
519
|
+
el.scrollIntoView({
|
|
520
|
+
behavior: (_a = action.behavior) != null ? _a : "smooth"
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
context.publishEvent("faq:scroll_to", { itemId: id });
|
|
524
|
+
return {
|
|
525
|
+
cleanup: () => {
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
async function executeToggleFaqItem(action, context, store) {
|
|
530
|
+
var _a;
|
|
531
|
+
const item = resolveItem(store, action.itemId, action.itemQuestion);
|
|
532
|
+
const { id } = item.config;
|
|
533
|
+
const desiredState = (_a = action.state) != null ? _a : "toggle";
|
|
534
|
+
let newState;
|
|
535
|
+
switch (desiredState) {
|
|
536
|
+
case "open":
|
|
537
|
+
store.expand(id);
|
|
538
|
+
newState = "open";
|
|
539
|
+
break;
|
|
540
|
+
case "closed":
|
|
541
|
+
store.collapse(id);
|
|
542
|
+
newState = "closed";
|
|
543
|
+
break;
|
|
544
|
+
default: {
|
|
545
|
+
const wasExpanded = store.getState().expandedItems.has(id);
|
|
546
|
+
store.toggle(id);
|
|
547
|
+
newState = wasExpanded ? "closed" : "open";
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
context.publishEvent("faq:toggle", { itemId: id, newState });
|
|
552
|
+
return {
|
|
553
|
+
cleanup: () => {
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
async function executeUpdateFaq(action, context, store) {
|
|
558
|
+
var _a, _b, _c;
|
|
559
|
+
switch (action.operation) {
|
|
560
|
+
case "add": {
|
|
561
|
+
const items = (_a = action.items) != null ? _a : [];
|
|
562
|
+
const position = action.position === "prepend" ? "prepend" : "append";
|
|
563
|
+
store.addItems(items, position);
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
case "remove": {
|
|
567
|
+
if (!action.itemId) {
|
|
568
|
+
throw new Error("FAQ item not found");
|
|
569
|
+
}
|
|
570
|
+
const exists = store.getState().items.some((i) => i.config.id === action.itemId);
|
|
571
|
+
if (!exists) {
|
|
572
|
+
throw new Error("FAQ item not found");
|
|
573
|
+
}
|
|
574
|
+
store.removeItem(action.itemId);
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
case "reorder": {
|
|
578
|
+
const order = (_b = action.order) != null ? _b : [];
|
|
579
|
+
store.reorderItems(order);
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
case "replace": {
|
|
583
|
+
const items = (_c = action.items) != null ? _c : [];
|
|
584
|
+
store.replaceItems(items);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
context.publishEvent("faq:update", { operation: action.operation });
|
|
589
|
+
return {
|
|
590
|
+
cleanup: () => {
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
var executorDefinitions = [
|
|
595
|
+
{ kind: "faq:scroll_to", executor: executeScrollToFaq },
|
|
596
|
+
{ kind: "faq:toggle_item", executor: executeToggleFaqItem },
|
|
597
|
+
{ kind: "faq:update", executor: executeUpdateFaq }
|
|
598
|
+
];
|
|
599
|
+
|
|
600
|
+
// ../adaptives/adaptive-faq/dist/FAQWidget.js
|
|
601
|
+
import { jsx as _jsx2, jsxs as _jsxs2 } from "react/jsx-runtime";
|
|
602
|
+
import React2, { useCallback as useCallback2, useEffect as useEffect2, useMemo, useReducer, useState as useState2 } from "react";
|
|
603
|
+
import { createRoot as createRoot2 } from "react-dom/client";
|
|
604
|
+
function getAnswerText(answer) {
|
|
605
|
+
if (typeof answer === "string")
|
|
606
|
+
return answer;
|
|
607
|
+
if (answer.type === "rich")
|
|
608
|
+
return answer.html;
|
|
609
|
+
return answer.content;
|
|
610
|
+
}
|
|
611
|
+
function renderAnswer(answer) {
|
|
612
|
+
if (typeof answer === "string") {
|
|
613
|
+
return _jsx2("p", { style: { margin: 0 }, children: answer });
|
|
614
|
+
}
|
|
615
|
+
if (answer.type === "rich") {
|
|
616
|
+
return _jsx2("div", { style: { margin: 0 }, dangerouslySetInnerHTML: { __html: answer.html } });
|
|
617
|
+
}
|
|
618
|
+
return _jsx2("p", { style: { margin: 0 }, children: answer.content });
|
|
619
|
+
}
|
|
620
|
+
function resolveFeedbackConfig(feedback) {
|
|
621
|
+
if (!feedback)
|
|
622
|
+
return null;
|
|
623
|
+
if (feedback === true) {
|
|
624
|
+
return { style: "thumbs" };
|
|
625
|
+
}
|
|
626
|
+
return feedback;
|
|
627
|
+
}
|
|
628
|
+
function getFeedbackPrompt(feedbackConfig) {
|
|
629
|
+
return feedbackConfig.prompt || "Was this helpful?";
|
|
630
|
+
}
|
|
631
|
+
var baseStyles = {
|
|
632
|
+
container: {
|
|
633
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
634
|
+
maxWidth: "800px",
|
|
635
|
+
margin: "0 auto"
|
|
636
|
+
},
|
|
637
|
+
searchWrapper: {
|
|
638
|
+
marginBottom: "16px"
|
|
639
|
+
},
|
|
640
|
+
searchInput: {
|
|
641
|
+
width: "100%",
|
|
642
|
+
padding: "12px 16px",
|
|
643
|
+
borderRadius: "8px",
|
|
644
|
+
fontSize: "14px",
|
|
645
|
+
outline: "none",
|
|
646
|
+
transition: "border-color 0.15s ease"
|
|
647
|
+
},
|
|
648
|
+
accordion: {
|
|
649
|
+
display: "flex",
|
|
650
|
+
flexDirection: "column",
|
|
651
|
+
gap: "8px"
|
|
652
|
+
},
|
|
653
|
+
item: {
|
|
654
|
+
borderRadius: "8px",
|
|
655
|
+
overflow: "hidden",
|
|
656
|
+
transition: "box-shadow 0.15s ease"
|
|
657
|
+
},
|
|
658
|
+
question: {
|
|
659
|
+
width: "100%",
|
|
660
|
+
padding: "16px 20px",
|
|
661
|
+
display: "flex",
|
|
662
|
+
alignItems: "center",
|
|
663
|
+
justifyContent: "space-between",
|
|
664
|
+
border: "none",
|
|
665
|
+
cursor: "pointer",
|
|
666
|
+
fontSize: "15px",
|
|
667
|
+
fontWeight: 500,
|
|
668
|
+
textAlign: "left",
|
|
669
|
+
transition: "background-color 0.15s ease"
|
|
670
|
+
},
|
|
671
|
+
chevron: {
|
|
672
|
+
fontSize: "18px",
|
|
673
|
+
transition: "transform 0.2s ease"
|
|
674
|
+
},
|
|
675
|
+
answer: {
|
|
676
|
+
padding: "0 20px 16px 20px",
|
|
677
|
+
fontSize: "14px",
|
|
678
|
+
lineHeight: 1.6,
|
|
679
|
+
overflow: "hidden",
|
|
680
|
+
transition: "max-height 0.2s ease, padding 0.2s ease"
|
|
681
|
+
},
|
|
682
|
+
category: {
|
|
683
|
+
display: "inline-block",
|
|
684
|
+
fontSize: "11px",
|
|
685
|
+
fontWeight: 600,
|
|
686
|
+
textTransform: "uppercase",
|
|
687
|
+
letterSpacing: "0.05em",
|
|
688
|
+
padding: "4px 8px",
|
|
689
|
+
borderRadius: "4px",
|
|
690
|
+
marginBottom: "8px"
|
|
691
|
+
},
|
|
692
|
+
categoryHeader: {
|
|
693
|
+
fontSize: "13px",
|
|
694
|
+
fontWeight: 700,
|
|
695
|
+
textTransform: "uppercase",
|
|
696
|
+
letterSpacing: "0.05em",
|
|
697
|
+
padding: "12px 4px 6px 4px",
|
|
698
|
+
marginTop: "8px"
|
|
699
|
+
},
|
|
700
|
+
feedback: {
|
|
701
|
+
display: "flex",
|
|
702
|
+
alignItems: "center",
|
|
703
|
+
gap: "8px",
|
|
704
|
+
marginTop: "12px",
|
|
705
|
+
paddingTop: "10px",
|
|
706
|
+
borderTop: "1px solid rgba(0, 0, 0, 0.08)",
|
|
707
|
+
fontSize: "13px"
|
|
708
|
+
},
|
|
709
|
+
feedbackButton: {
|
|
710
|
+
background: "none",
|
|
711
|
+
border: "1px solid transparent",
|
|
712
|
+
cursor: "pointer",
|
|
713
|
+
fontSize: "16px",
|
|
714
|
+
padding: "4px 8px",
|
|
715
|
+
borderRadius: "4px",
|
|
716
|
+
transition: "background-color 0.15s ease, border-color 0.15s ease"
|
|
717
|
+
},
|
|
718
|
+
feedbackButtonSelected: {
|
|
719
|
+
borderColor: "rgba(0, 0, 0, 0.2)",
|
|
720
|
+
backgroundColor: "rgba(0, 0, 0, 0.04)"
|
|
721
|
+
},
|
|
722
|
+
emptyState: {
|
|
723
|
+
textAlign: "center",
|
|
724
|
+
padding: "48px 24px",
|
|
725
|
+
fontSize: "14px"
|
|
726
|
+
},
|
|
727
|
+
noResults: {
|
|
728
|
+
textAlign: "center",
|
|
729
|
+
padding: "32px 16px",
|
|
730
|
+
fontSize: "14px"
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
var themeStyles = {
|
|
734
|
+
light: {
|
|
735
|
+
container: {
|
|
736
|
+
backgroundColor: base.white,
|
|
737
|
+
color: slateGrey[1]
|
|
738
|
+
},
|
|
739
|
+
searchInput: {
|
|
740
|
+
backgroundColor: slateGrey[12],
|
|
741
|
+
border: `1px solid ${slateGrey[11]}`,
|
|
742
|
+
color: slateGrey[1]
|
|
743
|
+
},
|
|
744
|
+
item: {
|
|
745
|
+
backgroundColor: slateGrey[12],
|
|
746
|
+
border: `1px solid ${slateGrey[11]}`
|
|
747
|
+
},
|
|
748
|
+
itemExpanded: {
|
|
749
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
|
|
750
|
+
},
|
|
751
|
+
question: {
|
|
752
|
+
backgroundColor: "transparent",
|
|
753
|
+
color: slateGrey[1]
|
|
754
|
+
},
|
|
755
|
+
questionHover: {
|
|
756
|
+
backgroundColor: slateGrey[12]
|
|
757
|
+
},
|
|
758
|
+
answer: {
|
|
759
|
+
color: slateGrey[6]
|
|
760
|
+
},
|
|
761
|
+
category: {
|
|
762
|
+
backgroundColor: purple[8],
|
|
763
|
+
color: purple[2]
|
|
764
|
+
},
|
|
765
|
+
categoryHeader: {
|
|
766
|
+
color: slateGrey[7]
|
|
767
|
+
},
|
|
768
|
+
emptyState: {
|
|
769
|
+
color: slateGrey[8]
|
|
770
|
+
},
|
|
771
|
+
feedbackPrompt: {
|
|
772
|
+
color: slateGrey[7]
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
dark: {
|
|
776
|
+
container: {
|
|
777
|
+
backgroundColor: slateGrey[1],
|
|
778
|
+
color: slateGrey[12]
|
|
779
|
+
},
|
|
780
|
+
searchInput: {
|
|
781
|
+
backgroundColor: slateGrey[3],
|
|
782
|
+
border: `1px solid ${slateGrey[5]}`,
|
|
783
|
+
color: slateGrey[12]
|
|
784
|
+
},
|
|
785
|
+
item: {
|
|
786
|
+
backgroundColor: slateGrey[3],
|
|
787
|
+
border: `1px solid ${slateGrey[5]}`
|
|
788
|
+
},
|
|
789
|
+
itemExpanded: {
|
|
790
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
791
|
+
},
|
|
792
|
+
question: {
|
|
793
|
+
backgroundColor: "transparent",
|
|
794
|
+
color: slateGrey[12]
|
|
795
|
+
},
|
|
796
|
+
questionHover: {
|
|
797
|
+
backgroundColor: slateGrey[5]
|
|
798
|
+
},
|
|
799
|
+
answer: {
|
|
800
|
+
color: slateGrey[8]
|
|
801
|
+
},
|
|
802
|
+
category: {
|
|
803
|
+
backgroundColor: purple[0],
|
|
804
|
+
color: purple[6]
|
|
805
|
+
},
|
|
806
|
+
categoryHeader: {
|
|
807
|
+
color: slateGrey[8]
|
|
808
|
+
},
|
|
809
|
+
emptyState: {
|
|
810
|
+
color: slateGrey[7]
|
|
811
|
+
},
|
|
812
|
+
feedbackPrompt: {
|
|
813
|
+
color: slateGrey[8]
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackValue, onFeedback }) {
|
|
818
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
819
|
+
const colors = themeStyles[theme];
|
|
820
|
+
const { question, answer } = item.config;
|
|
821
|
+
const itemStyle = {
|
|
822
|
+
...baseStyles.item,
|
|
823
|
+
...colors.item,
|
|
824
|
+
...isExpanded ? colors.itemExpanded : {}
|
|
825
|
+
};
|
|
826
|
+
const questionStyle = {
|
|
827
|
+
...baseStyles.question,
|
|
828
|
+
...colors.question,
|
|
829
|
+
...isHovered ? colors.questionHover : {}
|
|
830
|
+
};
|
|
831
|
+
const chevronStyle = {
|
|
832
|
+
...baseStyles.chevron,
|
|
833
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)"
|
|
834
|
+
};
|
|
835
|
+
const answerStyle = {
|
|
836
|
+
...baseStyles.answer,
|
|
837
|
+
...colors.answer,
|
|
838
|
+
maxHeight: isExpanded ? "500px" : "0",
|
|
839
|
+
paddingBottom: isExpanded ? "16px" : "0"
|
|
840
|
+
};
|
|
841
|
+
const feedbackStyle = {
|
|
842
|
+
...baseStyles.feedback,
|
|
843
|
+
...colors.feedbackPrompt
|
|
844
|
+
};
|
|
845
|
+
return _jsxs2("div", { style: itemStyle, "data-faq-item-id": item.config.id, children: [_jsxs2("button", { type: "button", style: questionStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [_jsx2("span", { children: question }), _jsx2("span", { style: chevronStyle, children: "\u25BC" })] }), _jsxs2("div", { style: answerStyle, "aria-hidden": !isExpanded, children: [renderAnswer(answer), isExpanded && feedbackConfig && _jsxs2("div", { style: feedbackStyle, children: [_jsx2("span", { children: getFeedbackPrompt(feedbackConfig) }), _jsx2("button", { type: "button", style: {
|
|
846
|
+
...baseStyles.feedbackButton,
|
|
847
|
+
...feedbackValue === "up" ? baseStyles.feedbackButtonSelected : {}
|
|
848
|
+
}, "aria-label": "Thumbs up", onClick: () => onFeedback(item.config.id, question, "up"), children: "\u{1F44D}" }), _jsx2("button", { type: "button", style: {
|
|
849
|
+
...baseStyles.feedbackButton,
|
|
850
|
+
...feedbackValue === "down" ? baseStyles.feedbackButtonSelected : {}
|
|
851
|
+
}, "aria-label": "Thumbs down", onClick: () => onFeedback(item.config.id, question, "down"), children: "\u{1F44E}" })] })] })] });
|
|
852
|
+
}
|
|
853
|
+
function FAQWidget({ config, runtime: runtime7, instanceId }) {
|
|
854
|
+
const [renderTick, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
855
|
+
const [expandedIds, setExpandedIds] = useState2(/* @__PURE__ */ new Set());
|
|
856
|
+
const [searchQuery, setSearchQuery] = useState2("");
|
|
857
|
+
const [feedbackState, setFeedbackState] = useState2(/* @__PURE__ */ new Map());
|
|
858
|
+
const feedbackConfig = useMemo(() => resolveFeedbackConfig(config.feedback), [config.feedback]);
|
|
859
|
+
useEffect2(() => {
|
|
860
|
+
const unsubscribe = runtime7.context.subscribe(() => {
|
|
861
|
+
forceUpdate();
|
|
862
|
+
});
|
|
863
|
+
return unsubscribe;
|
|
864
|
+
}, [runtime7.context]);
|
|
865
|
+
useEffect2(() => {
|
|
866
|
+
var _a;
|
|
867
|
+
if (!((_a = runtime7.accumulator) == null ? void 0 : _a.subscribe))
|
|
868
|
+
return;
|
|
869
|
+
return runtime7.accumulator.subscribe(() => {
|
|
870
|
+
forceUpdate();
|
|
871
|
+
});
|
|
872
|
+
}, [runtime7.accumulator]);
|
|
873
|
+
useEffect2(() => {
|
|
874
|
+
var _a, _b;
|
|
875
|
+
if (!config.scope || !((_a = runtime7.accumulator) == null ? void 0 : _a.register))
|
|
876
|
+
return;
|
|
877
|
+
const { events: eventNames, urlContains, props: propFilters } = config.scope;
|
|
878
|
+
const keys = /* @__PURE__ */ new Set();
|
|
879
|
+
for (const action of config.actions) {
|
|
880
|
+
if (((_b = action.showWhen) == null ? void 0 : _b.type) === "rules") {
|
|
881
|
+
for (const rule of action.showWhen.rules) {
|
|
882
|
+
for (const cond of rule.conditions) {
|
|
883
|
+
if (cond.type === "event_count" && cond.key) {
|
|
884
|
+
keys.add(cond.key);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
for (const key of keys) {
|
|
891
|
+
runtime7.accumulator.register(key, (event) => {
|
|
892
|
+
var _a2, _b2, _c;
|
|
893
|
+
if (!eventNames.includes(event.name))
|
|
894
|
+
return false;
|
|
895
|
+
if (urlContains) {
|
|
896
|
+
const pathname = String((_b2 = (_a2 = event.props) == null ? void 0 : _a2.pathname) != null ? _b2 : "");
|
|
897
|
+
if (!pathname.includes(urlContains))
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
if (propFilters) {
|
|
901
|
+
for (const [k, v] of Object.entries(propFilters)) {
|
|
902
|
+
if (((_c = event.props) == null ? void 0 : _c[k]) !== v)
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return true;
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}, [config.scope, config.actions, runtime7.accumulator]);
|
|
910
|
+
useEffect2(() => {
|
|
911
|
+
if (!runtime7.events.subscribe)
|
|
912
|
+
return;
|
|
913
|
+
if (runtime7.events.getRecent) {
|
|
914
|
+
const recentEvents = runtime7.events.getRecent({ patterns: ["^action\\.tooltip_cta_clicked$", "^action\\.modal_cta_clicked$"] }, 10);
|
|
915
|
+
const pendingEvent = recentEvents.filter((e) => {
|
|
916
|
+
var _a;
|
|
917
|
+
const actionId = (_a = e.props) == null ? void 0 : _a.actionId;
|
|
918
|
+
return typeof actionId === "string" && actionId.startsWith("faq:open:");
|
|
919
|
+
}).pop();
|
|
920
|
+
if (pendingEvent && Date.now() - pendingEvent.ts < 1e4) {
|
|
921
|
+
const questionId = pendingEvent.props.actionId.replace("faq:open:", "");
|
|
922
|
+
setExpandedIds(/* @__PURE__ */ new Set([questionId]));
|
|
923
|
+
requestAnimationFrame(() => {
|
|
924
|
+
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
925
|
+
if (el)
|
|
926
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const unsubscribe = runtime7.events.subscribe({ patterns: ["^action\\.tooltip_cta_clicked$", "^action\\.modal_cta_clicked$"] }, (event) => {
|
|
931
|
+
var _a;
|
|
932
|
+
const actionId = (_a = event.props) == null ? void 0 : _a.actionId;
|
|
933
|
+
if (typeof actionId !== "string" || !actionId.startsWith("faq:open:"))
|
|
934
|
+
return;
|
|
935
|
+
const questionId = actionId.replace("faq:open:", "");
|
|
936
|
+
setExpandedIds(/* @__PURE__ */ new Set([questionId]));
|
|
937
|
+
requestAnimationFrame(() => {
|
|
938
|
+
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
939
|
+
if (el)
|
|
940
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
941
|
+
});
|
|
942
|
+
runtime7.events.publish("canvas.requestOpen");
|
|
943
|
+
});
|
|
944
|
+
return unsubscribe;
|
|
945
|
+
}, [runtime7]);
|
|
946
|
+
const visibleQuestions = useMemo(() => config.actions.filter((q) => {
|
|
947
|
+
if (!q.showWhen)
|
|
948
|
+
return true;
|
|
949
|
+
const result = runtime7.evaluateSync(q.showWhen);
|
|
950
|
+
return result.value;
|
|
951
|
+
}), [config.actions, runtime7, renderTick]);
|
|
952
|
+
const orderedQuestions = useMemo(() => {
|
|
953
|
+
if (config.ordering === "priority") {
|
|
954
|
+
return [...visibleQuestions].sort((a, b) => {
|
|
955
|
+
var _a, _b;
|
|
956
|
+
return ((_a = b.config.priority) != null ? _a : 0) - ((_b = a.config.priority) != null ? _b : 0);
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
return visibleQuestions;
|
|
960
|
+
}, [visibleQuestions, config.ordering]);
|
|
961
|
+
const filteredQuestions = useMemo(() => {
|
|
962
|
+
if (!config.searchable || !searchQuery.trim()) {
|
|
963
|
+
return orderedQuestions;
|
|
964
|
+
}
|
|
965
|
+
const query = searchQuery.toLowerCase();
|
|
966
|
+
return orderedQuestions.filter((q) => {
|
|
967
|
+
var _a;
|
|
968
|
+
return q.config.question.toLowerCase().includes(query) || getAnswerText(q.config.answer).toLowerCase().includes(query) || ((_a = q.config.category) == null ? void 0 : _a.toLowerCase().includes(query));
|
|
969
|
+
});
|
|
970
|
+
}, [orderedQuestions, searchQuery, config.searchable]);
|
|
971
|
+
const categoryGroups = useMemo(() => {
|
|
972
|
+
const groups = /* @__PURE__ */ new Map();
|
|
973
|
+
for (const q of filteredQuestions) {
|
|
974
|
+
const cat = q.config.category;
|
|
975
|
+
if (!groups.has(cat)) {
|
|
976
|
+
groups.set(cat, []);
|
|
977
|
+
}
|
|
978
|
+
groups.get(cat).push(q);
|
|
979
|
+
}
|
|
980
|
+
return groups;
|
|
981
|
+
}, [filteredQuestions]);
|
|
982
|
+
const hasCategories = useMemo(() => filteredQuestions.some((q) => q.config.category), [filteredQuestions]);
|
|
983
|
+
const resolvedTheme = useMemo(() => {
|
|
984
|
+
var _a;
|
|
985
|
+
if (config.theme && config.theme !== "auto")
|
|
986
|
+
return config.theme;
|
|
987
|
+
if (typeof window !== "undefined") {
|
|
988
|
+
return ((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-color-scheme: dark)").matches) ? "dark" : "light";
|
|
989
|
+
}
|
|
990
|
+
return "light";
|
|
991
|
+
}, [config.theme]);
|
|
992
|
+
const handleToggle = useCallback2((id) => {
|
|
993
|
+
setExpandedIds((prev) => {
|
|
994
|
+
const next = new Set(prev);
|
|
995
|
+
if (config.expandBehavior === "single") {
|
|
996
|
+
if (prev.has(id)) {
|
|
997
|
+
return /* @__PURE__ */ new Set();
|
|
998
|
+
}
|
|
999
|
+
return /* @__PURE__ */ new Set([id]);
|
|
1000
|
+
}
|
|
1001
|
+
if (prev.has(id)) {
|
|
1002
|
+
next.delete(id);
|
|
1003
|
+
} else {
|
|
1004
|
+
next.add(id);
|
|
1005
|
+
}
|
|
1006
|
+
return next;
|
|
1007
|
+
});
|
|
1008
|
+
runtime7.events.publish("faq:toggled", {
|
|
1009
|
+
instanceId,
|
|
1010
|
+
questionId: id,
|
|
1011
|
+
expanded: !expandedIds.has(id),
|
|
1012
|
+
timestamp: Date.now()
|
|
1013
|
+
});
|
|
1014
|
+
}, [config.expandBehavior, runtime7.events, instanceId, expandedIds]);
|
|
1015
|
+
const handleFeedback = useCallback2((itemId, question, value) => {
|
|
1016
|
+
setFeedbackState((prev) => {
|
|
1017
|
+
const next = new Map(prev);
|
|
1018
|
+
next.set(itemId, value);
|
|
1019
|
+
return next;
|
|
1020
|
+
});
|
|
1021
|
+
runtime7.events.publish("faq:feedback", {
|
|
1022
|
+
itemId,
|
|
1023
|
+
question,
|
|
1024
|
+
value
|
|
1025
|
+
});
|
|
1026
|
+
}, [runtime7.events]);
|
|
1027
|
+
const containerStyle = {
|
|
1028
|
+
...baseStyles.container,
|
|
1029
|
+
...themeStyles[resolvedTheme].container
|
|
1030
|
+
};
|
|
1031
|
+
const searchInputStyle = {
|
|
1032
|
+
...baseStyles.searchInput,
|
|
1033
|
+
...themeStyles[resolvedTheme].searchInput
|
|
1034
|
+
};
|
|
1035
|
+
const emptyStateStyle = {
|
|
1036
|
+
...baseStyles.emptyState,
|
|
1037
|
+
...themeStyles[resolvedTheme].emptyState
|
|
1038
|
+
};
|
|
1039
|
+
const categoryHeaderStyle = {
|
|
1040
|
+
...baseStyles.categoryHeader,
|
|
1041
|
+
...themeStyles[resolvedTheme].categoryHeader
|
|
1042
|
+
};
|
|
1043
|
+
const renderItems = (items) => items.map((q) => _jsx2(FAQItem, { item: q, isExpanded: expandedIds.has(q.config.id), onToggle: () => handleToggle(q.config.id), theme: resolvedTheme, feedbackConfig, feedbackValue: feedbackState.get(q.config.id), onFeedback: handleFeedback }, q.config.id));
|
|
1044
|
+
if (visibleQuestions.length === 0) {
|
|
1045
|
+
return _jsx2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: _jsx2("div", { style: emptyStateStyle, children: "No FAQ questions available." }) });
|
|
1046
|
+
}
|
|
1047
|
+
return _jsxs2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: [config.searchable && _jsx2("div", { style: baseStyles.searchWrapper, children: _jsx2("input", { type: "text", placeholder: "Search questions...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: searchInputStyle }) }), _jsx2("div", { style: baseStyles.accordion, children: hasCategories ? Array.from(categoryGroups.entries()).map(([category, items]) => _jsxs2(React2.Fragment, { children: [category && _jsx2("div", { style: categoryHeaderStyle, "data-category-header": category, children: category }), renderItems(items)] }, category != null ? category : "__ungrouped")) : renderItems(filteredQuestions) }), config.searchable && filteredQuestions.length === 0 && searchQuery && _jsxs2("div", { style: { ...baseStyles.noResults, ...themeStyles[resolvedTheme].emptyState }, children: ['No questions found matching "', searchQuery, '"'] })] });
|
|
1048
|
+
}
|
|
1049
|
+
var FAQMountableWidget = {
|
|
1050
|
+
mount(container, config) {
|
|
1051
|
+
const { runtime: runtime7, instanceId = "faq-widget", ...faqConfig } = config || {
|
|
1052
|
+
expandBehavior: "single",
|
|
1053
|
+
searchable: false,
|
|
1054
|
+
theme: "auto",
|
|
1055
|
+
actions: []
|
|
1056
|
+
};
|
|
1057
|
+
if (runtime7 && typeof createRoot2 === "function") {
|
|
1058
|
+
const root = createRoot2(container);
|
|
1059
|
+
root.render(React2.createElement(FAQWidget, {
|
|
1060
|
+
config: faqConfig,
|
|
1061
|
+
runtime: runtime7,
|
|
1062
|
+
instanceId
|
|
1063
|
+
}));
|
|
1064
|
+
return () => {
|
|
1065
|
+
root.unmount();
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
const questions = faqConfig.actions || [];
|
|
1069
|
+
container.innerHTML = `
|
|
1070
|
+
<div style="font-family: system-ui; max-width: 800px;">
|
|
1071
|
+
${questions.map((q) => `
|
|
1072
|
+
<div style="margin-bottom: 8px; padding: 16px; background: ${slateGrey[12]}; border-radius: 8px;">
|
|
1073
|
+
<strong>${q.config.question}</strong>
|
|
1074
|
+
<p style="margin-top: 8px; color: ${slateGrey[6]};">${getAnswerText(q.config.answer)}</p>
|
|
1075
|
+
</div>
|
|
1076
|
+
`).join("")}
|
|
1077
|
+
</div>
|
|
1078
|
+
`;
|
|
1079
|
+
return () => {
|
|
1080
|
+
container.innerHTML = "";
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// ../adaptives/adaptive-faq/dist/runtime.js
|
|
1086
|
+
var runtime4 = {
|
|
1087
|
+
id: "adaptive-faq",
|
|
1088
|
+
version: "2.0.0",
|
|
1089
|
+
name: "FAQ Accordion",
|
|
1090
|
+
description: "Collapsible Q&A accordion with actions, rich content, feedback, and personalization",
|
|
1091
|
+
/**
|
|
1092
|
+
* Action executors for programmatic FAQ interaction.
|
|
1093
|
+
*/
|
|
1094
|
+
executors: executorDefinitions,
|
|
1095
|
+
/**
|
|
1096
|
+
* Widget definitions for the runtime's WidgetRegistry.
|
|
1097
|
+
*/
|
|
1098
|
+
widgets: [
|
|
1099
|
+
{
|
|
1100
|
+
id: "adaptive-faq:accordion",
|
|
1101
|
+
component: FAQMountableWidget,
|
|
1102
|
+
metadata: {
|
|
1103
|
+
name: "FAQ Accordion",
|
|
1104
|
+
description: "Collapsible Q&A accordion with search, categories, and feedback",
|
|
1105
|
+
icon: "\u2753"
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
],
|
|
1109
|
+
/**
|
|
1110
|
+
* Extract notify watcher entries from tile config props.
|
|
1111
|
+
* The runtime evaluates these continuously (even with drawer closed)
|
|
1112
|
+
* and publishes faq:question_revealed when showWhen transitions false → true.
|
|
1113
|
+
*/
|
|
1114
|
+
notifyWatchers(props) {
|
|
1115
|
+
var _a;
|
|
1116
|
+
const actions = (_a = props.actions) != null ? _a : [];
|
|
1117
|
+
return actions.filter((a) => a.notify && a.showWhen).map((a) => ({
|
|
1118
|
+
id: `faq:${a.config.id}`,
|
|
1119
|
+
strategy: a.showWhen,
|
|
1120
|
+
eventName: "faq:question_revealed",
|
|
1121
|
+
eventProps: {
|
|
1122
|
+
questionId: a.config.id,
|
|
1123
|
+
question: a.config.question,
|
|
1124
|
+
title: a.notify.title,
|
|
1125
|
+
body: a.notify.body,
|
|
1126
|
+
icon: a.notify.icon
|
|
1127
|
+
}
|
|
1128
|
+
}));
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
// ../adaptives/adaptive-gamification/dist/runtime.js
|
|
1133
|
+
var executeAwardBadge = async (action, context) => {
|
|
1134
|
+
const { badgeId } = action;
|
|
1135
|
+
context.publishEvent("gamification.badge_awarded", {
|
|
1136
|
+
badgeId,
|
|
1137
|
+
awardedAt: Date.now()
|
|
1138
|
+
});
|
|
1139
|
+
return {
|
|
1140
|
+
cleanup: () => {
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
};
|
|
1144
|
+
var executeAddPoints = async (action, context) => {
|
|
1145
|
+
const { points, reason } = action;
|
|
1146
|
+
context.publishEvent("gamification.points_added", {
|
|
1147
|
+
points,
|
|
1148
|
+
reason,
|
|
1149
|
+
timestamp: Date.now()
|
|
1150
|
+
});
|
|
1151
|
+
return {
|
|
1152
|
+
cleanup: () => {
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
};
|
|
1156
|
+
var badgeTriggerHandler = {
|
|
1157
|
+
names: ["page_view", "button_click"],
|
|
1158
|
+
handler: (_event, _ctx) => {
|
|
1159
|
+
console.log("[Gamification] Event received for badge trigger check");
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
var executors = [
|
|
1163
|
+
{ kind: "gamification:awardBadge", executor: executeAwardBadge },
|
|
1164
|
+
{ kind: "gamification:addPoints", executor: executeAddPoints }
|
|
1165
|
+
];
|
|
1166
|
+
var eventHandlers = [badgeTriggerHandler];
|
|
1167
|
+
var runtime5 = {
|
|
1168
|
+
id: "adaptive-gamification",
|
|
1169
|
+
version: "1.0.0",
|
|
1170
|
+
name: "Gamification",
|
|
1171
|
+
description: "Badges, rewards, points, and engagement mechanics",
|
|
1172
|
+
executors,
|
|
1173
|
+
eventHandlers
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
// ../adaptives/adaptive-nav/dist/NavWidget.js
|
|
1177
|
+
import { jsx as _jsx3, jsxs as _jsxs3 } from "react/jsx-runtime";
|
|
1178
|
+
import React3, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo2, useReducer as useReducer2, useState as useState3 } from "react";
|
|
1179
|
+
import { createRoot as createRoot3 } from "react-dom/client";
|
|
1180
|
+
function escapeHtml(str) {
|
|
1181
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1182
|
+
}
|
|
1183
|
+
var baseStyles2 = {
|
|
1184
|
+
container: {
|
|
1185
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1186
|
+
padding: "8px",
|
|
1187
|
+
maxWidth: "100%",
|
|
1188
|
+
overflow: "hidden"
|
|
1189
|
+
},
|
|
1190
|
+
accordion: {
|
|
1191
|
+
display: "flex",
|
|
1192
|
+
flexDirection: "column",
|
|
1193
|
+
gap: "4px"
|
|
1194
|
+
},
|
|
1195
|
+
item: {
|
|
1196
|
+
borderRadius: "8px",
|
|
1197
|
+
overflow: "hidden",
|
|
1198
|
+
transition: "box-shadow 0.2s ease"
|
|
1199
|
+
},
|
|
1200
|
+
header: {
|
|
1201
|
+
display: "flex",
|
|
1202
|
+
alignItems: "center",
|
|
1203
|
+
gap: "8px",
|
|
1204
|
+
width: "100%",
|
|
1205
|
+
padding: "12px 16px",
|
|
1206
|
+
border: "none",
|
|
1207
|
+
cursor: "pointer",
|
|
1208
|
+
fontSize: "14px",
|
|
1209
|
+
fontWeight: 500,
|
|
1210
|
+
fontFamily: "inherit",
|
|
1211
|
+
textAlign: "left",
|
|
1212
|
+
transition: "background-color 0.15s ease"
|
|
1213
|
+
},
|
|
1214
|
+
chevron: {
|
|
1215
|
+
fontSize: "10px",
|
|
1216
|
+
transition: "transform 0.2s ease",
|
|
1217
|
+
marginLeft: "auto",
|
|
1218
|
+
flexShrink: 0
|
|
1219
|
+
},
|
|
1220
|
+
icon: {
|
|
1221
|
+
fontSize: "16px",
|
|
1222
|
+
flexShrink: 0
|
|
1223
|
+
},
|
|
1224
|
+
body: {
|
|
1225
|
+
overflow: "hidden",
|
|
1226
|
+
transition: "max-height 0.25s ease, padding-bottom 0.25s ease",
|
|
1227
|
+
padding: "0 16px"
|
|
1228
|
+
},
|
|
1229
|
+
description: {
|
|
1230
|
+
fontSize: "13px",
|
|
1231
|
+
lineHeight: "1.5",
|
|
1232
|
+
margin: 0
|
|
1233
|
+
},
|
|
1234
|
+
linkButton: {
|
|
1235
|
+
display: "inline-flex",
|
|
1236
|
+
alignItems: "center",
|
|
1237
|
+
gap: "4px",
|
|
1238
|
+
marginTop: "10px",
|
|
1239
|
+
padding: "6px 12px",
|
|
1240
|
+
borderRadius: "6px",
|
|
1241
|
+
textDecoration: "none",
|
|
1242
|
+
fontSize: "13px",
|
|
1243
|
+
fontWeight: 500,
|
|
1244
|
+
cursor: "pointer",
|
|
1245
|
+
border: "none",
|
|
1246
|
+
transition: "background-color 0.15s ease"
|
|
1247
|
+
},
|
|
1248
|
+
categoryHeader: {
|
|
1249
|
+
fontSize: "11px",
|
|
1250
|
+
fontWeight: 600,
|
|
1251
|
+
textTransform: "uppercase",
|
|
1252
|
+
letterSpacing: "0.05em",
|
|
1253
|
+
padding: "12px 4px 4px"
|
|
1254
|
+
},
|
|
1255
|
+
emptyState: {
|
|
1256
|
+
fontSize: "13px",
|
|
1257
|
+
padding: "16px",
|
|
1258
|
+
textAlign: "center"
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
var themeStyles2 = {
|
|
1262
|
+
light: {
|
|
1263
|
+
container: {
|
|
1264
|
+
backgroundColor: base.white,
|
|
1265
|
+
color: slateGrey[1]
|
|
1266
|
+
},
|
|
1267
|
+
item: {
|
|
1268
|
+
backgroundColor: slateGrey[12],
|
|
1269
|
+
border: `1px solid ${slateGrey[11]}`
|
|
1270
|
+
},
|
|
1271
|
+
itemExpanded: {
|
|
1272
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
|
|
1273
|
+
},
|
|
1274
|
+
header: {
|
|
1275
|
+
backgroundColor: "transparent",
|
|
1276
|
+
color: slateGrey[1]
|
|
1277
|
+
},
|
|
1278
|
+
headerHover: {
|
|
1279
|
+
backgroundColor: slateGrey[12]
|
|
1280
|
+
},
|
|
1281
|
+
body: {
|
|
1282
|
+
color: slateGrey[6]
|
|
1283
|
+
},
|
|
1284
|
+
linkButton: {
|
|
1285
|
+
backgroundColor: purple[8],
|
|
1286
|
+
color: purple[2]
|
|
1287
|
+
},
|
|
1288
|
+
categoryHeader: {
|
|
1289
|
+
color: slateGrey[7]
|
|
1290
|
+
},
|
|
1291
|
+
emptyState: {
|
|
1292
|
+
color: slateGrey[8]
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
dark: {
|
|
1296
|
+
container: {
|
|
1297
|
+
backgroundColor: slateGrey[1],
|
|
1298
|
+
color: slateGrey[12]
|
|
1299
|
+
},
|
|
1300
|
+
item: {
|
|
1301
|
+
backgroundColor: slateGrey[3],
|
|
1302
|
+
border: `1px solid ${slateGrey[5]}`
|
|
1303
|
+
},
|
|
1304
|
+
itemExpanded: {
|
|
1305
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
1306
|
+
},
|
|
1307
|
+
header: {
|
|
1308
|
+
backgroundColor: "transparent",
|
|
1309
|
+
color: slateGrey[12]
|
|
1310
|
+
},
|
|
1311
|
+
headerHover: {
|
|
1312
|
+
backgroundColor: slateGrey[5]
|
|
1313
|
+
},
|
|
1314
|
+
body: {
|
|
1315
|
+
color: slateGrey[8]
|
|
1316
|
+
},
|
|
1317
|
+
linkButton: {
|
|
1318
|
+
backgroundColor: purple[0],
|
|
1319
|
+
color: purple[6]
|
|
1320
|
+
},
|
|
1321
|
+
categoryHeader: {
|
|
1322
|
+
color: slateGrey[8]
|
|
1323
|
+
},
|
|
1324
|
+
emptyState: {
|
|
1325
|
+
color: slateGrey[7]
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
1330
|
+
const [isHovered, setIsHovered] = useState3(false);
|
|
1331
|
+
const colors = themeStyles2[theme];
|
|
1332
|
+
const { title, description, href, icon, external } = item.config;
|
|
1333
|
+
const itemStyle = {
|
|
1334
|
+
...baseStyles2.item,
|
|
1335
|
+
...colors.item,
|
|
1336
|
+
...isExpanded ? colors.itemExpanded : {}
|
|
1337
|
+
};
|
|
1338
|
+
const headerStyle = {
|
|
1339
|
+
...baseStyles2.header,
|
|
1340
|
+
...colors.header,
|
|
1341
|
+
...isHovered ? colors.headerHover : {}
|
|
1342
|
+
};
|
|
1343
|
+
const chevronStyle = {
|
|
1344
|
+
...baseStyles2.chevron,
|
|
1345
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)"
|
|
1346
|
+
};
|
|
1347
|
+
const bodyStyle = {
|
|
1348
|
+
...baseStyles2.body,
|
|
1349
|
+
...colors.body,
|
|
1350
|
+
maxHeight: isExpanded ? "500px" : "0",
|
|
1351
|
+
paddingBottom: isExpanded ? "16px" : "0"
|
|
1352
|
+
};
|
|
1353
|
+
const handleLinkClick = (e) => {
|
|
1354
|
+
e.preventDefault();
|
|
1355
|
+
e.stopPropagation();
|
|
1356
|
+
if (href) {
|
|
1357
|
+
onNavigate(href, external != null ? external : false);
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
return _jsxs3("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs3("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx3("span", { style: baseStyles2.icon, children: icon }), _jsx3("span", { children: title }), _jsx3("span", { style: chevronStyle, children: "\u25BC" })] }), _jsxs3("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx3("p", { style: baseStyles2.description, children: description }), href && _jsxs3("a", { href, onClick: handleLinkClick, style: { ...baseStyles2.linkButton, ...colors.linkButton }, target: external ? "_blank" : void 0, rel: external ? "noopener noreferrer" : void 0, children: ["Go ", external ? "\u2197" : "\u2192"] })] })] });
|
|
1361
|
+
}
|
|
1362
|
+
function NavWidget({ config, runtime: runtime7, instanceId }) {
|
|
1363
|
+
const [renderTick, forceUpdate] = useReducer2((x) => x + 1, 0);
|
|
1364
|
+
const [expandedIds, setExpandedIds] = useState3(/* @__PURE__ */ new Set());
|
|
1365
|
+
useEffect3(() => {
|
|
1366
|
+
const unsubscribe = runtime7.context.subscribe(() => {
|
|
1367
|
+
forceUpdate();
|
|
1368
|
+
});
|
|
1369
|
+
return unsubscribe;
|
|
1370
|
+
}, [runtime7.context]);
|
|
1371
|
+
useEffect3(() => {
|
|
1372
|
+
var _a;
|
|
1373
|
+
if (!((_a = runtime7.accumulator) == null ? void 0 : _a.subscribe))
|
|
1374
|
+
return;
|
|
1375
|
+
return runtime7.accumulator.subscribe(() => {
|
|
1376
|
+
forceUpdate();
|
|
1377
|
+
});
|
|
1378
|
+
}, [runtime7.accumulator]);
|
|
1379
|
+
useEffect3(() => {
|
|
1380
|
+
var _a, _b;
|
|
1381
|
+
if (!config.scope || !((_a = runtime7.accumulator) == null ? void 0 : _a.register))
|
|
1382
|
+
return;
|
|
1383
|
+
const { events: eventNames, urlContains, props: propFilters } = config.scope;
|
|
1384
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1385
|
+
for (const action of config.actions) {
|
|
1386
|
+
if (((_b = action.showWhen) == null ? void 0 : _b.type) === "rules") {
|
|
1387
|
+
for (const rule of action.showWhen.rules) {
|
|
1388
|
+
for (const cond of rule.conditions) {
|
|
1389
|
+
if (cond.type === "event_count" && cond.key) {
|
|
1390
|
+
keys.add(cond.key);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
for (const key of keys) {
|
|
1397
|
+
runtime7.accumulator.register(key, (event) => {
|
|
1398
|
+
var _a2, _b2, _c;
|
|
1399
|
+
if (!eventNames.includes(event.name))
|
|
1400
|
+
return false;
|
|
1401
|
+
if (urlContains) {
|
|
1402
|
+
const pathname = String((_b2 = (_a2 = event.props) == null ? void 0 : _a2.pathname) != null ? _b2 : "");
|
|
1403
|
+
if (!pathname.includes(urlContains))
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
if (propFilters) {
|
|
1407
|
+
for (const [k, v] of Object.entries(propFilters)) {
|
|
1408
|
+
if (((_c = event.props) == null ? void 0 : _c[k]) !== v)
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return true;
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
}, [config.scope, config.actions, runtime7.accumulator]);
|
|
1416
|
+
const visibleTips = useMemo2(() => config.actions.filter((tip) => {
|
|
1417
|
+
if (!tip.showWhen)
|
|
1418
|
+
return true;
|
|
1419
|
+
try {
|
|
1420
|
+
const result = runtime7.evaluateSync(tip.showWhen);
|
|
1421
|
+
return result.value;
|
|
1422
|
+
} catch {
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
}), [config.actions, runtime7, renderTick]);
|
|
1426
|
+
const categoryGroups = useMemo2(() => {
|
|
1427
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1428
|
+
for (const tip of visibleTips) {
|
|
1429
|
+
const cat = tip.config.category;
|
|
1430
|
+
if (!groups.has(cat)) {
|
|
1431
|
+
groups.set(cat, []);
|
|
1432
|
+
}
|
|
1433
|
+
groups.get(cat).push(tip);
|
|
1434
|
+
}
|
|
1435
|
+
return groups;
|
|
1436
|
+
}, [visibleTips]);
|
|
1437
|
+
const hasCategories = useMemo2(() => visibleTips.some((t) => t.config.category), [visibleTips]);
|
|
1438
|
+
const resolvedTheme = useMemo2(() => {
|
|
1439
|
+
var _a;
|
|
1440
|
+
if (config.theme && config.theme !== "auto")
|
|
1441
|
+
return config.theme;
|
|
1442
|
+
if (typeof window !== "undefined") {
|
|
1443
|
+
return ((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-color-scheme: dark)").matches) ? "dark" : "light";
|
|
1444
|
+
}
|
|
1445
|
+
return "light";
|
|
1446
|
+
}, [config.theme]);
|
|
1447
|
+
const handleToggle = useCallback3((id) => {
|
|
1448
|
+
setExpandedIds((prev) => {
|
|
1449
|
+
const wasExpanded = prev.has(id);
|
|
1450
|
+
let next;
|
|
1451
|
+
if (config.expandBehavior === "single") {
|
|
1452
|
+
for (const prevId of prev) {
|
|
1453
|
+
if (prevId !== id) {
|
|
1454
|
+
runtime7.events.publish("nav:toggled", {
|
|
1455
|
+
instanceId,
|
|
1456
|
+
tipId: prevId,
|
|
1457
|
+
expanded: false,
|
|
1458
|
+
timestamp: Date.now()
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
next = wasExpanded ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([id]);
|
|
1463
|
+
} else {
|
|
1464
|
+
next = new Set(prev);
|
|
1465
|
+
if (wasExpanded) {
|
|
1466
|
+
next.delete(id);
|
|
1467
|
+
} else {
|
|
1468
|
+
next.add(id);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
runtime7.events.publish("nav:toggled", {
|
|
1472
|
+
instanceId,
|
|
1473
|
+
tipId: id,
|
|
1474
|
+
expanded: !wasExpanded,
|
|
1475
|
+
timestamp: Date.now()
|
|
1476
|
+
});
|
|
1477
|
+
return next;
|
|
1478
|
+
});
|
|
1479
|
+
}, [config.expandBehavior, runtime7.events, instanceId]);
|
|
1480
|
+
const handleNavigate = useCallback3((href, external) => {
|
|
1481
|
+
const normalizedHref = href.trim().toLowerCase();
|
|
1482
|
+
if (normalizedHref.startsWith("javascript:") || normalizedHref.startsWith("data:")) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
runtime7.events.publish("nav:tip_clicked", {
|
|
1486
|
+
instanceId,
|
|
1487
|
+
href,
|
|
1488
|
+
external,
|
|
1489
|
+
timestamp: Date.now()
|
|
1490
|
+
});
|
|
1491
|
+
if (external) {
|
|
1492
|
+
window.open(href, "_blank", "noopener,noreferrer");
|
|
1493
|
+
} else {
|
|
1494
|
+
window.location.href = href;
|
|
1495
|
+
}
|
|
1496
|
+
}, [runtime7.events, instanceId]);
|
|
1497
|
+
const containerStyle = {
|
|
1498
|
+
...baseStyles2.container,
|
|
1499
|
+
...themeStyles2[resolvedTheme].container
|
|
1500
|
+
};
|
|
1501
|
+
const categoryHeaderStyle = {
|
|
1502
|
+
...baseStyles2.categoryHeader,
|
|
1503
|
+
...themeStyles2[resolvedTheme].categoryHeader
|
|
1504
|
+
};
|
|
1505
|
+
const emptyStateStyle = {
|
|
1506
|
+
...baseStyles2.emptyState,
|
|
1507
|
+
...themeStyles2[resolvedTheme].emptyState
|
|
1508
|
+
};
|
|
1509
|
+
const renderItems = (items) => items.map((tip) => _jsx3(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id));
|
|
1510
|
+
if (visibleTips.length === 0) {
|
|
1511
|
+
return _jsx3("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx3("div", { style: emptyStateStyle, children: "No navigation tips available." }) });
|
|
1512
|
+
}
|
|
1513
|
+
return _jsx3("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx3("div", { style: baseStyles2.accordion, children: hasCategories ? Array.from(categoryGroups.entries()).map(([category, items]) => _jsxs3(React3.Fragment, { children: [category && _jsx3("div", { style: categoryHeaderStyle, "data-category-header": category, children: category }), renderItems(items)] }, category != null ? category : "__ungrouped")) : renderItems(visibleTips) }) });
|
|
1514
|
+
}
|
|
1515
|
+
var NavMountableWidget = {
|
|
1516
|
+
mount(container, config) {
|
|
1517
|
+
const { runtime: runtime7, instanceId = "nav-widget", ...navConfig } = config || {
|
|
1518
|
+
expandBehavior: "single",
|
|
1519
|
+
theme: "auto",
|
|
1520
|
+
actions: []
|
|
1521
|
+
};
|
|
1522
|
+
if (runtime7 && typeof createRoot3 === "function") {
|
|
1523
|
+
const root = createRoot3(container);
|
|
1524
|
+
root.render(React3.createElement(NavWidget, {
|
|
1525
|
+
config: navConfig,
|
|
1526
|
+
runtime: runtime7,
|
|
1527
|
+
instanceId
|
|
1528
|
+
}));
|
|
1529
|
+
return () => {
|
|
1530
|
+
root.unmount();
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
const tips = navConfig.actions || [];
|
|
1534
|
+
container.innerHTML = `
|
|
1535
|
+
<div style="font-family: system-ui; max-width: 100%;">
|
|
1536
|
+
${tips.map((tip) => `
|
|
1537
|
+
<div style="margin-bottom: 4px; padding: 12px 16px; background: ${slateGrey[12]}; border-radius: 8px;">
|
|
1538
|
+
${tip.config.icon ? `<span>${escapeHtml(tip.config.icon)}</span> ` : ""}<strong>${escapeHtml(tip.config.title)}</strong>
|
|
1539
|
+
<p style="margin-top: 8px; color: ${slateGrey[6]}; font-size: 13px;">${escapeHtml(tip.config.description)}</p>
|
|
1540
|
+
${tip.config.href ? `<a href="${escapeHtml(tip.config.href)}" style="color: ${purple[2]}; font-size: 13px;">Go →</a>` : ""}
|
|
1541
|
+
</div>
|
|
1542
|
+
`).join("")}
|
|
1543
|
+
</div>
|
|
1544
|
+
`;
|
|
1545
|
+
return () => {
|
|
1546
|
+
container.innerHTML = "";
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// ../adaptives/adaptive-nav/dist/runtime.js
|
|
1552
|
+
var executeScrollTo = async (action, context) => {
|
|
1553
|
+
var _a, _b, _c, _d;
|
|
1554
|
+
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
1555
|
+
if (!anchorEl) {
|
|
1556
|
+
throw new Error(`Anchor not found: ${action.anchorId}`);
|
|
1557
|
+
}
|
|
1558
|
+
anchorEl.scrollIntoView({
|
|
1559
|
+
behavior: (_a = action.behavior) != null ? _a : "smooth",
|
|
1560
|
+
block: (_b = action.block) != null ? _b : "center",
|
|
1561
|
+
inline: (_c = action.inline) != null ? _c : "nearest"
|
|
1562
|
+
});
|
|
1563
|
+
context.publishEvent("action.applied", {
|
|
1564
|
+
id: context.generateId(),
|
|
1565
|
+
kind: "navigation:scrollTo",
|
|
1566
|
+
anchorId: action.anchorId,
|
|
1567
|
+
behavior: (_d = action.behavior) != null ? _d : "smooth"
|
|
1568
|
+
});
|
|
1569
|
+
return {
|
|
1570
|
+
cleanup: () => {
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
};
|
|
1574
|
+
function isSameOrigin(url) {
|
|
1575
|
+
try {
|
|
1576
|
+
const parsed = new URL(url, window.location.origin);
|
|
1577
|
+
return parsed.origin === window.location.origin;
|
|
1578
|
+
} catch {
|
|
1579
|
+
return false;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
var executeNavigate = async (action, context) => {
|
|
1583
|
+
var _a;
|
|
1584
|
+
const url = action.url.trim();
|
|
1585
|
+
if (url.toLowerCase().startsWith("javascript:")) {
|
|
1586
|
+
throw new Error("javascript: URLs are not allowed");
|
|
1587
|
+
}
|
|
1588
|
+
const target = (_a = action.target) != null ? _a : "_self";
|
|
1589
|
+
context.publishEvent("action.applied", {
|
|
1590
|
+
id: context.generateId(),
|
|
1591
|
+
kind: "navigation:navigate",
|
|
1592
|
+
url: action.url,
|
|
1593
|
+
target
|
|
1594
|
+
});
|
|
1595
|
+
if (target === "_blank") {
|
|
1596
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
1597
|
+
} else if (!action.forceFullNavigation && isSameOrigin(url)) {
|
|
1598
|
+
window.history.pushState(null, "", url);
|
|
1599
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
1600
|
+
} else {
|
|
1601
|
+
window.location.href = url;
|
|
1602
|
+
}
|
|
1603
|
+
return {
|
|
1604
|
+
cleanup: () => {
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
};
|
|
1608
|
+
var executors2 = [
|
|
1609
|
+
{ kind: "navigation:scrollTo", executor: executeScrollTo },
|
|
1610
|
+
{ kind: "navigation:navigate", executor: executeNavigate }
|
|
1611
|
+
];
|
|
1612
|
+
var runtime6 = {
|
|
1613
|
+
id: "adaptive-nav",
|
|
1614
|
+
version: "2.0.0",
|
|
1615
|
+
name: "Navigation Tips",
|
|
1616
|
+
description: "Navigation actions and accordion-based tips with per-item conditional visibility",
|
|
1617
|
+
/**
|
|
1618
|
+
* Navigation action executors (scrollTo, navigate).
|
|
1619
|
+
*/
|
|
1620
|
+
executors: executors2,
|
|
1621
|
+
/**
|
|
1622
|
+
* Widget definitions for the runtime's WidgetRegistry.
|
|
1623
|
+
*/
|
|
1624
|
+
widgets: [
|
|
1625
|
+
{
|
|
1626
|
+
id: "adaptive-nav:tips",
|
|
1627
|
+
component: NavMountableWidget,
|
|
1628
|
+
metadata: {
|
|
1629
|
+
name: "Navigation Tips",
|
|
1630
|
+
description: "Accordion of contextual navigation tips with per-item visibility",
|
|
1631
|
+
icon: "\u{1F9ED}"
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
],
|
|
1635
|
+
/**
|
|
1636
|
+
* Extract notify watcher entries from tile config props.
|
|
1637
|
+
* The runtime evaluates these continuously (even with drawer closed)
|
|
1638
|
+
* and publishes nav:tip_revealed when showWhen transitions false → true.
|
|
1639
|
+
*/
|
|
1640
|
+
notifyWatchers(props) {
|
|
1641
|
+
var _a;
|
|
1642
|
+
const actions = (_a = props.actions) != null ? _a : [];
|
|
1643
|
+
return actions.filter((a) => a.notify && a.showWhen).map((a) => ({
|
|
1644
|
+
id: `nav:${a.config.id}`,
|
|
1645
|
+
strategy: a.showWhen,
|
|
1646
|
+
eventName: "nav:tip_revealed",
|
|
1647
|
+
eventProps: {
|
|
1648
|
+
tipId: a.config.id,
|
|
1649
|
+
title: a.notify.title,
|
|
1650
|
+
body: a.notify.body,
|
|
1651
|
+
icon: a.notify.icon
|
|
1652
|
+
}
|
|
1653
|
+
}));
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1657
|
+
// src/apps/builtinRuntimeModules.generated.ts
|
|
1658
|
+
var allRuntimes = [runtime3, runtime, runtime4, runtime5, runtime6, runtime2];
|
|
1659
|
+
var builtinAdaptiveManifests = allRuntimes.map((r) => ({
|
|
1660
|
+
id: r.id,
|
|
1661
|
+
version: r.version,
|
|
1662
|
+
name: r.name,
|
|
1663
|
+
description: r.description,
|
|
1664
|
+
runtime: {
|
|
1665
|
+
actions: (r.executors || []).map(({ kind, executor }) => ({ kind, executor })),
|
|
1666
|
+
widgets: r.widgets,
|
|
1667
|
+
notifyWatchers: r.notifyWatchers,
|
|
1668
|
+
events: r.eventHandlers
|
|
1669
|
+
},
|
|
1670
|
+
editor: void 0,
|
|
1671
|
+
metadata: { isBuiltIn: true }
|
|
1672
|
+
}));
|
|
1673
|
+
function registerBuiltinRuntimeModules(registry) {
|
|
1674
|
+
let count = 0;
|
|
1675
|
+
for (const manifest of builtinAdaptiveManifests) {
|
|
1676
|
+
if (!registry.has(manifest.id)) {
|
|
1677
|
+
registry.register(manifest);
|
|
1678
|
+
count++;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
console.log("[Syntro Runtime] Registered", count, "built-in runtime modules");
|
|
1682
|
+
}
|
|
1683
|
+
|
|
140
1684
|
// src/components/TileWheel.tsx
|
|
141
|
-
import { useEffect, useMemo, useState } from "react";
|
|
1685
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
|
|
142
1686
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
143
1687
|
function TileWheel({ tiles, intervalMs = 7e3, telemetry }) {
|
|
144
|
-
const [index, setIndex] =
|
|
145
|
-
const ordered =
|
|
1688
|
+
const [index, setIndex] = useState4(0);
|
|
1689
|
+
const ordered = useMemo3(
|
|
146
1690
|
() => [...tiles].sort((a, b) => {
|
|
147
1691
|
var _a, _b;
|
|
148
1692
|
return ((_a = a.priority) != null ? _a : 0) - ((_b = b.priority) != null ? _b : 0);
|
|
149
1693
|
}),
|
|
150
1694
|
[tiles]
|
|
151
1695
|
);
|
|
152
|
-
|
|
1696
|
+
useEffect4(() => {
|
|
153
1697
|
telemetry == null ? void 0 : telemetry.trackCanvasOpened("wheel");
|
|
154
1698
|
}, [telemetry]);
|
|
155
|
-
|
|
1699
|
+
useEffect4(() => {
|
|
156
1700
|
if (ordered.length <= 1) return;
|
|
157
1701
|
const id = setInterval(() => {
|
|
158
1702
|
setIndex((prev) => (prev + 1) % ordered.length);
|
|
159
1703
|
}, intervalMs);
|
|
160
1704
|
return () => clearInterval(id);
|
|
161
1705
|
}, [ordered, intervalMs]);
|
|
162
|
-
|
|
1706
|
+
useEffect4(() => {
|
|
163
1707
|
const current = ordered[index];
|
|
164
1708
|
if (current) {
|
|
165
1709
|
telemetry == null ? void 0 : telemetry.trackRectangleViewed(current.id, "wheel");
|
|
@@ -394,10 +1938,10 @@ var createOverlayRecipeFetcher = ({
|
|
|
394
1938
|
};
|
|
395
1939
|
|
|
396
1940
|
// src/SmartCanvasPortal.tsx
|
|
397
|
-
import { useLayoutEffect, useState as
|
|
1941
|
+
import { useLayoutEffect, useState as useState5 } from "react";
|
|
398
1942
|
import { createPortal } from "react-dom";
|
|
399
1943
|
function SmartCanvasPortal({ element, children }) {
|
|
400
|
-
const [mountNode, setMountNode] =
|
|
1944
|
+
const [mountNode, setMountNode] = useState5(null);
|
|
401
1945
|
useLayoutEffect(() => {
|
|
402
1946
|
if (!element) {
|
|
403
1947
|
setMountNode(null);
|
|
@@ -626,14 +2170,19 @@ function validateFrequencyEntry(data) {
|
|
|
626
2170
|
}
|
|
627
2171
|
|
|
628
2172
|
// src/index.ts
|
|
2173
|
+
var RUNTIME_SDK_BUILD = typeof __BUILD_TIMESTAMP__ !== "undefined" ? `${__BUILD_TIMESTAMP__} (${__BUILD_GIT_HASH__})` : "dev";
|
|
629
2174
|
if (typeof window !== "undefined") {
|
|
2175
|
+
console.log(`[Syntro Runtime] Build: ${RUNTIME_SDK_BUILD}`);
|
|
630
2176
|
const existing = window.SynOS;
|
|
2177
|
+
const registry = (existing == null ? void 0 : existing.appRegistry) || appRegistry;
|
|
631
2178
|
window.SynOS = {
|
|
632
2179
|
...existing,
|
|
633
|
-
appRegistry:
|
|
634
|
-
|
|
2180
|
+
appRegistry: registry,
|
|
2181
|
+
_runtimeBuild: RUNTIME_SDK_BUILD,
|
|
2182
|
+
React: React4,
|
|
635
2183
|
ReactDOM: { ...ReactDOMClient, createPortal: createPortal2, flushSync }
|
|
636
2184
|
};
|
|
2185
|
+
registerBuiltinRuntimeModules(registry);
|
|
637
2186
|
}
|
|
638
2187
|
export {
|
|
639
2188
|
ANIMATION_KEYFRAMES,
|
|
@@ -677,6 +2226,7 @@ export {
|
|
|
677
2226
|
ModelStrategyZ,
|
|
678
2227
|
MountWidgetZ,
|
|
679
2228
|
NavigateZ,
|
|
2229
|
+
NavigationMonitor,
|
|
680
2230
|
NormalizedEventZ,
|
|
681
2231
|
NotificationToastStack,
|
|
682
2232
|
PageContextZ,
|
|
@@ -685,6 +2235,7 @@ export {
|
|
|
685
2235
|
ParallelZ,
|
|
686
2236
|
PlacementZ,
|
|
687
2237
|
PulseZ,
|
|
2238
|
+
RUNTIME_SDK_BUILD,
|
|
688
2239
|
RUNTIME_VERSION,
|
|
689
2240
|
RemoveClassZ,
|
|
690
2241
|
RouteConditionZ,
|