@widgetic/creator 0.3.63 → 0.3.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CreatorApp.svelte +53 -5
- package/dist/components/WidgetDetails.svelte +18 -2
- package/dist/components/WidgetDetails.svelte.d.ts +2 -0
- package/dist/components/WidgetShapeLivePreviews.svelte +13 -0
- package/dist/components/WidgetShapeLivePreviews.svelte.d.ts +1 -0
- package/dist/stores/websocketStore.js +26 -4
- package/package.json +1 -1
package/dist/CreatorApp.svelte
CHANGED
|
@@ -494,7 +494,10 @@
|
|
|
494
494
|
let WidgeticCanvas: any;
|
|
495
495
|
let canvasRef: any;
|
|
496
496
|
/** HTML iframe overlays over Fabric WidgetShapes (W3). */
|
|
497
|
-
let widgetShapeLivePreviewsRef: {
|
|
497
|
+
let widgetShapeLivePreviewsRef: {
|
|
498
|
+
sync?: () => void;
|
|
499
|
+
requestReload?: (widgetId: string) => void;
|
|
500
|
+
} | null = null;
|
|
498
501
|
/** Selected WidgetShape id for chrome highlight (from Fabric selection). */
|
|
499
502
|
let canvasSelectedWidgetShapeId: string | null = null;
|
|
500
503
|
let widgetDetailsModulePromise: Promise<any> | null = null;
|
|
@@ -8230,6 +8233,11 @@ Requirements:
|
|
|
8230
8233
|
const responseData = response?.data || response;
|
|
8231
8234
|
const operationId = responseData?.operationId;
|
|
8232
8235
|
|
|
8236
|
+
// The Worker serves rebuilt code at the same preview URL, so the panel
|
|
8237
|
+
// must know when the current generation started — hasLiveWorkerPreview()
|
|
8238
|
+
// only counts frames loaded after this moment as "fresh".
|
|
8239
|
+
getWidgetDetailsRef(generationWidgetId)?.markCodegenStarted?.();
|
|
8240
|
+
|
|
8233
8241
|
if (!operationId) {
|
|
8234
8242
|
patchCodegenUiState(generationWidgetId, {
|
|
8235
8243
|
generateCodeStatus: '❌ Code generation started, but no operation ID was returned.',
|
|
@@ -8504,6 +8512,10 @@ Requirements:
|
|
|
8504
8512
|
[generationWidgetId]: 'preview',
|
|
8505
8513
|
};
|
|
8506
8514
|
syncWidgetShapeLivePreviews();
|
|
8515
|
+
// The Worker serves the rebuilt code at the SAME /preview URL — sync()
|
|
8516
|
+
// only reloads when src changes, so force a cache-busted iframe reload
|
|
8517
|
+
// on the canvas shape overlay or it keeps showing the previous version.
|
|
8518
|
+
widgetShapeLivePreviewsRef?.requestReload?.(generationWidgetId);
|
|
8507
8519
|
panelDisplayRevision += 1;
|
|
8508
8520
|
if (!completedPreviewBuildErr) {
|
|
8509
8521
|
await applyDesignSchemaDefaultsToPreview(generationWidgetId);
|
|
@@ -9318,10 +9330,43 @@ Requirements:
|
|
|
9318
9330
|
|
|
9319
9331
|
function pushCodegenProgressToUi(op: OperationStatus, widgetId: string | null | undefined) {
|
|
9320
9332
|
const line = formatCodegenProgressStatus(op);
|
|
9333
|
+
// SSE progress frames can arrive many times per second while the LLM
|
|
9334
|
+
// streams. Each one re-renders every open panel — coalesce them.
|
|
9321
9335
|
if (widgetId) {
|
|
9322
|
-
|
|
9323
|
-
}
|
|
9324
|
-
|
|
9336
|
+
scheduleCodegenStatusPush(widgetId, line);
|
|
9337
|
+
}
|
|
9338
|
+
scheduleCodegenChatStatusPush(widgetId, line);
|
|
9339
|
+
}
|
|
9340
|
+
|
|
9341
|
+
const CODEGEN_STATUS_THROTTLE_MS = 400;
|
|
9342
|
+
const codegenStatusTimers: Record<string, ReturnType<typeof setTimeout>> = {};
|
|
9343
|
+
const codegenStatusPendingLine: Record<string, string> = {};
|
|
9344
|
+
const codegenChatStatusTimers: Record<string, ReturnType<typeof setTimeout>> = {};
|
|
9345
|
+
const codegenChatStatusPendingLine: Record<string, string> = {};
|
|
9346
|
+
|
|
9347
|
+
/** At most one panel status write per widget per interval; trailing line always lands. */
|
|
9348
|
+
function scheduleCodegenStatusPush(widgetId: string, line: string): void {
|
|
9349
|
+
codegenStatusPendingLine[widgetId] = line;
|
|
9350
|
+
if (codegenStatusTimers[widgetId]) return;
|
|
9351
|
+
codegenStatusTimers[widgetId] = setTimeout(() => {
|
|
9352
|
+
delete codegenStatusTimers[widgetId];
|
|
9353
|
+
const pending = codegenStatusPendingLine[widgetId];
|
|
9354
|
+
delete codegenStatusPendingLine[widgetId];
|
|
9355
|
+
if (pending) patchCodegenUiState(widgetId, { generateCodeStatus: pending });
|
|
9356
|
+
}, CODEGEN_STATUS_THROTTLE_MS);
|
|
9357
|
+
}
|
|
9358
|
+
|
|
9359
|
+
/** Same throttle for the in-chat status bubble (updateLatestCodegenStatusMessage). */
|
|
9360
|
+
function scheduleCodegenChatStatusPush(widgetId: string | null | undefined, line: string): void {
|
|
9361
|
+
const key = widgetId || '_focused';
|
|
9362
|
+
codegenChatStatusPendingLine[key] = line;
|
|
9363
|
+
if (codegenChatStatusTimers[key]) return;
|
|
9364
|
+
codegenChatStatusTimers[key] = setTimeout(() => {
|
|
9365
|
+
delete codegenChatStatusTimers[key];
|
|
9366
|
+
const pending = codegenChatStatusPendingLine[key];
|
|
9367
|
+
delete codegenChatStatusPendingLine[key];
|
|
9368
|
+
if (pending) getChatMethodsForWidget(widgetId)?.updateLatestCodegenStatusMessage?.(pending);
|
|
9369
|
+
}, CODEGEN_STATUS_THROTTLE_MS);
|
|
9325
9370
|
}
|
|
9326
9371
|
|
|
9327
9372
|
function patchCodegenUiState(
|
|
@@ -14755,7 +14800,10 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
14755
14800
|
{@const shapeSource = shapePreviewSourceByWidgetId[panelWidgetId] || defaultVersionSource}
|
|
14756
14801
|
{@const viewingVersion = shapeSource && shapeSource.startsWith('v:')
|
|
14757
14802
|
? parseInt(shapeSource.slice(2), 10)
|
|
14758
|
-
|
|
14803
|
+
// A 'preview' source (Worker HEAD, unpublished) is not a published
|
|
14804
|
+
// version — falling back to panelPublishedVersion produced `vnull`
|
|
14805
|
+
// right after generation. The newest draft IS this HEAD's version.
|
|
14806
|
+
: (newestDraft ? newestDraft.version : panelPublishedVersion)}
|
|
14759
14807
|
{@const viewingIsLive = Number(viewingVersion) === Number(panelPublishedVersion)}
|
|
14760
14808
|
<!--
|
|
14761
14809
|
A version is a Draft until it is published. The history is one
|
|
@@ -801,7 +801,20 @@
|
|
|
801
801
|
// console.log('[WidgetDetails] Codegen preview marked fresh — skipping GitLab rebuilds for 30s');
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
-
/**
|
|
804
|
+
/** Timestamp of generation start — set by the parent when the operation begins. */
|
|
805
|
+
let codegenStartedAt: number | null = null;
|
|
806
|
+
/** Parent calls this when a generation starts for THIS widget's panel. */
|
|
807
|
+
export function markCodegenStarted(): void {
|
|
808
|
+
codegenStartedAt = Date.now();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* True when the iframe is already showing this widget's Worker preview AND was
|
|
813
|
+
* reloaded after the current generation started. The Worker serves new code at
|
|
814
|
+
* the same `/preview/{id}` URL, so URL equality alone cannot tell a stale frame
|
|
815
|
+
* (pre-codegen) from a fresh one — that mismatch left the old version on screen
|
|
816
|
+
* after generation.
|
|
817
|
+
*/
|
|
805
818
|
export function hasLiveWorkerPreview(): boolean {
|
|
806
819
|
if (!selectedWidgetId) return false;
|
|
807
820
|
if (!iframeSrc || iframeSrc === 'about:blank') return false;
|
|
@@ -809,7 +822,10 @@
|
|
|
809
822
|
const workerUrl = dynamicWorkerPreviewUrl || buildWorkerPreviewUrl(selectedWidgetId);
|
|
810
823
|
const currentBase = normalizePreviewBaseUrl(iframeSrc);
|
|
811
824
|
const workerBase = normalizePreviewBaseUrl(toPreviewFetchUrl(workerUrl));
|
|
812
|
-
|
|
825
|
+
if (!currentBase || !workerBase || currentBase !== workerBase) return false;
|
|
826
|
+
// During/after a generation, only a frame (re)loaded after it started counts.
|
|
827
|
+
if (codegenStartedAt !== null && lastPreviewIframeLoadAt < codegenStartedAt) return false;
|
|
828
|
+
return true;
|
|
813
829
|
}
|
|
814
830
|
|
|
815
831
|
// Preview resize
|
|
@@ -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
|
+
markCodegenStarted?: () => void;
|
|
111
112
|
hasLiveWorkerPreview?: () => boolean;
|
|
112
113
|
getLatestRuntimeLogs?: (maxLines?: number) => string[];
|
|
113
114
|
resumeDeferredPreviewLoad?: () => Promise<void>;
|
|
@@ -204,6 +205,7 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
204
205
|
showPublishedArtifact: (url: string | null) => void;
|
|
205
206
|
finishCompositionHydration: () => void;
|
|
206
207
|
markCodegenPreviewFresh: () => void;
|
|
208
|
+
markCodegenStarted: () => void;
|
|
207
209
|
hasLiveWorkerPreview: () => boolean;
|
|
208
210
|
getLatestRuntimeLogs: (maxLines?: number) => string[];
|
|
209
211
|
resumeDeferredPreviewLoad: () => Promise<void>;
|
|
@@ -171,6 +171,19 @@
|
|
|
171
171
|
}
|
|
172
172
|
pendingRefreshTokens.delete(widgetId);
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parent-driven forced reload for widgets whose Worker preview content changed
|
|
177
|
+
* at the same URL (codegen rebuild). `sync()` only reloads when `src` changes,
|
|
178
|
+
* so without a cache-bust token the overlay keeps showing pre-codegen content.
|
|
179
|
+
* Bumps the reload token → `{#key}` remounts the iframe with a fresh `_t`.
|
|
180
|
+
*/
|
|
181
|
+
export function requestReload(widgetId: string): void {
|
|
182
|
+
const token = Math.max(Date.now(), (reloadTokens[widgetId] || 0) + 1);
|
|
183
|
+
reloadTokens = { ...reloadTokens, [widgetId]: token };
|
|
184
|
+
iframeLoadedByWidgetId = { ...iframeLoadedByWidgetId, [widgetId]: false };
|
|
185
|
+
}
|
|
186
|
+
|
|
174
187
|
/** Which shape chrome version menu is open. */
|
|
175
188
|
let openVersionMenuWidgetId = $state<string | null>(null);
|
|
176
189
|
/** Hide Fabric placeholder flash — show loader until iframe fires load. */
|
|
@@ -90,6 +90,7 @@ interface Props {
|
|
|
90
90
|
resolvePublishDisabledReason?: ((widgetId: string, version: number) => string | null) | null;
|
|
91
91
|
}
|
|
92
92
|
declare const WidgetShapeLivePreviews: import("svelte").Component<Props, {
|
|
93
|
+
requestReload: (widgetId: string) => void;
|
|
93
94
|
sync: () => void;
|
|
94
95
|
}, "">;
|
|
95
96
|
type WidgetShapeLivePreviews = ReturnType<typeof WidgetShapeLivePreviews>;
|
|
@@ -39,6 +39,30 @@ export const codeGenerationStatus = writable({
|
|
|
39
39
|
timestamp: null
|
|
40
40
|
});
|
|
41
41
|
export const agentHarnessOutput = writable(null);
|
|
42
|
+
/**
|
|
43
|
+
* Throttle for high-frequency agent harness frames. The stream used to emit one
|
|
44
|
+
* WS event per LLM token (plus one SSE progress frame every 10 tokens), and each
|
|
45
|
+
* frame re-rendered every open panel — with `structuredClone` on schemas per
|
|
46
|
+
* render. That saturated the browser main thread and froze the whole site during
|
|
47
|
+
* generation. Frames now coalesce into one store write per interval; the final
|
|
48
|
+
* frame always flushes so the last line is never lost.
|
|
49
|
+
*/
|
|
50
|
+
const AGENT_HARNESS_FLUSH_MS = 300;
|
|
51
|
+
let agentHarnessPending = null;
|
|
52
|
+
let agentHarnessFlushTimer = null;
|
|
53
|
+
function flushAgentHarnessOutput() {
|
|
54
|
+
agentHarnessFlushTimer = null;
|
|
55
|
+
if (!agentHarnessPending)
|
|
56
|
+
return;
|
|
57
|
+
agentHarnessOutput.set(agentHarnessPending);
|
|
58
|
+
agentHarnessPending = null;
|
|
59
|
+
}
|
|
60
|
+
function pushAgentHarnessOutput(output) {
|
|
61
|
+
agentHarnessPending = { output, timestamp: new Date().toISOString() };
|
|
62
|
+
if (!agentHarnessFlushTimer) {
|
|
63
|
+
agentHarnessFlushTimer = setTimeout(flushAgentHarnessOutput, AGENT_HARNESS_FLUSH_MS);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
42
66
|
// =============================================================================
|
|
43
67
|
// Socket.IO Connection
|
|
44
68
|
// =============================================================================
|
|
@@ -204,10 +228,8 @@ function setupSocketListeners(sock) {
|
|
|
204
228
|
return;
|
|
205
229
|
if (showLogs)
|
|
206
230
|
console.log('[WsStore] Agent harness output:', output.slice(0, 120));
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
timestamp: new Date().toISOString()
|
|
210
|
-
});
|
|
231
|
+
// Coalesced write — see AGENT_HARNESS_FLUSH_MS above.
|
|
232
|
+
pushAgentHarnessOutput(output);
|
|
211
233
|
});
|
|
212
234
|
sock.on('dynamic_worker_preview_ready', (data) => {
|
|
213
235
|
if (showLogs)
|