@embeddables/cli 0.14.4-beta.3 → 0.15.0-beta.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.
Files changed (61) hide show
  1. package/.prompts/custom/build-funnel.md +3 -1
  2. package/.prompts/custom/carousel.md +159 -0
  3. package/.prompts/embeddables-cli.md +53 -3
  4. package/.prompts/short-rule-body.md +15 -0
  5. package/README.md +52 -0
  6. package/dist/cli.js +40 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/dangerously-publish.d.ts +53 -0
  9. package/dist/commands/dangerously-publish.d.ts.map +1 -0
  10. package/dist/commands/dangerously-publish.js +731 -0
  11. package/dist/commands/dangerously-publish.js.map +1 -0
  12. package/dist/commands/update-project-files.d.ts.map +1 -1
  13. package/dist/commands/update-project-files.js +52 -72
  14. package/dist/commands/update-project-files.js.map +1 -1
  15. package/dist/compiler/reverse.d.ts.map +1 -1
  16. package/dist/compiler/reverse.js +1 -0
  17. package/dist/compiler/reverse.js.map +1 -1
  18. package/dist/components/primitives/OptionSelector.d.ts +1 -0
  19. package/dist/components/primitives/OptionSelector.d.ts.map +1 -1
  20. package/dist/components/primitives/OptionSelector.js.map +1 -1
  21. package/dist/constants.d.ts +1 -1
  22. package/dist/constants.d.ts.map +1 -1
  23. package/dist/constants.js +2 -3
  24. package/dist/constants.js.map +1 -1
  25. package/dist/types-builder.d.ts +1 -0
  26. package/dist/types-builder.d.ts.map +1 -1
  27. package/dist/workbench/AutofillPanel.d.ts.map +1 -1
  28. package/dist/workbench/AutofillPanel.js +52 -14
  29. package/dist/workbench/AutofillPanel.js.map +1 -1
  30. package/dist/workbench/FeedbackPanel.d.ts +39 -0
  31. package/dist/workbench/FeedbackPanel.d.ts.map +1 -0
  32. package/dist/workbench/FeedbackPanel.js +279 -0
  33. package/dist/workbench/FeedbackPanel.js.map +1 -0
  34. package/dist/workbench/PageThumbnailStrip.d.ts +6 -0
  35. package/dist/workbench/PageThumbnailStrip.d.ts.map +1 -0
  36. package/dist/workbench/PageThumbnailStrip.js +124 -0
  37. package/dist/workbench/PageThumbnailStrip.js.map +1 -0
  38. package/dist/workbench/Toast.d.ts +18 -0
  39. package/dist/workbench/Toast.d.ts.map +1 -0
  40. package/dist/workbench/Toast.js +46 -0
  41. package/dist/workbench/Toast.js.map +1 -0
  42. package/dist/workbench/UserDataPanel.d.ts +2 -1
  43. package/dist/workbench/UserDataPanel.d.ts.map +1 -1
  44. package/dist/workbench/UserDataPanel.js +2 -1
  45. package/dist/workbench/UserDataPanel.js.map +1 -1
  46. package/dist/workbench/WorkbenchApp.d.ts.map +1 -1
  47. package/dist/workbench/WorkbenchApp.js +19 -3
  48. package/dist/workbench/WorkbenchApp.js.map +1 -1
  49. package/dist/workbench/supabase-browser.d.ts +11 -0
  50. package/dist/workbench/supabase-browser.d.ts.map +1 -0
  51. package/dist/workbench/supabase-browser.js +18 -0
  52. package/dist/workbench/supabase-browser.js.map +1 -0
  53. package/dist/workbench/types.d.ts +25 -0
  54. package/dist/workbench/types.d.ts.map +1 -0
  55. package/dist/workbench/types.js +2 -0
  56. package/dist/workbench/types.js.map +1 -0
  57. package/dist/workbench/useComponentSelection.d.ts +27 -0
  58. package/dist/workbench/useComponentSelection.d.ts.map +1 -0
  59. package/dist/workbench/useComponentSelection.js +203 -0
  60. package/dist/workbench/useComponentSelection.js.map +1 -0
  61. package/package.json +1 -1
