@widgetic/creator 0.3.67 → 0.3.69
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 +52 -98
- package/dist/CreatorApp.svelte +495 -164
- package/dist/components/WidgetDetails.svelte +32 -3
- package/dist/components/WidgetDetails.svelte.d.ts +2 -0
- package/dist/components/WidgetShapeLivePreviews.svelte +118 -45
- package/dist/components/WidgetShapeLivePreviews.svelte.d.ts +2 -0
- package/dist/stores/websocketStore.d.ts +1 -1
- package/dist/stores/websocketStore.js +1 -1
- package/dist/utils/freezeWatchdog.js +38 -7
- package/package.json +4 -5
|
@@ -806,6 +806,11 @@
|
|
|
806
806
|
// console.log('[WidgetDetails] Codegen preview marked fresh — skipping GitLab rebuilds for 30s');
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
+
/** Restore Checkpoint must compile GitLab HEAD even inside the post-codegen window. */
|
|
810
|
+
export function clearCodegenPreviewFresh(): void {
|
|
811
|
+
codegenPreviewFreshUntil = 0;
|
|
812
|
+
}
|
|
813
|
+
|
|
809
814
|
/** Timestamp of generation start — set by the parent when the operation begins. */
|
|
810
815
|
let codegenStartedAt: number | null = null;
|
|
811
816
|
/** Parent calls this when a generation starts for THIS widget's panel. */
|
|
@@ -2394,6 +2399,10 @@
|
|
|
2394
2399
|
if (previewHealthCheckHandle) { clearInterval(previewHealthCheckHandle); previewHealthCheckHandle = null; }
|
|
2395
2400
|
previewFallbackHandle = setTimeout(() => {
|
|
2396
2401
|
if ((previewLoading || previewHydrating) && (directPreviewUrl || iframeSrc)) {
|
|
2402
|
+
if (previewWidgetReady && !previewHydrating) {
|
|
2403
|
+
previewLoading = false;
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2397
2406
|
console.warn('[WidgetDetails] Preview fallback timeout — revealing iframe');
|
|
2398
2407
|
previewLoading = false;
|
|
2399
2408
|
previewError = null;
|
|
@@ -3172,6 +3181,11 @@
|
|
|
3172
3181
|
}
|
|
3173
3182
|
|
|
3174
3183
|
let lastWsPreviewSignature: string | null = null;
|
|
3184
|
+
let pendingWorkerPreviewFromWs: {
|
|
3185
|
+
workerUrl: string;
|
|
3186
|
+
buildTimeMs?: number | null;
|
|
3187
|
+
eventTimestamp?: string | null;
|
|
3188
|
+
} | null = null;
|
|
3175
3189
|
|
|
3176
3190
|
async function applyWorkerPreviewFromEvent(
|
|
3177
3191
|
workerUrl: string,
|
|
@@ -3188,6 +3202,16 @@
|
|
|
3188
3202
|
if (signature === lastWsPreviewSignature) return;
|
|
3189
3203
|
lastWsPreviewSignature = signature;
|
|
3190
3204
|
|
|
3205
|
+
if (isGeneratingCode) {
|
|
3206
|
+
pendingWorkerPreviewFromWs = { workerUrl, buildTimeMs, eventTimestamp };
|
|
3207
|
+
dynamicWorkerPreviewUrl = fetchUrl;
|
|
3208
|
+
directPreviewUrl = toWorkerPreviewDisplayUrl(fetchUrl);
|
|
3209
|
+
console.log('[WidgetDetails] Defer Worker iframe remount until codegen finishes');
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
pendingWorkerPreviewFromWs = null;
|
|
3214
|
+
|
|
3191
3215
|
// console.log('[WidgetDetails] Worker preview ready — force reloading iframe', {
|
|
3192
3216
|
// widgetId: selectedWidgetId,
|
|
3193
3217
|
// previewUrl: fetchUrl,
|
|
@@ -3215,6 +3239,13 @@
|
|
|
3215
3239
|
);
|
|
3216
3240
|
}
|
|
3217
3241
|
|
|
3242
|
+
$: if (!isGeneratingCode && pendingWorkerPreviewFromWs) {
|
|
3243
|
+
const pending = pendingWorkerPreviewFromWs;
|
|
3244
|
+
pendingWorkerPreviewFromWs = null;
|
|
3245
|
+
lastWsPreviewSignature = null;
|
|
3246
|
+
void applyWorkerPreviewFromEvent(pending.workerUrl, pending.buildTimeMs, pending.eventTimestamp);
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3218
3249
|
// Process queued actions
|
|
3219
3250
|
$: if (queuedActions.length > 0) {
|
|
3220
3251
|
void processQueuedActions();
|
|
@@ -3910,9 +3941,7 @@
|
|
|
3910
3941
|
|
|
3911
3942
|
<style>
|
|
3912
3943
|
.widget-details-drag-bar button:focus,
|
|
3913
|
-
.widget-details-drag-bar button:focus-visible
|
|
3914
|
-
.widget-details-drag-bar a:focus,
|
|
3915
|
-
.widget-details-drag-bar a:focus-visible {
|
|
3944
|
+
.widget-details-drag-bar button:focus-visible {
|
|
3916
3945
|
outline: none;
|
|
3917
3946
|
box-shadow: none;
|
|
3918
3947
|
}
|
|
@@ -108,6 +108,7 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
108
108
|
showPublishedArtifact?: (url: string | null) => void;
|
|
109
109
|
finishCompositionHydration?: () => void;
|
|
110
110
|
markCodegenPreviewFresh?: () => void;
|
|
111
|
+
clearCodegenPreviewFresh?: () => void;
|
|
111
112
|
markCodegenStarted?: () => void;
|
|
112
113
|
hasLiveWorkerPreview?: () => boolean;
|
|
113
114
|
getLatestRuntimeLogs?: (maxLines?: number) => string[];
|
|
@@ -208,6 +209,7 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
208
209
|
showPublishedArtifact: (url: string | null) => void;
|
|
209
210
|
finishCompositionHydration: () => void;
|
|
210
211
|
markCodegenPreviewFresh: () => void;
|
|
212
|
+
clearCodegenPreviewFresh: () => void;
|
|
211
213
|
markCodegenStarted: () => void;
|
|
212
214
|
hasLiveWorkerPreview: () => boolean;
|
|
213
215
|
getLatestRuntimeLogs: (maxLines?: number) => string[];
|
|
@@ -130,11 +130,16 @@
|
|
|
130
130
|
}: Props = $props();
|
|
131
131
|
|
|
132
132
|
const DRAG_BAR_H = 30;
|
|
133
|
-
const BAR_SHAPE_GAP = 4;
|
|
134
133
|
const SHOW_TITLE_MIN_WIDTH = 160;
|
|
135
134
|
/** Minimum chrome width when expanded so version + Live/Publish controls fit. */
|
|
136
135
|
const MIN_EXPANDED_CHROME_WIDTH = 220;
|
|
136
|
+
/** Keeps Fabric handles clickable. The strip is inside the shape and outside the iframe. */
|
|
137
137
|
const IFRAME_INSET_PX = 2;
|
|
138
|
+
/**
|
|
139
|
+
* Negative so the bar covers that inset. Otherwise the strip is a dead hit-zone:
|
|
140
|
+
* the pointer falls through to the canvas and selects the widget underneath.
|
|
141
|
+
*/
|
|
142
|
+
const BAR_SHAPE_GAP = -IFRAME_INSET_PX;
|
|
138
143
|
/** ~5 version rows before the menu scrolls (matches chat history panel). */
|
|
139
144
|
/** Row height ≈2.1rem (py-1.5 + text) + 0.6rem vertical padding for the menu box. */
|
|
140
145
|
const VERSION_MENU_ROW_REM = 2.1;
|
|
@@ -200,6 +205,7 @@
|
|
|
200
205
|
const coalescedReloadTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
201
206
|
|
|
202
207
|
export function requestReload(widgetId: string): void {
|
|
208
|
+
if (isGenerating) return;
|
|
203
209
|
const now = Date.now();
|
|
204
210
|
const sinceLast = now - (lastReloadAtByWidgetId[widgetId] || 0);
|
|
205
211
|
if (sinceLast < RELOAD_COALESCE_MS) {
|
|
@@ -222,12 +228,38 @@
|
|
|
222
228
|
lastReloadAtByWidgetId[widgetId] = now;
|
|
223
229
|
cancelPendingRefresh(widgetId);
|
|
224
230
|
const token = Math.max(now, (reloadTokens[widgetId] || 0) + 1);
|
|
225
|
-
pendingRefreshTokens
|
|
231
|
+
// Do not set pendingRefreshTokens — that path runs html2canvas (explicit Refresh only).
|
|
226
232
|
reloadTokens = { ...reloadTokens, [widgetId]: token };
|
|
227
233
|
iframeLoadedByWidgetId = { ...iframeLoadedByWidgetId, [widgetId]: false };
|
|
228
234
|
sync();
|
|
229
235
|
}
|
|
230
236
|
|
|
237
|
+
/** Drop coalesced overlay reloads and pending captures when a generation starts. */
|
|
238
|
+
export function cancelReloads(widgetId?: string): void {
|
|
239
|
+
if (widgetId) {
|
|
240
|
+
const coalesced = coalescedReloadTimers.get(widgetId);
|
|
241
|
+
if (coalesced) {
|
|
242
|
+
clearTimeout(coalesced);
|
|
243
|
+
coalescedReloadTimers.delete(widgetId);
|
|
244
|
+
}
|
|
245
|
+
cancelPendingRefresh(widgetId);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
for (const timer of coalescedReloadTimers.values()) clearTimeout(timer);
|
|
249
|
+
coalescedReloadTimers.clear();
|
|
250
|
+
for (const id of [...pendingRefreshTokens.keys()]) cancelPendingRefresh(id);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function setSelectedVersion(widgetId: string, versionId: string | null): void {
|
|
254
|
+
if (!widgetId) return;
|
|
255
|
+
if (!versionId) {
|
|
256
|
+
const { [widgetId]: _dropped, ...rest } = localSelectedVersionByWidgetId;
|
|
257
|
+
localSelectedVersionByWidgetId = rest;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
localSelectedVersionByWidgetId = { ...localSelectedVersionByWidgetId, [widgetId]: versionId };
|
|
261
|
+
}
|
|
262
|
+
|
|
231
263
|
/** Which shape chrome version menu is open. */
|
|
232
264
|
let openVersionMenuWidgetId = $state<string | null>(null);
|
|
233
265
|
/** Local manual version selection override per widget (ensures trigger updates immediately). */
|
|
@@ -291,17 +323,32 @@
|
|
|
291
323
|
draggingWidgetId = widgetId;
|
|
292
324
|
let lastX = event.clientX;
|
|
293
325
|
let lastY = event.clientY;
|
|
326
|
+
let pendingDx = 0;
|
|
327
|
+
let pendingDy = 0;
|
|
328
|
+
let dragFrame = 0;
|
|
329
|
+
|
|
330
|
+
function flushDrag() {
|
|
331
|
+
dragFrame = 0;
|
|
332
|
+
const dx = pendingDx;
|
|
333
|
+
const dy = pendingDy;
|
|
334
|
+
pendingDx = 0;
|
|
335
|
+
pendingDy = 0;
|
|
336
|
+
if (dx === 0 && dy === 0) return;
|
|
337
|
+
onTranslate?.(widgetId, dx, dy);
|
|
338
|
+
}
|
|
294
339
|
|
|
295
340
|
function onMove(e: PointerEvent) {
|
|
296
|
-
|
|
297
|
-
|
|
341
|
+
pendingDx += e.clientX - lastX;
|
|
342
|
+
pendingDy += e.clientY - lastY;
|
|
298
343
|
lastX = e.clientX;
|
|
299
344
|
lastY = e.clientY;
|
|
300
|
-
if (
|
|
301
|
-
|
|
345
|
+
if (dragFrame) return;
|
|
346
|
+
dragFrame = requestAnimationFrame(flushDrag);
|
|
302
347
|
}
|
|
303
348
|
|
|
304
349
|
function endDrag() {
|
|
350
|
+
if (dragFrame) cancelAnimationFrame(dragFrame);
|
|
351
|
+
flushDrag();
|
|
305
352
|
draggingWidgetId = null;
|
|
306
353
|
document.removeEventListener('pointermove', onMove);
|
|
307
354
|
document.removeEventListener('pointerup', endDrag);
|
|
@@ -415,11 +462,24 @@
|
|
|
415
462
|
return (option.shortLabel || option.label || fallback).trim();
|
|
416
463
|
}
|
|
417
464
|
|
|
465
|
+
let versionMenuBox = $state<{ widgetId: string; top: number; right: number } | null>(null);
|
|
466
|
+
|
|
418
467
|
function toggleVersionMenu(event: MouseEvent, widgetId: string) {
|
|
419
468
|
event.preventDefault();
|
|
420
469
|
event.stopPropagation();
|
|
421
470
|
onSelectShape?.(widgetId);
|
|
422
|
-
|
|
471
|
+
if (openVersionMenuWidgetId === widgetId) {
|
|
472
|
+
openVersionMenuWidgetId = null;
|
|
473
|
+
versionMenuBox = null;
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
477
|
+
versionMenuBox = {
|
|
478
|
+
widgetId,
|
|
479
|
+
top: rect.bottom + 2,
|
|
480
|
+
right: window.innerWidth - rect.right,
|
|
481
|
+
};
|
|
482
|
+
openVersionMenuWidgetId = widgetId;
|
|
423
483
|
}
|
|
424
484
|
|
|
425
485
|
function selectVersion(event: MouseEvent, widgetId: string, versionId: string) {
|
|
@@ -428,7 +488,6 @@
|
|
|
428
488
|
openVersionMenuWidgetId = null;
|
|
429
489
|
if (!versionId) return;
|
|
430
490
|
onVersionChange?.(widgetId, versionId);
|
|
431
|
-
// The parent logs the selected version and resolved commit once the build completes.
|
|
432
491
|
}
|
|
433
492
|
|
|
434
493
|
function handleSetLiveClick(event: MouseEvent, widgetId: string, versionId: string) {
|
|
@@ -586,6 +645,13 @@
|
|
|
586
645
|
{@const bodyWidth = overlay.localWidth ?? overlay.width}
|
|
587
646
|
{@const bodyHeight = overlay.localHeight ?? overlay.height}
|
|
588
647
|
{@const bodyMatrix = overlay.matrix ?? [1, 0, 0, 1, overlay.left, overlay.top]}
|
|
648
|
+
{@const frameScaleX = Math.hypot(bodyMatrix[0], bodyMatrix[1]) || 1}
|
|
649
|
+
{@const frameScaleY = Math.hypot(bodyMatrix[2], bodyMatrix[3]) || 1}
|
|
650
|
+
{@const frameRotation = Math.atan2(bodyMatrix[1], bodyMatrix[0])}
|
|
651
|
+
{@const frameCos = Math.cos(frameRotation)}
|
|
652
|
+
{@const frameSin = Math.sin(frameRotation)}
|
|
653
|
+
{@const frameWidth = Math.max(1, bodyWidth * frameScaleX)}
|
|
654
|
+
{@const frameHeight = Math.max(1, bodyHeight * frameScaleY)}
|
|
589
655
|
{@const showExpandedChrome = isSelected || overlay.width >= SHOW_TITLE_MIN_WIDTH}
|
|
590
656
|
{@const chromeWidth = showExpandedChrome
|
|
591
657
|
? Math.max(overlay.width, MIN_EXPANDED_CHROME_WIDTH)
|
|
@@ -625,6 +691,12 @@
|
|
|
625
691
|
title="Drag widget"
|
|
626
692
|
onpointerdown={(e) => startShapeDrag(e, overlay.widgetId)}
|
|
627
693
|
>
|
|
694
|
+
{#if isPrimary}
|
|
695
|
+
<div
|
|
696
|
+
class="widget-shape-primary-stroke pointer-events-none absolute z-20 bg-[#DC2626]"
|
|
697
|
+
style="left: 0; width: {overlay.width}px; top: {DRAG_BAR_H - IFRAME_INSET_PX - 1}px; height: 2px;"
|
|
698
|
+
></div>
|
|
699
|
+
{/if}
|
|
628
700
|
<span class="widget-shape-drag-grip shrink-0 text-xs leading-none text-gray-400" aria-hidden="true">⠿</span>
|
|
629
701
|
|
|
630
702
|
{#if showExpandedChrome}
|
|
@@ -733,42 +805,6 @@
|
|
|
733
805
|
</HoverTooltip>
|
|
734
806
|
{/if}
|
|
735
807
|
{/if}
|
|
736
|
-
{#if versionMenuOpen}
|
|
737
|
-
{@const hasScroll = versionOptions.length > 5}
|
|
738
|
-
<div
|
|
739
|
-
class="widget-shape-version-menu absolute top-full z-30 mt-0.5 w-max rounded-md border border-emerald-200 bg-white p-1 shadow-lg"
|
|
740
|
-
style="right: -12px;"
|
|
741
|
-
role="listbox"
|
|
742
|
-
>
|
|
743
|
-
<div
|
|
744
|
-
class="widget-shape-version-scroll flex flex-col gap-1 overflow-x-hidden {hasScroll ? 'overflow-y-auto' : 'overflow-y-visible'}"
|
|
745
|
-
style="{hasScroll ? 'max-height: 185px; margin-right: -4px; padding-right: 4px;' : 'max-height: none;'} scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.2) transparent;"
|
|
746
|
-
>
|
|
747
|
-
{#each versionOptions as option (option.id)}
|
|
748
|
-
{@const optionVersion = Number(option.id.slice(2))}
|
|
749
|
-
{@const optionBusy = (void titleRevision, !!resolveIsPublishing?.(overlay.widgetId))}
|
|
750
|
-
{@const optionPublishDisabled = !resolveCanPublishDraft?.(overlay.widgetId, optionVersion)
|
|
751
|
-
|| !!resolvePublishDisabled?.(overlay.widgetId, optionVersion)
|
|
752
|
-
|| (selectedVersion?.id === option.id && selectedPreviewBlocked)}
|
|
753
|
-
{@const optionPublishReason = (void titleRevision, resolvePublishDisabledReason?.(overlay.widgetId, optionVersion))}
|
|
754
|
-
<VersionDropdownRow
|
|
755
|
-
versionLabel={option.shortLabel || option.label}
|
|
756
|
-
dateLabel={option.dateLabel}
|
|
757
|
-
isLive={option.isLive === true}
|
|
758
|
-
isDraft={isDraftVersion(option)}
|
|
759
|
-
selected={selectedVersion?.id === option.id}
|
|
760
|
-
busy={optionBusy}
|
|
761
|
-
publishDisabled={optionPublishDisabled}
|
|
762
|
-
publishDisabledReason={optionPublishReason || 'Build this draft version and make it the Live version'}
|
|
763
|
-
setLiveDisabled={settingLiveVersion === optionVersion}
|
|
764
|
-
onselect={() => selectVersionFromMenu(overlay.widgetId, option.id)}
|
|
765
|
-
onpublish={() => handlePublishDraftClickFromMenu(overlay.widgetId, option.id)}
|
|
766
|
-
onsetlive={() => handleSetLiveClickFromMenu(overlay.widgetId, option.id)}
|
|
767
|
-
/>
|
|
768
|
-
{/each}
|
|
769
|
-
</div>
|
|
770
|
-
</div>
|
|
771
|
-
{/if}
|
|
772
808
|
</div>
|
|
773
809
|
{:else if versionTriggerText && versionTriggerText !== '—'}
|
|
774
810
|
<span
|
|
@@ -829,7 +865,7 @@
|
|
|
829
865
|
<div
|
|
830
866
|
class="widget-shape-live-preview-frame pointer-events-none fixed left-0 top-0 origin-top-left overflow-visible"
|
|
831
867
|
data-widget-id={overlay.widgetId}
|
|
832
|
-
style="height: {
|
|
868
|
+
style="height: {frameHeight}px; width: {frameWidth}px; transform: matrix({frameCos}, {frameSin}, {-frameSin}, {frameCos}, {bodyMatrix[4]}, {bodyMatrix[5]}); z-index: {isSelected ? 60 : 1};"
|
|
833
869
|
>
|
|
834
870
|
<!-- A new reload gets its own window so old load/screenshot replies cannot win. -->
|
|
835
871
|
{#if overlay.src}
|
|
@@ -880,6 +916,43 @@
|
|
|
880
916
|
></div>
|
|
881
917
|
{/if}
|
|
882
918
|
</div>
|
|
919
|
+
{#if versionMenuOpen && versionMenuBox?.widgetId === overlay.widgetId}
|
|
920
|
+
{@const hasScroll = versionOptions.length > 5}
|
|
921
|
+
<div
|
|
922
|
+
class="widget-shape-version-menu pointer-events-auto fixed z-[400] w-max rounded-md border border-emerald-200 bg-white p-1 shadow-lg"
|
|
923
|
+
data-version-menu-for={overlay.widgetId}
|
|
924
|
+
style="top: {versionMenuBox.top}px; right: {versionMenuBox.right}px;"
|
|
925
|
+
role="listbox"
|
|
926
|
+
>
|
|
927
|
+
<div
|
|
928
|
+
class="widget-shape-version-scroll flex flex-col gap-1 overflow-x-hidden {hasScroll ? 'overflow-y-auto' : 'overflow-y-visible'}"
|
|
929
|
+
style="{hasScroll ? 'max-height: 185px; margin-right: -4px; padding-right: 4px;' : 'max-height: none;'} scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.2) transparent;"
|
|
930
|
+
>
|
|
931
|
+
{#each versionOptions as option (option.id)}
|
|
932
|
+
{@const optionVersion = Number(option.id.slice(2))}
|
|
933
|
+
{@const optionBusy = (void titleRevision, !!resolveIsPublishing?.(overlay.widgetId))}
|
|
934
|
+
{@const optionPublishDisabled = !resolveCanPublishDraft?.(overlay.widgetId, optionVersion)
|
|
935
|
+
|| !!resolvePublishDisabled?.(overlay.widgetId, optionVersion)
|
|
936
|
+
|| (selectedVersion?.id === option.id && selectedPreviewBlocked)}
|
|
937
|
+
{@const optionPublishReason = (void titleRevision, resolvePublishDisabledReason?.(overlay.widgetId, optionVersion))}
|
|
938
|
+
<VersionDropdownRow
|
|
939
|
+
versionLabel={option.shortLabel || option.label}
|
|
940
|
+
dateLabel={option.dateLabel}
|
|
941
|
+
isLive={option.isLive === true}
|
|
942
|
+
isDraft={isDraftVersion(option)}
|
|
943
|
+
selected={selectedVersion?.id === option.id}
|
|
944
|
+
busy={optionBusy}
|
|
945
|
+
publishDisabled={optionPublishDisabled}
|
|
946
|
+
publishDisabledReason={optionPublishReason || 'Build this draft version and make it the Live version'}
|
|
947
|
+
setLiveDisabled={settingLiveVersion === optionVersion}
|
|
948
|
+
onselect={() => selectVersionFromMenu(overlay.widgetId, option.id)}
|
|
949
|
+
onpublish={() => handlePublishDraftClickFromMenu(overlay.widgetId, option.id)}
|
|
950
|
+
onsetlive={() => handleSetLiveClickFromMenu(overlay.widgetId, option.id)}
|
|
951
|
+
/>
|
|
952
|
+
{/each}
|
|
953
|
+
</div>
|
|
954
|
+
</div>
|
|
955
|
+
{/if}
|
|
883
956
|
</div>
|
|
884
957
|
{/each}
|
|
885
958
|
</div>
|
|
@@ -95,6 +95,8 @@ interface Props {
|
|
|
95
95
|
}
|
|
96
96
|
declare const WidgetShapeLivePreviews: import("svelte").Component<Props, {
|
|
97
97
|
requestReload: (widgetId: string) => void;
|
|
98
|
+
cancelReloads: (widgetId?: string) => void;
|
|
99
|
+
setSelectedVersion: (widgetId: string, versionId: string | null) => void;
|
|
98
100
|
sync: () => void;
|
|
99
101
|
}, "">;
|
|
100
102
|
type WidgetShapeLivePreviews = ReturnType<typeof WidgetShapeLivePreviews>;
|
|
@@ -24,7 +24,7 @@ export interface CodeGenerationStatus {
|
|
|
24
24
|
error: string | null;
|
|
25
25
|
timestamp: string | null;
|
|
26
26
|
}
|
|
27
|
-
/** Agent harness repair lines pushed during codegen (
|
|
27
|
+
/** Agent harness repair lines pushed during codegen (codegen:output). */
|
|
28
28
|
export interface AgentHarnessOutput {
|
|
29
29
|
output: string;
|
|
30
30
|
timestamp: string;
|
|
@@ -222,7 +222,7 @@ function setupSocketListeners(sock) {
|
|
|
222
222
|
// =========================================================================
|
|
223
223
|
// Dynamic Worker Preview
|
|
224
224
|
// =========================================================================
|
|
225
|
-
sock.on('
|
|
225
|
+
sock.on('codegen:output', (data) => {
|
|
226
226
|
const output = typeof data?.output === 'string' ? data.output.trim() : '';
|
|
227
227
|
if (!output)
|
|
228
228
|
return;
|
|
@@ -29,7 +29,7 @@ let reported = false;
|
|
|
29
29
|
let lastStatus = '';
|
|
30
30
|
let breadcrumbs = [];
|
|
31
31
|
const PING_MS = 500;
|
|
32
|
-
const STALL_MS =
|
|
32
|
+
const STALL_MS = 1500;
|
|
33
33
|
|
|
34
34
|
function describeBreadcrumbs() {
|
|
35
35
|
if (breadcrumbs.length === 0) return 'no instrumented task over 200ms — the block is somewhere not yet measured';
|
|
@@ -45,12 +45,16 @@ setInterval(() => {
|
|
|
45
45
|
if (!freezeStartedAt) freezeStartedAt = now - STALL_MS;
|
|
46
46
|
if (!reported) {
|
|
47
47
|
reported = true;
|
|
48
|
-
|
|
49
|
-
'ms. Last status: "' + (lastStatus || '—') + '"'
|
|
48
|
+
const msg = '[FreezeReport] Main thread unresponsive ' + Math.round(now - freezeStartedAt) +
|
|
49
|
+
'ms. Last status: "' + (lastStatus || '—') + '". Select the top page in the console frame picker (not the widget iframe).';
|
|
50
|
+
console.warn(msg);
|
|
51
|
+
postMessage({ type: 'freeze-report', phase: 'start', message: msg });
|
|
50
52
|
}
|
|
51
53
|
} else if (freezeStartedAt) {
|
|
52
|
-
|
|
53
|
-
'ms. Last status: "' + (lastStatus || '—') + '". Blocking work: ' + describeBreadcrumbs()
|
|
54
|
+
const msg = '[FreezeReport] Main thread recovered after ' + Math.round(now - freezeStartedAt) +
|
|
55
|
+
'ms. Last status: "' + (lastStatus || '—') + '". Blocking work: ' + describeBreadcrumbs();
|
|
56
|
+
console.warn(msg);
|
|
57
|
+
postMessage({ type: 'freeze-report', phase: 'recovery', message: msg });
|
|
54
58
|
freezeStartedAt = null;
|
|
55
59
|
reported = false;
|
|
56
60
|
}
|
|
@@ -128,11 +132,38 @@ export function installFreezeWatchdog() {
|
|
|
128
132
|
URL.revokeObjectURL(url);
|
|
129
133
|
let lastStatus = '';
|
|
130
134
|
let pendingBreadcrumbs = [];
|
|
135
|
+
let lastLongTaskSentAt = performance.now();
|
|
136
|
+
function longTasksSince(since) {
|
|
137
|
+
if (typeof performance === 'undefined' || typeof performance.getEntriesByType !== 'function')
|
|
138
|
+
return [];
|
|
139
|
+
const found = [];
|
|
140
|
+
for (const entry of performance.getEntriesByType('longtask')) {
|
|
141
|
+
const endedAt = entry.startTime + entry.duration;
|
|
142
|
+
if (endedAt < since || entry.duration < BREADCRUMB_MIN_MS)
|
|
143
|
+
continue;
|
|
144
|
+
const attribution = entry.attribution?.[0];
|
|
145
|
+
const source = attribution?.containerSrc || attribution?.containerName || attribution?.containerType;
|
|
146
|
+
found.push({ label: 'longtask', duration: entry.duration, source });
|
|
147
|
+
}
|
|
148
|
+
return found;
|
|
149
|
+
}
|
|
131
150
|
worker.onmessage = (event) => {
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
const data = event.data;
|
|
152
|
+
if (data?.type === 'ping') {
|
|
153
|
+
const breadcrumbs = pendingBreadcrumbs.concat(longTasksSince(lastLongTaskSentAt));
|
|
154
|
+
lastLongTaskSentAt = performance.now();
|
|
155
|
+
worker.postMessage({ type: 'pong', status: lastStatus, breadcrumbs });
|
|
134
156
|
pendingBreadcrumbs = [];
|
|
135
157
|
}
|
|
158
|
+
else if (data?.type === 'freeze-report' && data.message) {
|
|
159
|
+
console.warn(data.message);
|
|
160
|
+
try {
|
|
161
|
+
window.top?.console.warn(data.message);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
/* cross-origin embed */
|
|
165
|
+
}
|
|
166
|
+
}
|
|
136
167
|
};
|
|
137
168
|
let stopLongTaskObserver = null;
|
|
138
169
|
const watchdog = {
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@widgetic/creator",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.69",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@sveltejs/kit": "^2.20.0",
|
|
7
7
|
"@widgetic/api-sdk": "^1.0.10",
|
|
8
8
|
"@widgetic/cache-layer": "^0.1.2",
|
|
9
9
|
"@widgetic/canvas": "^0.5.4",
|
|
10
|
-
"@widgetic/chat": "^0.1.
|
|
10
|
+
"@widgetic/chat": "^0.1.13",
|
|
11
11
|
"@widgetic/design-system": "^0.5.8",
|
|
12
12
|
"@widgetic/editor": "^4.0.1",
|
|
13
13
|
"@widgetic/file-browser": "^0.5.17",
|
|
@@ -43,9 +43,8 @@
|
|
|
43
43
|
"dev": "npm run stop-dev && npm run dev-sv",
|
|
44
44
|
"predev-sv": "node scripts/suppress-postcss-from-warning.js",
|
|
45
45
|
"dev-sv": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") VITE_BUILD_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo local) NODE_ENV=development vite dev --port 5174",
|
|
46
|
-
"lps": "volta run npm link --save @widgetic/design-system && volta run npm link --save @widgetic/api-sdk && volta run npm link --save @widgetic/canvas",
|
|
46
|
+
"lps": "volta run npm link --save @widgetic/design-system && volta run npm link --save @widgetic/api-sdk && volta run npm link --save @widgetic/canvas && volta run npm link --save @widgetic/chat",
|
|
47
47
|
"llinks": "ls -la node_modules/@widgetic",
|
|
48
|
-
"ldt": "volta run npm link --save @widgetic/dev-tools",
|
|
49
48
|
"clean-npm-cache": "npm cache clean --force",
|
|
50
49
|
"clean-svelte-cache": "rm -rf .svelte-kit && npx svelte-kit sync",
|
|
51
50
|
"clean-vite-cache": "rm -rf node_modules/.vite",
|
|
@@ -72,7 +71,7 @@
|
|
|
72
71
|
"@widgetic/api-sdk": "^1.0.10",
|
|
73
72
|
"@widgetic/cache-layer": "^0.1.2",
|
|
74
73
|
"@widgetic/canvas": "^0.5.4",
|
|
75
|
-
"@widgetic/chat": "^0.1.
|
|
74
|
+
"@widgetic/chat": "^0.1.13",
|
|
76
75
|
"@widgetic/design-system": "^0.5.8",
|
|
77
76
|
"@widgetic/dev-tools": "^0.2.0",
|
|
78
77
|
"@widgetic/editor": "^4.0.1",
|