@widgetic/creator 0.3.69 → 0.3.71
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
CHANGED
|
@@ -1194,7 +1194,12 @@
|
|
|
1194
1194
|
// console.log('[Chat] Codegen failure bubble:', content.slice(0, 160));
|
|
1195
1195
|
}
|
|
1196
1196
|
|
|
1197
|
-
/**
|
|
1197
|
+
/** A retry of a prompt that already produced a version opens a new message. A failed retry stays on the same bubble. */
|
|
1198
|
+
function messageTextHasCommit(content: string | null | undefined): boolean {
|
|
1199
|
+
return /(?:Commit:?\s+|Code saved\s*\()[a-f0-9]{7,40}/i.test(String(content || ''));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/** Retry failed codegen without duplicating the user bubble. A successful prompt forks a new message so each version can show "You are here". */
|
|
1198
1203
|
async function handleChatRetryMessage(content: string, sourceWidgetId?: string) {
|
|
1199
1204
|
const widgetId = sourceWidgetId ?? selectedWidgetId;
|
|
1200
1205
|
if (!widgetId) return;
|
|
@@ -1212,44 +1217,63 @@
|
|
|
1212
1217
|
const draftUploadIds = panelChat?.getAttachmentUploadIds?.() ?? [];
|
|
1213
1218
|
const draftImageUrls = panelChat?.getAttachmentDataUrls?.() ?? [];
|
|
1214
1219
|
|
|
1215
|
-
//
|
|
1216
|
-
//
|
|
1220
|
+
// A prompt that already has a commit must not reuse that bubble: Version N and
|
|
1221
|
+
// Version N+1 would share one message, and "You are here" could not mark the older one.
|
|
1222
|
+
// The standing sketch is attached on the server for the model. It is not copied onto the chat row.
|
|
1223
|
+
let forkUserMessage = false;
|
|
1217
1224
|
let retryFileAttachments: Array<{ url: string; file_type: string; file_name: string | null; file_size: number | null }> = [];
|
|
1218
1225
|
if (draftUploadIds.length === 0 && draftImageUrls.length === 0) {
|
|
1219
1226
|
try {
|
|
1220
1227
|
const panelState = getPanelState(widgetId);
|
|
1221
1228
|
const convId = panelChat?.getConversationId?.() ?? panelState.currentChatConversationId ?? null;
|
|
1222
1229
|
if (convId) {
|
|
1223
|
-
const convMessages = await handleLoadMessages(convId, widgetId);
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1230
|
+
const convMessages = await handleLoadMessages(convId, widgetId) || [];
|
|
1231
|
+
const needle = content.trim();
|
|
1232
|
+
let matchIndex = -1;
|
|
1233
|
+
for (let i = convMessages.length - 1; i >= 0; i--) {
|
|
1234
|
+
const message = convMessages[i];
|
|
1235
|
+
const type = message.messageType || message.message_type;
|
|
1236
|
+
const text = String(message.messageContent || message.message_content || '').trim();
|
|
1237
|
+
if (type === 'user' && text === needle) {
|
|
1238
|
+
matchIndex = i;
|
|
1239
|
+
break;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (matchIndex >= 0) {
|
|
1243
|
+
forkUserMessage = convMessages.slice(matchIndex + 1).some((message) =>
|
|
1244
|
+
(message.messageType || message.message_type) === 'assistant'
|
|
1245
|
+
&& messageTextHasCommit(message.messageContent || message.message_content),
|
|
1246
|
+
);
|
|
1247
|
+
if (!forkUserMessage) {
|
|
1248
|
+
retryFileAttachments = (convMessages[matchIndex].attachments || [])
|
|
1249
|
+
.filter((attachment) => attachment.url && /^https?:\/\//i.test(attachment.url))
|
|
1250
|
+
.filter((attachment) => !/design-screenshot/i.test(attachment.file_name || attachment.fileName || ''))
|
|
1251
|
+
.map((attachment) => ({
|
|
1252
|
+
url: attachment.url,
|
|
1253
|
+
file_type: attachment.file_type || attachment.fileType || 'application/octet-stream',
|
|
1254
|
+
file_name: attachment.file_name || attachment.fileName || null,
|
|
1255
|
+
file_size: attachment.file_size || attachment.fileSize || null,
|
|
1256
|
+
}));
|
|
1257
|
+
}
|
|
1238
1258
|
}
|
|
1239
1259
|
}
|
|
1240
1260
|
} catch (retryAttErr) {
|
|
1241
|
-
console.warn('[Chat→Retry] Could not load
|
|
1261
|
+
console.warn('[Chat→Retry] Could not load the prompt turn for retry:', retryAttErr);
|
|
1242
1262
|
}
|
|
1243
1263
|
}
|
|
1264
|
+
if (forkUserMessage) {
|
|
1265
|
+
panelChat?.addUserMessage?.(content);
|
|
1266
|
+
}
|
|
1244
1267
|
|
|
1245
1268
|
await generateCode({
|
|
1246
1269
|
prompt: content,
|
|
1247
1270
|
fromChat: true,
|
|
1248
1271
|
widgetId,
|
|
1249
1272
|
forceRecreateRepo: true,
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1273
|
+
forkUserMessage,
|
|
1274
|
+
attachmentIds: forkUserMessage ? undefined : (draftUploadIds.length > 0 ? draftUploadIds : undefined),
|
|
1275
|
+
promptImagesOverride: forkUserMessage ? [] : (draftImageUrls.length > 0 ? draftImageUrls : undefined),
|
|
1276
|
+
persistFileAttachments: !forkUserMessage && retryFileAttachments.length > 0 ? retryFileAttachments : undefined,
|
|
1253
1277
|
});
|
|
1254
1278
|
}
|
|
1255
1279
|
|
|
@@ -1532,6 +1556,7 @@
|
|
|
1532
1556
|
file_size?: number | null;
|
|
1533
1557
|
display_order?: number;
|
|
1534
1558
|
}>,
|
|
1559
|
+
forkUserMessage = false,
|
|
1535
1560
|
): Promise<void> {
|
|
1536
1561
|
if (!conversationsClient || !messagesClient) return;
|
|
1537
1562
|
try {
|
|
@@ -1552,6 +1577,8 @@
|
|
|
1552
1577
|
convId = conv?.id;
|
|
1553
1578
|
}
|
|
1554
1579
|
if (!convId) return;
|
|
1580
|
+
// The new bubble was already saved by addUserMessage. Do not patch the previous turn with the sketch.
|
|
1581
|
+
if (forkUserMessage) return;
|
|
1555
1582
|
|
|
1556
1583
|
const persistImages = uniquePersistImageUrls([
|
|
1557
1584
|
...(imageUrls || []),
|
|
@@ -8288,6 +8315,8 @@ Requirements:
|
|
|
8288
8315
|
skipIntentGate?: boolean;
|
|
8289
8316
|
/** The tapped chip's structured intent, declared so the server measures the chip conversion without matching copy. */
|
|
8290
8317
|
chipIntent?: ClarificationSuggestion['intent'];
|
|
8318
|
+
/** Successful retry: a new user row already exists. Do not attach images to the previous turn. */
|
|
8319
|
+
forkUserMessage?: boolean;
|
|
8291
8320
|
/** Chat files (PDF, video, images) to store on the user message before codegen. */
|
|
8292
8321
|
persistFileAttachments?: Array<{
|
|
8293
8322
|
url?: string;
|
|
@@ -8545,8 +8574,9 @@ Requirements:
|
|
|
8545
8574
|
}
|
|
8546
8575
|
|
|
8547
8576
|
// Prepare images for the request (if any) — prefer IDs/URLs captured before clearDraft
|
|
8548
|
-
let panelPromptImages =
|
|
8549
|
-
|
|
8577
|
+
let panelPromptImages = options.forkUserMessage
|
|
8578
|
+
? []
|
|
8579
|
+
: options.promptImagesOverride && options.promptImagesOverride.length > 0
|
|
8550
8580
|
? options.promptImagesOverride
|
|
8551
8581
|
: generationWidgetId === focusedPanelWidgetId
|
|
8552
8582
|
? promptImages
|
|
@@ -8556,12 +8586,14 @@ Requirements:
|
|
|
8556
8586
|
panelPromptImages = panelPromptImages.filter(
|
|
8557
8587
|
(url) => typeof url === 'string' && (url.startsWith('data:image/') || /^https?:\/\//i.test(url)) && url.length > 20,
|
|
8558
8588
|
);
|
|
8559
|
-
const chatAttachmentUrls = panelChat?.getAttachmentDataUrls?.() ?? [];
|
|
8560
|
-
const chatUploadIds =
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8589
|
+
const chatAttachmentUrls = options.forkUserMessage ? [] : (panelChat?.getAttachmentDataUrls?.() ?? []);
|
|
8590
|
+
const chatUploadIds = options.forkUserMessage
|
|
8591
|
+
? []
|
|
8592
|
+
: [
|
|
8593
|
+
...(options.attachmentIds || []),
|
|
8594
|
+
...(panelChat?.getAttachmentUploadIds?.() ?? []),
|
|
8595
|
+
].filter((id, index, arr) => id && arr.indexOf(id) === index);
|
|
8596
|
+
if (!options.forkUserMessage && chatAttachmentUrls.length > 0 && !(options.promptImagesOverride?.length)) {
|
|
8565
8597
|
const fromChat = chatAttachmentUrls.filter(
|
|
8566
8598
|
(url) => typeof url === 'string' && (url.startsWith('data:image/') || /^https?:\/\//i.test(url)) && url.length > 20,
|
|
8567
8599
|
);
|
|
@@ -8633,8 +8665,9 @@ Requirements:
|
|
|
8633
8665
|
await ensureCodegenUserMessagePersisted(
|
|
8634
8666
|
generationWidgetId,
|
|
8635
8667
|
prompt,
|
|
8636
|
-
persistImageUrls,
|
|
8637
|
-
options.persistFileAttachments,
|
|
8668
|
+
options.forkUserMessage ? [] : persistImageUrls,
|
|
8669
|
+
options.forkUserMessage ? undefined : options.persistFileAttachments,
|
|
8670
|
+
!!options.forkUserMessage,
|
|
8638
8671
|
);
|
|
8639
8672
|
|
|
8640
8673
|
// Clear unsent draft/attachments once payload is captured — prevents restore on widget return
|
|
@@ -8885,14 +8918,63 @@ Requirements:
|
|
|
8885
8918
|
if (generationWidgetId) {
|
|
8886
8919
|
const { [generationWidgetId]: _resolved, ...restResolved } = shapeHistoryResolvedByWidgetId;
|
|
8887
8920
|
shapeHistoryResolvedByWidgetId = restResolved;
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8921
|
+
const alignWidgetId = generationWidgetId;
|
|
8922
|
+
const alignCommitSha = completedCommitSha;
|
|
8923
|
+
const alignGeneratedVersion = (attempt: number) => {
|
|
8924
|
+
void loadPublishHistoryForPanel(alignWidgetId, { silent: true }).then(() => {
|
|
8925
|
+
const versionId = findVersionIdMatchingCommit(alignWidgetId, alignCommitSha);
|
|
8926
|
+
const matchedByCommit = !!alignCommitSha
|
|
8927
|
+
&& !!versionId
|
|
8928
|
+
&& getShapePublishHistory(alignWidgetId).some((row) =>
|
|
8929
|
+
versionId === `v:${Number(row.version)}`
|
|
8930
|
+
&& commitsMatch(row?.commitSha ?? row?.commit_sha, alignCommitSha),
|
|
8931
|
+
);
|
|
8932
|
+
if (!matchedByCommit && alignCommitSha && attempt < 4) {
|
|
8933
|
+
setTimeout(() => alignGeneratedVersion(attempt + 1), 1500);
|
|
8934
|
+
return;
|
|
8893
8935
|
}
|
|
8936
|
+
if (!versionId) return;
|
|
8937
|
+
if (restoredViewingVersionByWidgetId[alignWidgetId] != null) {
|
|
8938
|
+
const { [alignWidgetId]: _clearedRestore, ...restRestored } = restoredViewingVersionByWidgetId;
|
|
8939
|
+
restoredViewingVersionByWidgetId = restRestored;
|
|
8940
|
+
}
|
|
8941
|
+
const menuNow = dropdownVersionByWidgetId[alignWidgetId];
|
|
8942
|
+
const sourceNow = shapePreviewSourceByWidgetId[alignWidgetId];
|
|
8943
|
+
const selectedRow = getShapePublishHistory(alignWidgetId)
|
|
8944
|
+
.find((row) => `v:${Number(row.version)}` === menuNow);
|
|
8945
|
+
if (
|
|
8946
|
+
menuNow?.startsWith('v:')
|
|
8947
|
+
&& menuNow === sourceNow
|
|
8948
|
+
&& alignCommitSha
|
|
8949
|
+
&& !commitsMatch(selectedRow?.commitSha ?? selectedRow?.commit_sha, alignCommitSha)
|
|
8950
|
+
) {
|
|
8951
|
+
return;
|
|
8952
|
+
}
|
|
8953
|
+
selectVersionInDropdowns(alignWidgetId, versionId);
|
|
8954
|
+
shapePreviewSourceByWidgetId = {
|
|
8955
|
+
...shapePreviewSourceByWidgetId,
|
|
8956
|
+
[alignWidgetId]: versionId,
|
|
8957
|
+
};
|
|
8958
|
+
syncWidgetShapeLivePreviews();
|
|
8959
|
+
panelDisplayRevision += 1;
|
|
8960
|
+
if (versionUsesSharedHeadPreview(alignWidgetId, versionId)) {
|
|
8961
|
+
const headUrl = resolveWidgetShapeLivePreviewUrl(alignWidgetId);
|
|
8962
|
+
getWidgetDetailsRef(alignWidgetId)?.showLiveWorkerPreview?.(headUrl);
|
|
8963
|
+
widgetShapeLivePreviewsRef?.requestReload?.(alignWidgetId);
|
|
8964
|
+
} else {
|
|
8965
|
+
const versionNumber = Number(versionId.slice(2));
|
|
8966
|
+
if (Number.isFinite(versionNumber)) {
|
|
8967
|
+
void ensureWidgetShapeVersionPreview(alignWidgetId, versionNumber);
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
console.log('[VersionPreview] Aligned dropdown with generated commit', {
|
|
8971
|
+
widgetId: alignWidgetId.substring(0, 8),
|
|
8972
|
+
versionId,
|
|
8973
|
+
commit: alignCommitSha?.substring(0, 8) ?? null,
|
|
8974
|
+
});
|
|
8894
8975
|
});
|
|
8895
|
-
}
|
|
8976
|
+
};
|
|
8977
|
+
setTimeout(() => alignGeneratedVersion(0), 1200);
|
|
8896
8978
|
}
|
|
8897
8979
|
|
|
8898
8980
|
// Always reconcile GitLab HEAD after codegen (metadata.commitId can be missing/stale).
|
|
@@ -9053,10 +9135,8 @@ Requirements:
|
|
|
9053
9135
|
await activePanelRef?.refreshPreview?.({ withRetry: true, skipIfAlreadyShowing: true });
|
|
9054
9136
|
}
|
|
9055
9137
|
activePanelRef?.setPreviewBuildError(null);
|
|
9056
|
-
// TODO(tomorrow): Dupa o generare noua, sincronizarea versiunii in dropdown si istoric se desincronizeaza.
|
|
9057
|
-
// Trebuie armonizata selectia automata a noii versiuni draft (vN) create in istoric,
|
|
9058
|
-
// actualizat trigger-ul pe canvas si in chat la noul numar de versiune si pastrata coerenta intre preview si dropdown.
|
|
9059
9138
|
// Show Worker preview on the canvas shape (not the previous Live CDN artifact).
|
|
9139
|
+
// History alignment below moves the dropdown onto the commit this build belongs to.
|
|
9060
9140
|
shapePreviewSourceByWidgetId = {
|
|
9061
9141
|
...shapePreviewSourceByWidgetId,
|
|
9062
9142
|
[generationWidgetId]: 'preview',
|
|
@@ -10495,6 +10575,15 @@ Requirements:
|
|
|
10495
10575
|
function getPanelEditPreviewUrl(widgetId: string): string | null {
|
|
10496
10576
|
const ps = getPanelDisplayState(widgetId);
|
|
10497
10577
|
const source = shapePreviewSourceByWidgetId[widgetId];
|
|
10578
|
+
// Head draft and the post-codegen 'preview' source are the Worker build.
|
|
10579
|
+
// Falling through here used to hand the panel the last published CDN file
|
|
10580
|
+
// while the dropdown already said the new draft.
|
|
10581
|
+
if (source === 'preview' || (source && versionUsesSharedHeadPreview(widgetId, source))) {
|
|
10582
|
+
const hasCode = !!(ps.lastCommitId || loadCommitIdForWidget(widgetId));
|
|
10583
|
+
if (hasCode && source !== 'live') {
|
|
10584
|
+
return `${widgetBuilderPreviewBaseUrl()}/preview/${widgetId}`;
|
|
10585
|
+
}
|
|
10586
|
+
}
|
|
10498
10587
|
if (source && source.startsWith('v:')) {
|
|
10499
10588
|
const version = parseInt(source.slice(2), 10);
|
|
10500
10589
|
if (Number.isFinite(version)) {
|
|
@@ -10631,6 +10720,45 @@ Requirements:
|
|
|
10631
10720
|
return draftVersions.length > 0 ? Math.max(...draftVersions) : null;
|
|
10632
10721
|
}
|
|
10633
10722
|
|
|
10723
|
+
/** True when this draft row's commit is still the repo HEAD the shared Worker preview serves. */
|
|
10724
|
+
function draftCommitMatchesRepoHead(widgetId: string, record: { commitSha?: string | null; commit_sha?: string | null } | null | undefined): boolean {
|
|
10725
|
+
const draftCommitSha = normalizeCommitSha(record?.commitSha ?? record?.commit_sha ?? null);
|
|
10726
|
+
const headCommitSha = normalizeCommitSha(
|
|
10727
|
+
getPanelDisplayState(widgetId).lastCommitId ?? loadCommitIdForWidget(widgetId),
|
|
10728
|
+
);
|
|
10729
|
+
return !draftCommitSha || !headCommitSha || commitsMatch(draftCommitSha, headCommitSha);
|
|
10730
|
+
}
|
|
10731
|
+
|
|
10732
|
+
/**
|
|
10733
|
+
* Shared `/preview/{widgetId}` is only the selected row when that row is the
|
|
10734
|
+
* newest draft and its commit is still HEAD. Every other version needs its
|
|
10735
|
+
* own build — otherwise the dropdown says vN while the iframe shows HEAD.
|
|
10736
|
+
*/
|
|
10737
|
+
function versionUsesSharedHeadPreview(widgetId: string, versionId: string | null | undefined): boolean {
|
|
10738
|
+
if (!versionId || versionId === 'preview' || versionId === 'live') return versionId !== 'live';
|
|
10739
|
+
if (!versionId.startsWith('v:')) return false;
|
|
10740
|
+
const version = parseInt(versionId.slice(2), 10);
|
|
10741
|
+
if (!Number.isFinite(version)) return false;
|
|
10742
|
+
const record = getShapePublishHistory(widgetId).find((row) => Number(row.version) === version);
|
|
10743
|
+
if (record?.publishedAt ?? record?.published_at) return false;
|
|
10744
|
+
const newestDraft = getNewestDraftVersionId(widgetId);
|
|
10745
|
+
if (newestDraft == null || version !== newestDraft) return false;
|
|
10746
|
+
if (restoredViewingVersionByWidgetId[widgetId] != null) return false;
|
|
10747
|
+
return draftCommitMatchesRepoHead(widgetId, record);
|
|
10748
|
+
}
|
|
10749
|
+
|
|
10750
|
+
function findVersionIdMatchingCommit(widgetId: string, commitSha: string | null): string | null {
|
|
10751
|
+
const target = normalizeCommitSha(commitSha);
|
|
10752
|
+
const history = getShapePublishHistory(widgetId);
|
|
10753
|
+
if (target) {
|
|
10754
|
+
const match = history.find((row) => commitsMatch(row?.commitSha ?? row?.commit_sha, target));
|
|
10755
|
+
const version = Number(match?.version);
|
|
10756
|
+
if (Number.isFinite(version)) return `v:${version}`;
|
|
10757
|
+
}
|
|
10758
|
+
const newestDraft = getNewestDraftVersionId(widgetId);
|
|
10759
|
+
return newestDraft != null ? `v:${newestDraft}` : null;
|
|
10760
|
+
}
|
|
10761
|
+
|
|
10634
10762
|
function resolveWidgetShapeLivePreviewUrl(widgetId: string): string | null {
|
|
10635
10763
|
if (!widgetId) return null;
|
|
10636
10764
|
const source = shapePreviewSourceByWidgetId[widgetId] || 'live';
|
|
@@ -10665,11 +10793,15 @@ Requirements:
|
|
|
10665
10793
|
// `/preview/{widgetId}` artifact then serves the restored code under the
|
|
10666
10794
|
// newest version's number — ask for a commit-specific build instead.
|
|
10667
10795
|
const newestDraft = getNewestDraftVersionId(widgetId);
|
|
10668
|
-
const
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10796
|
+
const draftIsRepoHead = draftCommitMatchesRepoHead(widgetId, record);
|
|
10797
|
+
if (
|
|
10798
|
+
!isPublished
|
|
10799
|
+
&& newestDraft != null
|
|
10800
|
+
&& version === newestDraft
|
|
10801
|
+
&& draftIsRepoHead
|
|
10802
|
+
&& workerUrl
|
|
10803
|
+
&& restoredViewingVersionByWidgetId[widgetId] == null
|
|
10804
|
+
) {
|
|
10673
10805
|
return workerUrl;
|
|
10674
10806
|
}
|
|
10675
10807
|
return resolveWidgetShapeVersionPreviewState(widgetId)?.url ?? null;
|
|
@@ -10852,10 +10984,18 @@ Requirements:
|
|
|
10852
10984
|
const isPublished = !!(record?.publishedAt ?? record?.published_at);
|
|
10853
10985
|
const newestDraft = getNewestDraftVersionId(widgetId);
|
|
10854
10986
|
const workerUrl = resolveWidgetShapeLivePreviewUrl(widgetId);
|
|
10855
|
-
const viewingHeadDraft = !isPublished
|
|
10856
|
-
|
|
10987
|
+
const viewingHeadDraft = !isPublished
|
|
10988
|
+
&& newestDraft != null
|
|
10989
|
+
&& version === newestDraft
|
|
10990
|
+
&& draftCommitMatchesRepoHead(widgetId, record)
|
|
10991
|
+
&& !!workerUrl
|
|
10992
|
+
&& restoredViewingVersionByWidgetId[widgetId] == null;
|
|
10993
|
+
if (viewingHeadDraft && workerUrl) {
|
|
10857
10994
|
const detailsRef = getWidgetDetailsRef(widgetId);
|
|
10858
|
-
|
|
10995
|
+
// Same Worker URL as the previous draft: the shape iframe does not
|
|
10996
|
+
// remount unless the cache-bust token changes.
|
|
10997
|
+
detailsRef?.showLiveWorkerPreview?.(workerUrl);
|
|
10998
|
+
widgetShapeLivePreviewsRef?.requestReload?.(widgetId);
|
|
10859
10999
|
} else {
|
|
10860
11000
|
void ensureWidgetShapeVersionPreview(widgetId, version);
|
|
10861
11001
|
}
|
|
@@ -10992,6 +11132,7 @@ Requirements:
|
|
|
10992
11132
|
*/
|
|
10993
11133
|
let versionLabelsByWidgetId: Record<string, Record<string, string>> = {};
|
|
10994
11134
|
let versionPromptLabelsByWidgetId: Record<string, Record<string, string>> = {};
|
|
11135
|
+
let versionTimelineByWidgetId: Record<string, Array<{ version: number; sha: string; prompt: string }>> = {};
|
|
10995
11136
|
let viewingVersionByWidgetId: Record<string, number | null> = {};
|
|
10996
11137
|
$: {
|
|
10997
11138
|
void shapePreviewSourceByWidgetId;
|
|
@@ -11002,16 +11143,19 @@ Requirements:
|
|
|
11002
11143
|
const nextHeads: Record<string, string | null> = {};
|
|
11003
11144
|
const nextLabels: Record<string, Record<string, string>> = {};
|
|
11004
11145
|
const nextPrompts: Record<string, Record<string, string>> = {};
|
|
11146
|
+
const nextTimeline: Record<string, Array<{ version: number; sha: string; prompt: string }>> = {};
|
|
11005
11147
|
const nextViewing: Record<string, number | null> = {};
|
|
11006
11148
|
for (const widgetId of openWidgetPanelIds) {
|
|
11007
11149
|
nextHeads[widgetId] = resolveViewingCommitShaForPanel(widgetId);
|
|
11008
11150
|
nextLabels[widgetId] = buildVersionLabelsForPanel(widgetId);
|
|
11009
11151
|
nextPrompts[widgetId] = buildVersionPromptLabelsForPanel(widgetId);
|
|
11152
|
+
nextTimeline[widgetId] = buildVersionTimelineForPanel(widgetId);
|
|
11010
11153
|
nextViewing[widgetId] = viewingVersionNumberForPanel(widgetId);
|
|
11011
11154
|
}
|
|
11012
11155
|
headCommitShaByWidgetId = nextHeads;
|
|
11013
11156
|
versionLabelsByWidgetId = nextLabels;
|
|
11014
11157
|
versionPromptLabelsByWidgetId = nextPrompts;
|
|
11158
|
+
versionTimelineByWidgetId = nextTimeline;
|
|
11015
11159
|
viewingVersionByWidgetId = nextViewing;
|
|
11016
11160
|
}
|
|
11017
11161
|
|
|
@@ -11049,6 +11193,20 @@ Requirements:
|
|
|
11049
11193
|
return text || null;
|
|
11050
11194
|
}
|
|
11051
11195
|
|
|
11196
|
+
function buildVersionTimelineForPanel(widgetId: string): Array<{ version: number; sha: string; prompt: string }> {
|
|
11197
|
+
const ps = getPanelDisplayState(widgetId);
|
|
11198
|
+
const history = ps.publishHistory?.length ? ps.publishHistory : publishHistory;
|
|
11199
|
+
const timeline: Array<{ version: number; sha: string; prompt: string }> = [];
|
|
11200
|
+
for (const record of history || []) {
|
|
11201
|
+
if (record?.version == null) continue;
|
|
11202
|
+
const sha = normalizeCommitSha(record?.commitSha ?? record?.commit_sha);
|
|
11203
|
+
const prompt = versionPromptKey(record?.prompt);
|
|
11204
|
+
if (!sha || !prompt) continue;
|
|
11205
|
+
timeline.push({ version: Number(record.version), sha, prompt });
|
|
11206
|
+
}
|
|
11207
|
+
return timeline;
|
|
11208
|
+
}
|
|
11209
|
+
|
|
11052
11210
|
/** User prompt → "Version N", for turns whose commit line was never saved. */
|
|
11053
11211
|
function buildVersionPromptLabelsForPanel(widgetId: string): Record<string, string> {
|
|
11054
11212
|
const labels: Record<string, string> = {};
|
|
@@ -12521,15 +12679,24 @@ $: if ($dynamicWorkerPreviewStatus.widgetId && $dynamicWorkerPreviewStatus.previ
|
|
|
12521
12679
|
const wsEventKey = `${wsReadyWidgetId}:${$dynamicWorkerPreviewStatus.previewUrl}:${$dynamicWorkerPreviewStatus.timestamp ?? ''}`;
|
|
12522
12680
|
if (wsEventKey !== lastWsWorkerPreviewEventKey) {
|
|
12523
12681
|
lastWsWorkerPreviewEventKey = wsEventKey;
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
|
|
12529
|
-
|
|
12682
|
+
const selectedVersion = dropdownVersionByWidgetId[wsReadyWidgetId]
|
|
12683
|
+
|| shapePreviewSourceByWidgetId[wsReadyWidgetId];
|
|
12684
|
+
const keepHistoricalSelection = !generatingWidgetIds[wsReadyWidgetId]
|
|
12685
|
+
&& !!selectedVersion?.startsWith('v:')
|
|
12686
|
+
&& !versionUsesSharedHeadPreview(wsReadyWidgetId, selectedVersion);
|
|
12687
|
+
if (keepHistoricalSelection) {
|
|
12688
|
+
console.log('[CreatorApp] Historical version stays selected; shared HEAD build ignored', wsReadyWidgetId.substring(0, 8));
|
|
12530
12689
|
} else {
|
|
12531
|
-
|
|
12532
|
-
|
|
12690
|
+
shapePreviewSourceByWidgetId = {
|
|
12691
|
+
...shapePreviewSourceByWidgetId,
|
|
12692
|
+
[wsReadyWidgetId]: 'preview',
|
|
12693
|
+
};
|
|
12694
|
+
if (codegenCaptureBlocked()) {
|
|
12695
|
+
console.log('[CreatorApp] Defer canvas Worker preview reload until codegen finishes', wsReadyWidgetId.substring(0, 8));
|
|
12696
|
+
} else {
|
|
12697
|
+
syncWidgetShapeLivePreviews();
|
|
12698
|
+
widgetShapeLivePreviewsRef?.requestReload?.(wsReadyWidgetId);
|
|
12699
|
+
}
|
|
12533
12700
|
}
|
|
12534
12701
|
}
|
|
12535
12702
|
}
|
|
@@ -15934,6 +16101,7 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
15934
16101
|
widgetHeadCommitSha={headCommitShaByWidgetId[panelWidgetId] || ps.lastCommitId}
|
|
15935
16102
|
versionLabelByCommitSha={versionLabelsByWidgetId[panelWidgetId] || {}}
|
|
15936
16103
|
versionLabelByPrompt={versionPromptLabelsByWidgetId[panelWidgetId] || {}}
|
|
16104
|
+
versionTimeline={versionTimelineByWidgetId[panelWidgetId] || []}
|
|
15937
16105
|
viewingVersion={viewingVersionByWidgetId[panelWidgetId] ?? null}
|
|
15938
16106
|
onSendMessage={(content, attachments) => handleChatSendMessage(content, attachments, panelWidgetId)}
|
|
15939
16107
|
onRetryMessage={(content) => handleChatRetryMessage(content, panelWidgetId)}
|
|
@@ -497,6 +497,20 @@
|
|
|
497
497
|
void forceReloadPreviewIframe(url);
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Shared Worker HEAD (`/preview/{widgetId}`). Do not pin it: a pin blocks the
|
|
502
|
+
* next build event, so the panel keeps the previous generation.
|
|
503
|
+
*/
|
|
504
|
+
export function showLiveWorkerPreview(url: string | null): void {
|
|
505
|
+
if (!url) return;
|
|
506
|
+
pinnedPublishedPreviewUrl = null;
|
|
507
|
+
lastWsPreviewSignature = null;
|
|
508
|
+
publishedArtifactReachable = true;
|
|
509
|
+
_lastEditPreviewBase = null;
|
|
510
|
+
_lastEmbedPreviewBase = null;
|
|
511
|
+
void forceReloadPreviewIframe(url);
|
|
512
|
+
}
|
|
513
|
+
|
|
500
514
|
export function finishCompositionHydration(): void {
|
|
501
515
|
if (compositionHydrationTimeout) {
|
|
502
516
|
clearTimeout(compositionHydrationTimeout);
|
|
@@ -106,6 +106,7 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
106
106
|
*/ showCompositionSteps?: boolean;
|
|
107
107
|
beginCompositionHydration?: () => void;
|
|
108
108
|
showPublishedArtifact?: (url: string | null) => void;
|
|
109
|
+
showLiveWorkerPreview?: (url: string | null) => void;
|
|
109
110
|
finishCompositionHydration?: () => void;
|
|
110
111
|
markCodegenPreviewFresh?: () => void;
|
|
111
112
|
clearCodegenPreviewFresh?: () => void;
|
|
@@ -207,6 +208,7 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
207
208
|
debugModeAvailable: boolean;
|
|
208
209
|
beginCompositionHydration: () => void;
|
|
209
210
|
showPublishedArtifact: (url: string | null) => void;
|
|
211
|
+
showLiveWorkerPreview: (url: string | null) => void;
|
|
210
212
|
finishCompositionHydration: () => void;
|
|
211
213
|
markCodegenPreviewFresh: () => void;
|
|
212
214
|
clearCodegenPreviewFresh: () => void;
|
|
@@ -205,19 +205,25 @@
|
|
|
205
205
|
const coalescedReloadTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
206
206
|
|
|
207
207
|
export function requestReload(widgetId: string): void {
|
|
208
|
-
if (isGenerating) return;
|
|
209
208
|
const now = Date.now();
|
|
210
209
|
const sinceLast = now - (lastReloadAtByWidgetId[widgetId] || 0);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
210
|
+
// A second call inside the window used to be dropped when a timer already
|
|
211
|
+
// existed, so the codegen reload (the one that matches the new commit)
|
|
212
|
+
// never ran and the shape kept the previous build. Replace the timer so
|
|
213
|
+
// the later request wins. While generating, wait and try again.
|
|
214
|
+
if (isGenerating || sinceLast < RELOAD_COALESCE_MS) {
|
|
215
|
+
const existing = coalescedReloadTimers.get(widgetId);
|
|
216
|
+
if (existing) clearTimeout(existing);
|
|
217
|
+
const wait = isGenerating
|
|
218
|
+
? RELOAD_COALESCE_MS
|
|
219
|
+
: Math.max(0, RELOAD_COALESCE_MS - sinceLast);
|
|
220
|
+
coalescedReloadTimers.set(
|
|
221
|
+
widgetId,
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
coalescedReloadTimers.delete(widgetId);
|
|
224
|
+
requestReload(widgetId);
|
|
225
|
+
}, wait),
|
|
226
|
+
);
|
|
221
227
|
return;
|
|
222
228
|
}
|
|
223
229
|
const coalesced = coalescedReloadTimers.get(widgetId);
|
|
@@ -132,27 +132,10 @@ export function installFreezeWatchdog() {
|
|
|
132
132
|
URL.revokeObjectURL(url);
|
|
133
133
|
let lastStatus = '';
|
|
134
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
|
-
}
|
|
150
135
|
worker.onmessage = (event) => {
|
|
151
136
|
const data = event.data;
|
|
152
137
|
if (data?.type === 'ping') {
|
|
153
|
-
|
|
154
|
-
lastLongTaskSentAt = performance.now();
|
|
155
|
-
worker.postMessage({ type: 'pong', status: lastStatus, breadcrumbs });
|
|
138
|
+
worker.postMessage({ type: 'pong', status: lastStatus, breadcrumbs: pendingBreadcrumbs });
|
|
156
139
|
pendingBreadcrumbs = [];
|
|
157
140
|
}
|
|
158
141
|
else if (data?.type === 'freeze-report' && data.message) {
|