@@ -0,0 +1,279 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Feedback Panel for collecting and reviewing design feedback on embeddable components.
4
+ * Supports session-based feedback collection, screenshots, and shareable review URLs.
5
+ */
6
+ import { useCallback, useEffect, useState } from 'react';
7
+ import html2canvas from 'html2canvas';
8
+ import { getSupabaseBrowserClient } from './supabase-browser.js';
9
+ import { useComponentSelection } from './useComponentSelection.js';
10
+ import { SUPABASE_URL } from '../constants.js';
11
+ const FEEDBACK_BUCKET = 'feedback_reviews';
12
+ /** Generate shareable URL with feedbackReview and feedbackProject params. */
13
+ function buildShareableUrl(reviewId, projectId) {
14
+ const url = new URL(window.location.href);
15
+ url.searchParams.set('feedbackReview', reviewId);
16
+ url.searchParams.set('feedbackProject', projectId);
17
+ return url.toString();
18
+ }
19
+ /** Fetch review JSON from Supabase storage. Path: feedback_reviews/{projectId}/{reviewId}.json */
20
+ async function fetchReview(reviewId, projectId) {
21
+ try {
22
+ const base = SUPABASE_URL.replace(/\/$/, '');
23
+ const url = `${base}/storage/v1/object/public/${FEEDBACK_BUCKET}/${projectId}/${reviewId}.json`;
24
+ const res = await fetch(url);
25
+ if (!res.ok)
26
+ return null;
27
+ return (await res.json());
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ // Track last highlighted element so we can clear its outline when switching comments
34
+ let lastHighlightedEl = null;
35
+ /** Scroll to and highlight a component by ID. Clears previous highlight immediately. */
36
+ function scrollToComponent(componentId) {
37
+ if (lastHighlightedEl) {
38
+ lastHighlightedEl.style.outline = '';
39
+ lastHighlightedEl = null;
40
+ }
41
+ const el = document.querySelector(`.cid-${componentId}`);
42
+ if (el) {
43
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
44
+ el.style.outline = '2px solid rgba(59, 130, 246, 0.8)';
45
+ lastHighlightedEl = el;
46
+ setTimeout(() => {
47
+ if (lastHighlightedEl === el) {
48
+ el.style.outline = '';
49
+ lastHighlightedEl = null;
50
+ }
51
+ }, 2000);
52
+ }
53
+ }
54
+ export function FeedbackPanel({ embeddableId, feedbackReviewId, feedbackProjectId, projectId: injectedProjectId, }) {
55
+ const [sessionActive, setSessionActive] = useState(false);
56
+ const [comments, setComments] = useState([]);
57
+ const [commentText, setCommentText] = useState('');
58
+ const [attachments, setAttachments] = useState([]);
59
+ const [includeViewportScreenshot, setIncludeViewportScreenshot] = useState(false);
60
+ const [includeComponentScreenshot, setIncludeComponentScreenshot] = useState(false);
61
+ const [loadingReview, setLoadingReview] = useState(!!(feedbackReviewId && feedbackProjectId));
62
+ const [loadedReview, setLoadedReview] = useState(null);
63
+ const [completeError, setCompleteError] = useState(null);
64
+ const [copiedUrl, setCopiedUrl] = useState(false);
65
+ const [isUploading, setIsUploading] = useState(false);
66
+ const [loadError, setLoadError] = useState(null);
67
+ const [submitError, setSubmitError] = useState(null);
68
+ const { state, selectedComponent, selectedComponentId, error, startInspecting, stopInspecting, clearSelection, inspectAnother, } = useComponentSelection(embeddableId);
69
+ const savvy = window.Savvy;
70
+ // Load review when feedbackReviewId and feedbackProjectId are present (read-only mode)
71
+ useEffect(() => {
72
+ if (!feedbackReviewId || !feedbackProjectId) {
73
+ setLoadingReview(false);
74
+ return;
75
+ }
76
+ let cancelled = false;
77
+ setLoadError(null);
78
+ fetchReview(feedbackReviewId, feedbackProjectId)
79
+ .then((review) => {
80
+ if (!cancelled && review) {
81
+ setLoadedReview(review);
82
+ setComments(review.comments);
83
+ }
84
+ else if (!cancelled && !review) {
85
+ setLoadError('Feedback review not found or could not be loaded.');
86
+ }
87
+ })
88
+ .finally(() => {
89
+ if (!cancelled)
90
+ setLoadingReview(false);
91
+ });
92
+ return () => {
93
+ cancelled = true;
94
+ };
95
+ }, [feedbackReviewId, feedbackProjectId]);
96
+ const handleStartSession = useCallback(() => {
97
+ setSessionActive(true);
98
+ setComments([]);
99
+ }, []);
100
+ /** Capture screenshots at submit time based on toggles. */
101
+ const captureScreenshots = useCallback(async () => {
102
+ const screenshots = [];
103
+ if (includeViewportScreenshot) {
104
+ try {
105
+ const canvas = await html2canvas(document.body, {
106
+ useCORS: true,
107
+ allowTaint: true,
108
+ logging: false,
109
+ // Exclude workbench panel from viewport screenshot
110
+ ignoreElements: (el) => el.id === '__embeddables_workbench_root' ||
111
+ !!el.closest('#__embeddables_workbench_root'),
112
+ });
113
+ screenshots.push({
114
+ type: 'screenshot',
115
+ data: canvas.toDataURL('image/png'),
116
+ filename: 'viewport.png',
117
+ });
118
+ }
119
+ catch (err) {
120
+ console.error('[Feedback] Viewport screenshot failed:', err);
121
+ }
122
+ }
123
+ if (includeComponentScreenshot && selectedComponentId) {
124
+ const el = document.querySelector(`.cid-${selectedComponentId}`);
125
+ if (el) {
126
+ try {
127
+ const canvas = await html2canvas(el, {
128
+ useCORS: true,
129
+ allowTaint: true,
130
+ logging: false,
131
+ });
132
+ screenshots.push({
133
+ type: 'screenshot',
134
+ data: canvas.toDataURL('image/png'),
135
+ filename: `component-${selectedComponentId}.png`,
136
+ });
137
+ }
138
+ catch (err) {
139
+ console.error('[Feedback] Component screenshot failed:', err);
140
+ }
141
+ }
142
+ }
143
+ return screenshots;
144
+ }, [includeViewportScreenshot, includeComponentScreenshot, selectedComponentId]);
145
+ const handleFileAttach = useCallback((e) => {
146
+ const files = e.target.files;
147
+ if (!files?.length)
148
+ return;
149
+ const file = files[0];
150
+ const reader = new FileReader();
151
+ reader.onload = () => {
152
+ const data = reader.result;
153
+ setAttachments((prev) => [
154
+ ...prev,
155
+ { type: 'file', data, filename: file.name },
156
+ ]);
157
+ };
158
+ reader.readAsDataURL(file);
159
+ e.target.value = '';
160
+ }, []);
161
+ const removeAttachment = useCallback((index) => {
162
+ setAttachments((prev) => prev.filter((_, i) => i !== index));
163
+ }, []);
164
+ const handleSubmitComment = useCallback(async () => {
165
+ const screenshotAttachments = await captureScreenshots();
166
+ const allAttachments = [...attachments, ...screenshotAttachments];
167
+ if (!commentText.trim() && allAttachments.length === 0)
168
+ return;
169
+ if (!savvy?.getUserData || !savvy?.getFlowJson) {
170
+ setSubmitError('Embeddable runtime is not ready. Please wait for the page to load.');
171
+ return;
172
+ }
173
+ setSubmitError(null);
174
+ const userData = savvy.getUserData(embeddableId);
175
+ const flowJson = savvy.getFlowJson(embeddableId);
176
+ const pageKey = userData?.current_page_key ?? 'unknown';
177
+ const pageId = userData?.current_page_id ?? 'unknown';
178
+ const comment = {
179
+ id: crypto.randomUUID(),
180
+ pageKey,
181
+ pageId,
182
+ componentKey: selectedComponent?.key,
183
+ componentId: selectedComponentId ?? undefined,
184
+ componentType: selectedComponent?.type,
185
+ comment: commentText.trim(),
186
+ attachments: allAttachments,
187
+ userData: userData,
188
+ metadata: {
189
+ timestamp: new Date().toISOString(),
190
+ viewportWidth: window.innerWidth,
191
+ viewportHeight: window.innerHeight,
192
+ },
193
+ };
194
+ setComments((prev) => [...prev, comment]);
195
+ setCommentText('');
196
+ setAttachments([]);
197
+ setIncludeViewportScreenshot(false);
198
+ setIncludeComponentScreenshot(false);
199
+ clearSelection();
200
+ }, [
201
+ commentText,
202
+ attachments,
203
+ selectedComponent,
204
+ selectedComponentId,
205
+ embeddableId,
206
+ savvy,
207
+ clearSelection,
208
+ captureScreenshots,
209
+ ]);
210
+ const handleCompleteReview = useCallback(async () => {
211
+ if (comments.length === 0)
212
+ return;
213
+ if (!savvy?.getFlowJson)
214
+ return;
215
+ setCompleteError(null);
216
+ setIsUploading(true);
217
+ const flowJson = savvy.getFlowJson(embeddableId);
218
+ // Prefer injected projectId (from embeddables.json in dev), then flow, else fallback
219
+ const projectId = injectedProjectId ?? flowJson?.project_id ?? '_unknown';
220
+ const reviewId = crypto.randomUUID();
221
+ const baseUrl = window.location.href.split('?')[0];
222
+ const review = {
223
+ id: reviewId,
224
+ embeddableId,
225
+ projectId,
226
+ baseUrl,
227
+ comments,
228
+ createdAt: new Date().toISOString(),
229
+ };
230
+ try {
231
+ const supabase = getSupabaseBrowserClient();
232
+ // Store in project_id folder: feedback_reviews/{projectId}/{reviewId}.json
233
+ const storagePath = `${projectId}/${reviewId}.json`;
234
+ const { error: uploadError } = await supabase.storage
235
+ .from(FEEDBACK_BUCKET)
236
+ .upload(storagePath, JSON.stringify(review), {
237
+ contentType: 'application/json',
238
+ upsert: false,
239
+ });
240
+ if (uploadError) {
241
+ setCompleteError(uploadError.message);
242
+ return;
243
+ }
244
+ const shareUrl = buildShareableUrl(reviewId, projectId);
245
+ try {
246
+ await navigator.clipboard.writeText(shareUrl);
247
+ setCopiedUrl(true);
248
+ setTimeout(() => setCopiedUrl(false), 3000);
249
+ }
250
+ catch {
251
+ setCompleteError(`Review saved! Copy this URL: ${shareUrl}`);
252
+ }
253
+ }
254
+ catch (err) {
255
+ setCompleteError(err instanceof Error ? err.message : String(err));
256
+ }
257
+ finally {
258
+ setIsUploading(false);
259
+ }
260
+ }, [comments, embeddableId, savvy, injectedProjectId]);
261
+ const handleJumpToComment = useCallback((c) => {
262
+ if (c.componentId) {
263
+ scrollToComponent(c.componentId);
264
+ }
265
+ }, []);
266
+ const isReviewMode = !!(feedbackReviewId && feedbackProjectId);
267
+ if (loadingReview) {
268
+ return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx("div", { className: "text-sm text-slate-400", children: "Loading feedback review..." }) }));
269
+ }
270
+ if (loadError && isReviewMode) {
271
+ return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx("div", { className: "rounded-xl bg-rose-500/10 px-4 py-3 text-sm text-rose-200 ring-1 ring-inset ring-rose-500/20", children: loadError }) }));
272
+ }
273
+ return (_jsxs("div", { className: "mx-auto flex h-full w-full max-w-6xl flex-col gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("div", { className: "text-xs font-semibold tracking-wide text-slate-100", children: isReviewMode ? 'Feedback Review' : 'Feedback' }), !isReviewMode && (_jsx("div", { className: "flex items-center gap-2", children: !sessionActive ? (_jsx("button", { type: "button", onClick: handleStartSession, className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-emerald-600 px-2.5 py-1.5 text-xs font-semibold text-white ring-1 ring-inset ring-emerald-500 hover:bg-emerald-500", children: "Start feedback session" })) : (_jsx(_Fragment, { children: comments.length > 0 && (_jsx("button", { type: "button", onClick: handleCompleteReview, disabled: isUploading, className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-sky-600 px-2.5 py-1.5 text-xs font-semibold text-white ring-1 ring-inset ring-sky-500 hover:bg-sky-500 disabled:opacity-50 disabled:cursor-not-allowed", children: isUploading ? 'Uploading…' : copiedUrl ? '✓ Copied!' : 'Complete feedback review' })) })) }))] }), (completeError || submitError) && (_jsx("div", { className: "rounded-xl bg-rose-500/10 px-3 py-2 text-xs text-rose-200 ring-1 ring-inset ring-rose-500/20", children: completeError ?? submitError })), !sessionActive && !isReviewMode && (_jsx("div", { className: "text-[11px] text-slate-400", children: "Click \"Start feedback session\" to begin collecting feedback on components." })), (sessionActive || isReviewMode) && (_jsxs("div", { className: "flex min-h-0 flex-1 gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-3 overflow-auto", children: [!isReviewMode && (_jsxs("div", { className: "rounded-xl bg-white/5 p-3 ring-1 ring-inset ring-white/10", children: [_jsx("div", { className: "mb-2 text-[11px] font-semibold uppercase tracking-wider text-slate-400", children: "Select element" }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [state === 'idle' && (_jsxs("button", { type: "button", onClick: startInspecting, className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-sky-600 px-2.5 py-1.5 text-xs font-semibold text-white ring-1 ring-inset ring-sky-500 hover:bg-sky-500", children: [_jsx("span", { className: "text-sm", children: "\u2316" }), "Select element"] })), state === 'inspecting' && (_jsx("button", { type: "button", onClick: stopInspecting, className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-rose-600 px-2.5 py-1.5 text-xs font-semibold text-white ring-1 ring-inset ring-rose-500 hover:bg-rose-500", children: "Stop selecting" })), state === 'selected' && selectedComponent && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "text-xs text-slate-300", children: [selectedComponent.key, " (", selectedComponent.type, ")"] }), _jsx("button", { type: "button", onClick: inspectAnother, className: "rounded-lg bg-sky-600 px-2.5 py-1.5 text-xs font-semibold text-white hover:bg-sky-500", children: "Change" }), _jsx("button", { type: "button", onClick: clearSelection, className: "rounded-lg bg-slate-700 px-2.5 py-1.5 text-xs font-semibold text-slate-200 hover:bg-slate-600", children: "Clear" })] }))] }), error && (_jsx("div", { className: "mt-2 text-xs text-rose-300", children: error }))] })), sessionActive && !isReviewMode && (_jsxs("div", { className: "flex flex-col gap-2 rounded-xl bg-white/5 p-3 ring-1 ring-inset ring-white/10", children: [_jsx("div", { className: "text-[11px] font-semibold uppercase tracking-wider text-slate-400", children: "Describe the change you want" }), _jsx("textarea", { value: commentText, onChange: (e) => setCommentText(e.target.value), placeholder: "e.g. Make this button larger, change the color to blue...", className: "min-h-[80px] w-full rounded-lg bg-slate-900/60 px-3 py-2 text-xs text-slate-100 ring-1 ring-inset ring-slate-600 placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-sky-500/60", rows: 3 }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("label", { className: "flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs font-medium text-slate-200 hover:bg-slate-700/50", children: [_jsx("input", { type: "checkbox", checked: includeViewportScreenshot, onChange: (e) => setIncludeViewportScreenshot(e.target.checked), className: "rounded border-slate-600 bg-slate-800 text-sky-500 focus:ring-sky-500" }), "Include viewport screenshot"] }), selectedComponentId && (_jsxs("label", { className: "flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs font-medium text-slate-200 hover:bg-slate-700/50", children: [_jsx("input", { type: "checkbox", checked: includeComponentScreenshot, onChange: (e) => setIncludeComponentScreenshot(e.target.checked), className: "rounded border-slate-600 bg-slate-800 text-sky-500 focus:ring-sky-500" }), "Include component screenshot"] })), _jsxs("label", { className: "cursor-pointer rounded-lg bg-slate-700 px-2.5 py-1.5 text-xs font-medium text-slate-200 hover:bg-slate-600", children: ["Attach file", _jsx("input", { type: "file", accept: "image/*,.pdf", className: "hidden", onChange: handleFileAttach })] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((a, i) => (_jsxs("div", { className: "flex items-center gap-1 rounded bg-slate-800 px-2 py-1 text-[10px]", children: [a.type === 'screenshot' ? '📷' : '📎', " ", a.filename ?? 'file', _jsx("button", { type: "button", onClick: () => removeAttachment(i), className: "text-rose-400 hover:text-rose-300", children: "\u00D7" })] }, i))) })), _jsx("button", { type: "button", onClick: handleSubmitComment, disabled: !commentText.trim() && attachments.length === 0, className: "self-start rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white ring-1 ring-inset ring-emerald-500 hover:bg-emerald-500 disabled:opacity-50 disabled:cursor-not-allowed", children: "Add comment" })] }))] }), _jsxs("div", { className: "flex w-72 shrink-0 flex-col", children: [_jsxs("div", { className: "mb-2 text-[11px] font-semibold uppercase tracking-wider text-slate-400", children: ["Comments (", comments.length, ")"] }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto rounded-xl bg-slate-950/60 p-3 ring-1 ring-inset ring-white/10", children: comments.length === 0 ? (_jsx("div", { className: "text-[11px] text-slate-500 italic", children: "No comments yet." })) : (_jsx("div", { className: "flex flex-col gap-2", children: comments.map((c) => (_jsxs("button", { type: "button", onClick: () => handleJumpToComment(c), className: "text-left rounded-lg bg-white/5 p-2 text-xs text-slate-200 ring-1 ring-inset ring-white/10 hover:bg-white/10 transition-colors", children: [_jsx("div", { className: "font-medium text-slate-100 line-clamp-2", children: c.comment || '(No description)' }), _jsx("div", { className: "mt-1 text-[10px] text-slate-500", children: c.componentKey != null
274
+ ? `Component: ${c.componentKey} (${c.componentType ?? c.componentId ?? ''})`
275
+ : c.componentId
276
+ ? `Component: ${c.componentId}`
277
+ : `Page: ${c.pageKey}` })] }, c.id))) })) })] })] }))] }));
278
+ }
279
+ //# sourceMappingURL=FeedbackPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedbackPanel.js","sourceRoot":"","sources":["../../src/workbench/FeedbackPanel.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAc,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC/D,OAAO,WAAW,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAiD9C,MAAM,eAAe,GAAG,kBAAkB,CAAA;AAE1C,6EAA6E;AAC7E,SAAS,iBAAiB,CAAC,QAAgB,EAAE,SAAiB;IAC5D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACzC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAA;IAChD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAA;IAClD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,WAAW,CACxB,QAAgB,EAChB,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC5C,MAAM,GAAG,GAAG,GAAG,IAAI,6BAA6B,eAAe,IAAI,SAAS,IAAI,QAAQ,OAAO,CAAA;QAC/F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QACxB,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,IAAI,iBAAiB,GAAuB,IAAI,CAAA;AAEhD,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,IAAI,iBAAiB,EAAE,CAAC;QACtB,iBAAiB,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAA;QACpC,iBAAiB,GAAG,IAAI,CAAA;IAC1B,CAAC;IACD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,WAAW,EAAE,CAAuB,CAAA;IAC9E,IAAI,EAAE,EAAE,CAAC;QACP,EAAE,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC1D,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,mCAAmC,CAAA;QACtD,iBAAiB,GAAG,EAAE,CAAA;QACtB,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,iBAAiB,KAAK,EAAE,EAAE,CAAC;gBAC7B,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAA;gBACrB,iBAAiB,GAAG,IAAI,CAAA;YAC1B,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAA;IACV,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAC5B,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAAE,iBAAiB,GACT;IACnB,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACzD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAoB,EAAE,CAAC,CAAA;IAC/D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAClD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAiC,EAAE,CAAC,CAAA;IAClF,MAAM,CAAC,yBAAyB,EAAE,4BAA4B,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACjF,MAAM,CAAC,0BAA0B,EAAE,6BAA6B,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnF,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAChD,CAAC,CAAC,CAAC,gBAAgB,IAAI,iBAAiB,CAAC,CAC1C,CAAA;IACD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAwB,IAAI,CAAC,CAAA;IAC7E,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IACvE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACjD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACrD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAC/D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEnE,MAAM,EACJ,KAAK,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,GACf,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAA;IAEvC,MAAM,KAAK,GAAI,MAAsB,CAAC,KAAK,CAAA;IAE3C,uFAAuF;IACvF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,gBAAgB,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC5C,gBAAgB,CAAC,KAAK,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QACD,IAAI,SAAS,GAAG,KAAK,CAAA;QACrB,YAAY,CAAC,IAAI,CAAC,CAAA;QAClB,WAAW,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;aAC7C,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACf,IAAI,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;gBACzB,eAAe,CAAC,MAAM,CAAC,CAAA;gBACvB,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YAC9B,CAAC;iBAAM,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjC,YAAY,CAAC,mDAAmD,CAAC,CAAA;YACnE,CAAC;QACH,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,SAAS;gBAAE,gBAAgB,CAAC,KAAK,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;QACJ,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAA;QAClB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,CAAA;IAEzC,MAAM,kBAAkB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC1C,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACtB,WAAW,CAAC,EAAE,CAAC,CAAA;IACjB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,2DAA2D;IAC3D,MAAM,kBAAkB,GAAG,WAAW,CAAC,KAAK,IAA6C,EAAE;QACzF,MAAM,WAAW,GAAmC,EAAE,CAAA;QACtD,IAAI,yBAAyB,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE;oBAC9C,OAAO,EAAE,IAAI;oBACb,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,KAAK;oBACd,mDAAmD;oBACnD,cAAc,EAAE,CAAC,EAAE,EAAE,EAAE,CACrB,EAAE,CAAC,EAAE,KAAK,8BAA8B;wBACxC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,+BAA+B,CAAC;iBAChD,CAAC,CAAA;gBACF,WAAW,CAAC,IAAI,CAAC;oBACf,IAAI,EAAE,YAAY;oBAClB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;oBACnC,QAAQ,EAAE,cAAc;iBACzB,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC;QACD,IAAI,0BAA0B,IAAI,mBAAmB,EAAE,CAAC;YACtD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,mBAAmB,EAAE,CAAuB,CAAA;YACtF,IAAI,EAAE,EAAE,CAAC;gBACP,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE;wBACnC,OAAO,EAAE,IAAI;wBACb,UAAU,EAAE,IAAI;wBAChB,OAAO,EAAE,KAAK;qBACf,CAAC,CAAA;oBACF,WAAW,CAAC,IAAI,CAAC;wBACf,IAAI,EAAE,YAAY;wBAClB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;wBACnC,QAAQ,EAAE,aAAa,mBAAmB,MAAM;qBACjD,CAAC,CAAA;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAA;gBAC/D,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,WAAW,CAAA;IACpB,CAAC,EAAE,CAAC,yBAAyB,EAAE,0BAA0B,EAAE,mBAAmB,CAAC,CAAC,CAAA;IAEhF,MAAM,gBAAgB,GAAG,WAAW,CAAC,CAAC,CAAsC,EAAE,EAAE;QAC9E,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;QAC5B,IAAI,CAAC,KAAK,EAAE,MAAM;YAAE,OAAM;QAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAA;QAC/B,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;YACnB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAgB,CAAA;YACpC,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACvB,GAAG,IAAI;gBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE;aACrD,CAAC,CAAA;QACJ,CAAC,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAA;IACrB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,gBAAgB,GAAG,WAAW,CAAC,CAAC,KAAa,EAAE,EAAE;QACrD,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAA;IAC9D,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,mBAAmB,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACjD,MAAM,qBAAqB,GAAG,MAAM,kBAAkB,EAAE,CAAA;QACxD,MAAM,cAAc,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,qBAAqB,CAAC,CAAA;QACjE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAC9D,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;YAC/C,cAAc,CAAC,oEAAoE,CAAC,CAAA;YACpF,OAAM;QACR,CAAC;QACD,cAAc,CAAC,IAAI,CAAC,CAAA;QAEpB,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,OAAO,GAAG,QAAQ,EAAE,gBAAgB,IAAI,SAAS,CAAA;QACvD,MAAM,MAAM,GAAG,QAAQ,EAAE,eAAe,IAAI,SAAS,CAAA;QAErD,MAAM,OAAO,GAAoB;YAC/B,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,OAAO;YACP,MAAM;YACN,YAAY,EAAE,iBAAiB,EAAE,GAAG;YACpC,WAAW,EAAE,mBAAmB,IAAI,SAAS;YAC7C,aAAa,EAAE,iBAAiB,EAAE,IAAI;YACtC,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE;YAC3B,WAAW,EAAE,cAAc;YAC3B,QAAQ,EAAE,QAA+C;YACzD,QAAQ,EAAE;gBACR,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,aAAa,EAAE,MAAM,CAAC,UAAU;gBAChC,cAAc,EAAE,MAAM,CAAC,WAAW;aACnC;SACF,CAAA;QAED,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QACzC,cAAc,CAAC,EAAE,CAAC,CAAA;QAClB,cAAc,CAAC,EAAE,CAAC,CAAA;QAClB,4BAA4B,CAAC,KAAK,CAAC,CAAA;QACnC,6BAA6B,CAAC,KAAK,CAAC,CAAA;QACpC,cAAc,EAAE,CAAA;IAClB,CAAC,EAAE;QACD,WAAW;QACX,WAAW;QACX,iBAAiB;QACjB,mBAAmB;QACnB,YAAY;QACZ,KAAK;QACL,cAAc;QACd,kBAAkB;KACnB,CAAC,CAAA;IAEF,MAAM,oBAAoB,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QACjC,IAAI,CAAC,KAAK,EAAE,WAAW;YAAE,OAAM;QAE/B,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACtB,cAAc,CAAC,IAAI,CAAC,CAAA;QAEpB,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;QAChD,qFAAqF;QACrF,MAAM,SAAS,GACb,iBAAiB,IAAI,QAAQ,EAAE,UAAU,IAAI,UAAU,CAAA;QAEzD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;QACpC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAClD,MAAM,MAAM,GAAmB;YAC7B,EAAE,EAAE,QAAQ;YACZ,YAAY;YACZ,SAAS;YACT,OAAO;YACP,QAAQ;YACR,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAA;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,wBAAwB,EAAE,CAAA;YAC3C,2EAA2E;YAC3E,MAAM,WAAW,GAAG,GAAG,SAAS,IAAI,QAAQ,OAAO,CAAA;YACnD,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO;iBAClD,IAAI,CAAC,eAAe,CAAC;iBACrB,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;gBAC3C,WAAW,EAAE,kBAAkB;gBAC/B,MAAM,EAAE,KAAK;aACd,CAAC,CAAA;YAEJ,IAAI,WAAW,EAAE,CAAC;gBAChB,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;gBACrC,OAAM;YACR,CAAC;YAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;YACvD,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;gBAC7C,YAAY,CAAC,IAAI,CAAC,CAAA;gBAClB,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAA;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,gBAAgB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACpE,CAAC;gBAAS,CAAC;YACT,cAAc,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAA;IAEtD,MAAM,mBAAmB,GAAG,WAAW,CAAC,CAAC,CAAkB,EAAE,EAAE;QAC7D,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAClB,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;QAClC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,gBAAgB,IAAI,iBAAiB,CAAC,CAAA;IAE9D,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,SAAS,EAAC,yCAAyC,YACtD,cAAK,SAAS,EAAC,wBAAwB,2CAAiC,GACpE,CACP,CAAA;IACH,CAAC;IAED,IAAI,SAAS,IAAI,YAAY,EAAE,CAAC;QAC9B,OAAO,CACL,cAAK,SAAS,EAAC,yCAAyC,YACtD,cAAK,SAAS,EAAC,8FAA8F,YAC1G,SAAS,GACN,GACF,CACP,CAAA;IACH,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,qDAAqD,aAElE,eAAK,SAAS,EAAC,mDAAmD,aAChE,cAAK,SAAS,EAAC,oDAAoD,YAChE,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,UAAU,GAC1C,EACL,CAAC,YAAY,IAAI,CAChB,cAAK,SAAS,EAAC,yBAAyB,YACrC,CAAC,aAAa,CAAC,CAAC,CAAC,CAChB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAC,kLAAkL,uCAGrL,CACV,CAAC,CAAC,CAAC,CACF,4BACG,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAC,sNAAsN,YAE/N,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,0BAA0B,GAC3E,CACV,GACA,CACJ,GACG,CACP,IACG,EAEL,CAAC,aAAa,IAAI,WAAW,CAAC,IAAI,CACjC,cAAK,SAAS,EAAC,8FAA8F,YAC1G,aAAa,IAAI,WAAW,GACzB,CACP,EAEA,CAAC,aAAa,IAAI,CAAC,YAAY,IAAI,CAClC,cAAK,SAAS,EAAC,4BAA4B,6FAErC,CACP,EAEA,CAAC,aAAa,IAAI,YAAY,CAAC,IAAI,CAClC,eAAK,SAAS,EAAC,2CAA2C,aAExD,eAAK,SAAS,EAAC,kDAAkD,aAE9D,CAAC,YAAY,IAAI,CAChB,eAAK,SAAS,EAAC,2DAA2D,aACxE,cAAK,SAAS,EAAC,wEAAwE,+BAEjF,EACN,eAAK,SAAS,EAAC,mCAAmC,aAC/C,KAAK,KAAK,MAAM,IAAI,CACnB,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,eAAe,EACxB,SAAS,EAAC,sKAAsK,aAEhL,eAAM,SAAS,EAAC,SAAS,uBAAe,sBAEjC,CACV,EACA,KAAK,KAAK,YAAY,IAAI,CACzB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,cAAc,EACvB,SAAS,EAAC,yKAAyK,+BAG5K,CACV,EACA,KAAK,KAAK,UAAU,IAAI,iBAAiB,IAAI,CAC5C,8BACE,gBAAM,SAAS,EAAC,wBAAwB,aACrC,iBAAiB,CAAC,GAAG,QAAI,iBAAiB,CAAC,IAAI,SAC3C,EACP,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,cAAc,EACvB,SAAS,EAAC,uFAAuF,uBAG1F,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,cAAc,EACvB,SAAS,EAAC,+FAA+F,sBAGlG,IACR,CACJ,IACG,EACL,KAAK,IAAI,CACR,cAAK,SAAS,EAAC,4BAA4B,YAAE,KAAK,GAAO,CAC1D,IACG,CACP,EAGA,aAAa,IAAI,CAAC,YAAY,IAAI,CACjC,eAAK,SAAS,EAAC,+EAA+E,aAC5F,cAAK,SAAS,EAAC,mEAAmE,6CAE5E,EACN,mBACE,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC/C,WAAW,EAAC,2DAA2D,EACvE,SAAS,EAAC,mMAAmM,EAC7M,IAAI,EAAE,CAAC,GACP,EACF,eAAK,SAAS,EAAC,mCAAmC,aAChD,iBAAO,SAAS,EAAC,0HAA0H,aACzI,gBACE,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,yBAAyB,EAClC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,4BAA4B,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAC/D,SAAS,EAAC,uEAAuE,GACjF,mCAEI,EACP,mBAAmB,IAAI,CACtB,iBAAO,SAAS,EAAC,0HAA0H,aACzI,gBACE,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,0BAA0B,EACnC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,6BAA6B,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAChE,SAAS,EAAC,uEAAuE,GACjF,oCAEI,CACT,EACD,iBAAO,SAAS,EAAC,4GAA4G,4BAE3H,gBACE,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,SAAS,EAAC,QAAQ,EAClB,QAAQ,EAAE,gBAAgB,GAC1B,IACI,IACJ,EACL,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CACzB,cAAK,SAAS,EAAC,sBAAsB,YAClC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACzB,eAEE,SAAS,EAAC,oEAAoE,aAE7E,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAG,CAAC,CAAC,QAAQ,IAAI,MAAM,EAC7D,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAClC,SAAS,EAAC,mCAAmC,uBAGtC,KAVJ,CAAC,CAWF,CACP,CAAC,GACE,CACP,EACD,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EACzD,SAAS,EAAC,2LAA2L,4BAG9L,IACL,CACP,IACG,EAGN,eAAK,SAAS,EAAC,6BAA6B,aAC1C,eAAK,SAAS,EAAC,wEAAwE,2BAC1E,QAAQ,CAAC,MAAM,SACtB,EACN,cAAK,SAAS,EAAC,6FAA6F,YACzG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACvB,cAAK,SAAS,EAAC,mCAAmC,iCAE5C,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,qBAAqB,YACjC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACnB,kBAEE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,EACrC,SAAS,EAAC,gIAAgI,aAE1I,cAAK,SAAS,EAAC,yCAAyC,YACrD,CAAC,CAAC,OAAO,IAAI,kBAAkB,GAC5B,EACN,cAAK,SAAS,EAAC,iCAAiC,YAC7C,CAAC,CAAC,YAAY,IAAI,IAAI;oDACrB,CAAC,CAAC,cAAc,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,GAAG;oDAC5E,CAAC,CAAC,CAAC,CAAC,WAAW;wDACb,CAAC,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE;wDAC/B,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GACtB,KAdD,CAAC,CAAC,EAAE,CAeF,CACV,CAAC,GACE,CACP,GACG,IACF,IACF,CACP,IACG,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ type PageThumbnailStripProps = {
2
+ embeddableId: string;
3
+ };
4
+ export declare function PageThumbnailStrip({ embeddableId }: PageThumbnailStripProps): import("react/jsx-runtime").JSX.Element | null;
5
+ export {};
6
+ //# sourceMappingURL=PageThumbnailStrip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageThumbnailStrip.d.ts","sourceRoot":"","sources":["../../src/workbench/PageThumbnailStrip.tsx"],"names":[],"mappings":"AAUA,KAAK,uBAAuB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAmBD,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,EAAE,uBAAuB,kDA+J3E"}
@@ -0,0 +1,124 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * PageThumbnailStrip — horizontal row of page thumbnails below the Workbench.
4
+ * Renders each page as a sandboxed iframe preview when Savvy.getStaticHtml is available,
5
+ * otherwise shows a fallback card with page key and index.
6
+ * Clicking a thumbnail navigates to that page via Savvy.goToPage.
7
+ */
8
+ import { useCallback, useEffect, useRef, useState } from 'react';
9
+ const USERDATA_UPDATED_EVENT = 'embeddables:userdata_updated';
10
+ /** Default viewport width when flow has no breakpoints. Matches common embeddable config. */
11
+ const DEFAULT_VIEWPORT_WIDTH = 800;
12
+ /** Thumbnail card display size (px). */
13
+ const THUMB_WIDTH = 120;
14
+ const THUMB_HEIGHT = 80;
15
+ /** Derive viewport width from flow breakpoints (largest max_width) or default. */
16
+ function getViewportWidth(flow) {
17
+ const breakpoints = flow?.breakpoints;
18
+ if (!breakpoints?.length)
19
+ return DEFAULT_VIEWPORT_WIDTH;
20
+ const max = Math.max(...breakpoints.map((b) => b.max_width));
21
+ return max > 0 ? max : DEFAULT_VIEWPORT_WIDTH;
22
+ }
23
+ export function PageThumbnailStrip({ embeddableId }) {
24
+ const [pages, setPages] = useState([]);
25
+ const [currentPageId, setCurrentPageId] = useState('');
26
+ const [viewportWidth, setViewportWidth] = useState(DEFAULT_VIEWPORT_WIDTH);
27
+ const stripRef = useRef(null);
28
+ const currentCardRef = useRef(null);
29
+ const htmlCacheRef = useRef(new Map());
30
+ const [, setCacheVersion] = useState(0);
31
+ const savvy = window.Savvy;
32
+ const refresh = useCallback(() => {
33
+ if (!savvy?.getFlowJson || !savvy?.getUserData)
34
+ return;
35
+ try {
36
+ const flowJson = savvy.getFlowJson(embeddableId);
37
+ if (flowJson?.pages) {
38
+ setPages(flowJson.pages.map((p) => ({ id: p.id, key: p.key })));
39
+ }
40
+ setViewportWidth(getViewportWidth(flowJson));
41
+ const userData = savvy.getUserData(embeddableId);
42
+ if (userData?.current_page_id) {
43
+ setCurrentPageId(userData.current_page_id);
44
+ }
45
+ }
46
+ catch {
47
+ // ignore errors
48
+ }
49
+ }, [embeddableId, savvy]);
50
+ useEffect(() => {
51
+ const timer = setTimeout(refresh, 100);
52
+ return () => clearTimeout(timer);
53
+ }, [refresh]);
54
+ useEffect(() => {
55
+ const handler = (event) => {
56
+ const detail = event?.detail;
57
+ const eventEmbeddableId = detail?.embeddableId;
58
+ if (typeof eventEmbeddableId === 'string' &&
59
+ eventEmbeddableId.length > 0 &&
60
+ eventEmbeddableId !== embeddableId) {
61
+ return;
62
+ }
63
+ refresh();
64
+ };
65
+ window.addEventListener(USERDATA_UPDATED_EVENT, handler);
66
+ return () => window.removeEventListener(USERDATA_UPDATED_EVENT, handler);
67
+ }, [embeddableId, refresh]);
68
+ // Load HTML for each page when getStaticHtml is available; cache in ref
69
+ useEffect(() => {
70
+ if (!savvy?.getStaticHtml || pages.length === 0)
71
+ return;
72
+ const cache = htmlCacheRef.current;
73
+ cache.clear();
74
+ let cancelled = false;
75
+ const load = () => {
76
+ for (const page of pages) {
77
+ if (cancelled)
78
+ return;
79
+ const html = savvy.getStaticHtml?.(embeddableId, page.id);
80
+ if (html != null && html !== '') {
81
+ cache.set(page.id, html);
82
+ }
83
+ }
84
+ if (!cancelled) {
85
+ setCacheVersion((v) => v + 1);
86
+ }
87
+ };
88
+ load();
89
+ return () => {
90
+ cancelled = true;
91
+ };
92
+ }, [embeddableId, pages, savvy]);
93
+ // Auto-scroll strip to keep the current page thumbnail visible
94
+ useEffect(() => {
95
+ const strip = stripRef.current;
96
+ const card = currentCardRef.current;
97
+ if (!strip || !card)
98
+ return;
99
+ const cardRect = card.getBoundingClientRect();
100
+ const stripRect = strip.getBoundingClientRect();
101
+ if (cardRect.left < stripRect.left || cardRect.right > stripRect.right) {
102
+ card.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
103
+ }
104
+ }, [currentPageId, pages]);
105
+ const handleClick = (pageId) => {
106
+ if (savvy?.goToPage) {
107
+ savvy.goToPage(embeddableId, pageId);
108
+ }
109
+ };
110
+ if (pages.length === 0)
111
+ return null;
112
+ return (_jsx("div", { ref: stripRef, className: "flex h-[90px] shrink-0 gap-2 overflow-x-auto border-t border-white/10 bg-slate-950/70 px-3 py-2", role: "tablist", "aria-label": "Page thumbnails", children: pages.map((page, index) => {
113
+ const isCurrent = page.id === currentPageId;
114
+ const html = htmlCacheRef.current.get(page.id);
115
+ const hasGetStaticHtml = typeof savvy?.getStaticHtml === 'function';
116
+ return (_jsxs("button", { ref: isCurrent ? currentCardRef : undefined, type: "button", onClick: () => handleClick(page.id), role: "tab", "aria-selected": isCurrent, "aria-label": `Page ${index + 1}: ${page.key}`, className: `flex shrink-0 flex-col items-stretch overflow-hidden rounded-lg bg-white/5 ring-1 ring-inset transition-all hover:bg-white/10 hover:ring-white/20 ${isCurrent ? 'ring-2 ring-blue-500 ring-offset-2 ring-offset-slate-950' : 'ring-white/10'}`, children: [_jsx("div", { className: "relative overflow-hidden", style: { width: THUMB_WIDTH, height: THUMB_HEIGHT }, children: hasGetStaticHtml && html ? (_jsx("iframe", { srcDoc: html, title: `Preview: ${page.key}`, sandbox: "", className: "pointer-events-none", style: {
117
+ width: viewportWidth,
118
+ height: Math.round((viewportWidth * THUMB_HEIGHT) / THUMB_WIDTH),
119
+ transform: `scale(${THUMB_WIDTH / viewportWidth})`,
120
+ transformOrigin: 'top left',
121
+ } })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-slate-800/50 text-[10px] text-slate-500", children: index + 1 })) }), _jsxs("div", { className: "flex items-center gap-1 px-1.5 py-1 text-[10px] text-slate-400", children: [_jsx("span", { className: "truncate", children: page.key }), _jsx("span", { className: "shrink-0", children: index + 1 })] })] }, page.id));
122
+ }) }));
123
+ }
124
+ //# sourceMappingURL=PageThumbnailStrip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageThumbnailStrip.js","sourceRoot":"","sources":["../../src/workbench/PageThumbnailStrip.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type ToastMessage = {
2
+ id: number;
3
+ text: string;
4
+ type: 'success' | 'error';
5
+ };
6
+ type ToastProps = {
7
+ message: ToastMessage;
8
+ onDismiss: (id: number) => void;
9
+ };
10
+ /** Single toast notification that auto-dismisses after a timeout. */
11
+ export declare function Toast({ message, onDismiss }: ToastProps): import("react/jsx-runtime").JSX.Element;
12
+ /** Container that renders a stack of toast messages anchored above the workbench bar. */
13
+ export declare function ToastContainer({ messages, onDismiss, }: {
14
+ messages: ToastMessage[];
15
+ onDismiss: (id: number) => void;
16
+ }): import("react/jsx-runtime").JSX.Element | null;
17
+ export {};
18
+ //# sourceMappingURL=Toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../src/workbench/Toast.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;CAC1B,CAAA;AAED,KAAK,UAAU,GAAG;IAChB,OAAO,EAAE,YAAY,CAAA;IACrB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,CAAA;AASD,qEAAqE;AACrE,wBAAgB,KAAK,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,2CA8CvD;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,kDAUA"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ const AUTO_DISMISS_MS = 3000;
4
+ const FADE_OUT_MS = 200;
5
+ /** Shared with WorkbenchApp / panels: slate surface, sky for positive, rose for errors. */
6
+ const workbenchToastSurfaceClass = 'rounded-lg bg-slate-800 px-3 py-2 text-xs font-medium shadow-2xl shadow-black/40 ring-1 ring-inset ring-slate-600';
7
+ /** Single toast notification that auto-dismisses after a timeout. */
8
+ export function Toast({ message, onDismiss }) {
9
+ const [visible, setVisible] = useState(false);
10
+ const dismissTimerRef = useRef(null);
11
+ // Animate in on mount
12
+ useEffect(() => {
13
+ const frame = requestAnimationFrame(() => setVisible(true));
14
+ return () => cancelAnimationFrame(frame);
15
+ }, []);
16
+ // Clear the inner dismiss timer on unmount to prevent leaks
17
+ useEffect(() => {
18
+ return () => {
19
+ if (dismissTimerRef.current)
20
+ clearTimeout(dismissTimerRef.current);
21
+ };
22
+ }, []);
23
+ // Auto-dismiss after timeout
24
+ useEffect(() => {
25
+ const timer = setTimeout(() => {
26
+ setVisible(false);
27
+ dismissTimerRef.current = setTimeout(() => onDismiss(message.id), FADE_OUT_MS);
28
+ }, AUTO_DISMISS_MS);
29
+ return () => clearTimeout(timer);
30
+ }, [message.id, onDismiss]);
31
+ const handleDismiss = useCallback(() => {
32
+ setVisible(false);
33
+ if (dismissTimerRef.current)
34
+ clearTimeout(dismissTimerRef.current);
35
+ dismissTimerRef.current = setTimeout(() => onDismiss(message.id), FADE_OUT_MS);
36
+ }, [message.id, onDismiss]);
37
+ const isSuccess = message.type === 'success';
38
+ return (_jsx("div", { role: "status", "aria-live": "polite", onClick: handleDismiss, className: `pointer-events-auto cursor-pointer transition-all duration-200 ${workbenchToastSurfaceClass} ${visible ? 'translate-y-0 opacity-100' : 'translate-y-2 opacity-0'} ${isSuccess ? 'text-sky-200' : 'text-rose-300'}`, children: message.text }));
39
+ }
40
+ /** Container that renders a stack of toast messages anchored above the workbench bar. */
41
+ export function ToastContainer({ messages, onDismiss, }) {
42
+ if (messages.length === 0)
43
+ return null;
44
+ return (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-full mb-2 flex flex-col items-end gap-2 px-4", children: messages.map((msg) => (_jsx(Toast, { message: msg, onDismiss: onDismiss }, msg.id))) }));
45
+ }
46
+ //# sourceMappingURL=Toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toast.js","sourceRoot":"","sources":["../../src/workbench/Toast.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  type UserDataPanelProps = {
2
2
  embeddableId: string;
3
+ showToast: (text: string, type?: 'success' | 'error') => void;
3
4
  };
4
- export declare function UserDataPanel({ embeddableId }: UserDataPanelProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function UserDataPanel({ embeddableId, showToast }: UserDataPanelProps): import("react/jsx-runtime").JSX.Element;
5
6
  export {};
6
7
  //# sourceMappingURL=UserDataPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserDataPanel.d.ts","sourceRoot":"","sources":["../../src/workbench/UserDataPanel.tsx"],"names":[],"mappings":"AAIA,KAAK,kBAAkB,GAAG;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AA0LD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,EAAE,kBAAkB,2CA2nBjE"}
1
+ {"version":3,"file":"UserDataPanel.d.ts","sourceRoot":"","sources":["../../src/workbench/UserDataPanel.tsx"],"names":[],"mappings":"AAIA,KAAK,kBAAkB,GAAG;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,KAAK,IAAI,CAAA;CAC9D,CAAA;AA0LD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CA6nB5E"}
@@ -143,7 +143,7 @@ function detectLanguagesFromFlow(flowJson) {
143
143
  }
144
144
  return Array.from(langCodes).sort();
145
145
  }
146
- export function UserDataPanel({ embeddableId }) {
146
+ export function UserDataPanel({ embeddableId, showToast }) {
147
147
  const [rawJson, setRawJson] = useState('');
148
148
  const [error, setError] = useState(null);
149
149
  const [isSaving, setIsSaving] = useState(false);
@@ -319,6 +319,7 @@ export function UserDataPanel({ embeddableId }) {
319
319
  setError(null);
320
320
  try {
321
321
  savvy.resetUserData(embeddableId);
322
+ showToast('User data reset — all values returned to defaults.');
322
323
  // Hack - find all inputs inside the main page and set them to empty string
323
324
  setTimeout(() => {
324
325
  const inputs = document.querySelectorAll('savvy input');