@widgetic/creator 0.3.55 → 0.3.57
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 +116 -79
- package/dist/components/WidgetDetails.svelte +33 -9
- package/dist/components/WidgetDetails.svelte.d.ts +5 -0
- package/dist/creator-types.d.ts +2 -1
- package/dist/utils/persistWidgetThumbnail.d.ts +5 -0
- package/dist/utils/persistWidgetThumbnail.js +28 -0
- package/package.json +1 -1
package/dist/CreatorApp.svelte
CHANGED
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
isInfrastructurePreviewError
|
|
68
68
|
} from './pageHelpers.js';
|
|
69
69
|
import { uploadChatFileToWidgeticApi } from './utils/widgeticChatUpload.js';
|
|
70
|
+
import { persistWidgetThumbnail } from './utils/persistWidgetThumbnail.js';
|
|
70
71
|
import { nativeScrollbarPadding } from './utils/nativeScrollbarPadding.js';
|
|
71
72
|
import { streamOperationUpdates } from './utils/operationStream.js';
|
|
72
73
|
import { generateEmbedCode, EMBED_FORMATS, EMBED_FORMAT_LABELS, type EmbedFormat } from './utils/embedCode.js';
|
|
@@ -81,6 +82,14 @@
|
|
|
81
82
|
import { setEnableLocalCache, isLocalCacheEnabled } from './localCacheGate.js';
|
|
82
83
|
import type { CreatorAppProps } from './creator-types.js';
|
|
83
84
|
|
|
85
|
+
function resolveCreatorWorkflowStep(
|
|
86
|
+
step?: CreatorAppProps['initialStep'] | 'create' | 'edit' | 'embed' | null
|
|
87
|
+
): 'create' | 'edit' | 'embed' {
|
|
88
|
+
if (!canvasAdvancedFeatures) return 'create';
|
|
89
|
+
if (step === 'edit' || step === 'embed') return step;
|
|
90
|
+
return 'create';
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
/** Embedded mode props (site integration) */
|
|
85
94
|
export let embedded: CreatorAppProps['embedded'] = false;
|
|
86
95
|
export let canvasId: CreatorAppProps['canvasId'] = null;
|
|
@@ -928,10 +937,6 @@
|
|
|
928
937
|
codegenWidgetId = finalizedWidgetId;
|
|
929
938
|
const newPanelChat = getChatMethodsForWidget(finalizedWidgetId);
|
|
930
939
|
newPanelChat?.setDraftText?.('');
|
|
931
|
-
const hydrateImages = fallbackChatImageAttachments(finalizedWidgetId);
|
|
932
|
-
if (hydrateImages.length > 0) {
|
|
933
|
-
newPanelChat?.updateLastUserMessageAttachments?.(hydrateImages);
|
|
934
|
-
}
|
|
935
940
|
}
|
|
936
941
|
|
|
937
942
|
const panelChat = getChatMethodsForWidget(codegenWidgetId);
|
|
@@ -1251,31 +1256,6 @@
|
|
|
1251
1256
|
* picked, which may differ from convos[0] when several empty conversations
|
|
1252
1257
|
* exist (feedback c: chat looked empty while messages went elsewhere).
|
|
1253
1258
|
*/
|
|
1254
|
-
function userMessageImpliesAttachedFile(content: string): boolean {
|
|
1255
|
-
return /imaginea ata[sș]at[ăa]|attached (reference )?(image|document|file|pdf|video)|reference image|din imagine|ata[sș]at[ăa]?\s+(document|pdf|fi[sș]ier|video)|attached document|starting from the attached image/i.test(
|
|
1256
|
-
content || '',
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
function fallbackChatImageAttachments(widgetId: string): Array<{
|
|
1261
|
-
id: string;
|
|
1262
|
-
url: string;
|
|
1263
|
-
file_type: string;
|
|
1264
|
-
file_name: string;
|
|
1265
|
-
}> {
|
|
1266
|
-
const urls = uniquePersistImageUrls([
|
|
1267
|
-
...(getPanelState(widgetId).promptImages ?? []),
|
|
1268
|
-
...(widgetId === focusedPanelWidgetId ? promptImages : []),
|
|
1269
|
-
(typeof canvasRef?.getWidgetShapeDataUrl === 'function' ? canvasRef.getWidgetShapeDataUrl(widgetId) : '') || '',
|
|
1270
|
-
]);
|
|
1271
|
-
return urls.map((url, i) => ({
|
|
1272
|
-
id: `hydrate_img_${i}`,
|
|
1273
|
-
url,
|
|
1274
|
-
file_type: guessAttachmentFileType(url, 'design-screenshot.png', 'image/png'),
|
|
1275
|
-
file_name: 'design-screenshot.png',
|
|
1276
|
-
}));
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
1259
|
function guessAttachmentFileType(url: string, fileName?: string | null, fallback?: string | null): string {
|
|
1280
1260
|
if (fallback && String(fallback).includes('/')) return String(fallback);
|
|
1281
1261
|
const hint = `${url || ''} ${fileName || ''}`;
|
|
@@ -1297,7 +1277,26 @@
|
|
|
1297
1277
|
else if (url.startsWith('data:image/')) dataUrls.push(url);
|
|
1298
1278
|
}
|
|
1299
1279
|
// Same screenshot often exists as CDN URL + compressed data URL — keep HTTPS only.
|
|
1300
|
-
|
|
1280
|
+
// If only data URLs remain, keep the first one so convert does not triple the bubble.
|
|
1281
|
+
if (httpsUrls.length > 0) return httpsUrls;
|
|
1282
|
+
return dataUrls.slice(0, 1);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
async function persistWidgetListThumbnail(widgetId: string, imageDataUrl: string): Promise<void> {
|
|
1286
|
+
if (!widgetsClient || !widgetId || !imageDataUrl) return;
|
|
1287
|
+
try {
|
|
1288
|
+
const compact = imageDataUrl.startsWith('data:image/')
|
|
1289
|
+
? await compressImageDataUrlForCodegen(imageDataUrl, 384, 180000)
|
|
1290
|
+
: imageDataUrl;
|
|
1291
|
+
const thumbnailUrl = await persistWidgetThumbnail(widgetsClient, widgetId, compact);
|
|
1292
|
+
if (thumbnailUrl) {
|
|
1293
|
+
widgets = widgets.map((w) =>
|
|
1294
|
+
w.id === widgetId ? { ...w, thumbnailUrl, thumbnail_url: thumbnailUrl } : w
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
console.warn('[CreatorApp] persist list thumbnail failed', widgetId.slice(0, 8), error);
|
|
1299
|
+
}
|
|
1301
1300
|
}
|
|
1302
1301
|
|
|
1303
1302
|
async function ensureCodegenUserMessagePersisted(
|
|
@@ -1400,6 +1399,14 @@
|
|
|
1400
1399
|
.filter((url: string | undefined): url is string => !!url),
|
|
1401
1400
|
);
|
|
1402
1401
|
const existingHasRemoteImage = [...existingUrls].some((url) => /^https?:\/\//i.test(url));
|
|
1402
|
+
// Chat already persisted CDN attachments for this turn — do not merge data URLs/canvas shots.
|
|
1403
|
+
if (targetUser && existingHasRemoteImage) {
|
|
1404
|
+
if (convId !== visibleConversationId) {
|
|
1405
|
+
await panelChat?.selectConversation?.(convId);
|
|
1406
|
+
patchPanelSnapshot(widgetId, { currentChatConversationId: convId });
|
|
1407
|
+
}
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1403
1410
|
const missingPersist = persistAttachments.filter((att) => {
|
|
1404
1411
|
if (att.url && existingUrls.has(att.url)) return false;
|
|
1405
1412
|
if (existingHasRemoteImage && typeof att.url === 'string' && att.url.startsWith('data:')) return false;
|
|
@@ -1627,14 +1634,6 @@
|
|
|
1627
1634
|
created_at: content.created_at || content.createdAt || a.created_at || a.createdAt || new Date().toISOString(),
|
|
1628
1635
|
};
|
|
1629
1636
|
}).filter((a: { url: string }) => !!a.url);
|
|
1630
|
-
if (
|
|
1631
|
-
attachments.length === 0
|
|
1632
|
-
&& (m.message_type || m.messageType || 'user') === 'user'
|
|
1633
|
-
&& userMessageImpliesAttachedFile(m.message_content || m.messageContent || '')
|
|
1634
|
-
&& widgetIdForSync
|
|
1635
|
-
) {
|
|
1636
|
-
attachments = fallbackChatImageAttachments(widgetIdForSync);
|
|
1637
|
-
}
|
|
1638
1637
|
return {
|
|
1639
1638
|
id: m.id,
|
|
1640
1639
|
conversationId: m.conversation_id || m.conversationId || conversationId,
|
|
@@ -3592,12 +3591,12 @@
|
|
|
3592
3591
|
const buildSha = import.meta.env.VITE_BUILD_SHA || 'local';
|
|
3593
3592
|
const buildLabel = `v${appVersion} · ${buildSha}`;
|
|
3594
3593
|
|
|
3595
|
-
// Default coder: host prop (site embed) wins. Fallback Flash
|
|
3594
|
+
// Default coder: host prop (site embed) wins. Fallback = first catalog model (DeepSeek V4.1 Flash on Baseten).
|
|
3596
3595
|
const hostCoderModelId = (defaultCoderModelId || '').trim() || null;
|
|
3597
3596
|
const hostPlannerRaw = (defaultPlannerModelId || '').trim();
|
|
3598
3597
|
const hostPlannerModelId =
|
|
3599
3598
|
!hostPlannerRaw || /^(none|skip)$/i.test(hostPlannerRaw) ? null : hostPlannerRaw;
|
|
3600
|
-
let defaultModelId = hostCoderModelId || 'baseten-
|
|
3599
|
+
let defaultModelId = hostCoderModelId || 'baseten-deepseek-v4.1-flash';
|
|
3601
3600
|
|
|
3602
3601
|
// Available LLM models for code generation
|
|
3603
3602
|
interface LLMModelOption {
|
|
@@ -3608,18 +3607,18 @@
|
|
|
3608
3607
|
supportsVision?: boolean;
|
|
3609
3608
|
}
|
|
3610
3609
|
|
|
3611
|
-
// Default fallback models in case API fails (
|
|
3610
|
+
// Default fallback models in case API fails (order mirrors llm-models.catalog.json)
|
|
3612
3611
|
const fallbackModels: LLMModelOption[] = [
|
|
3612
|
+
{ id: 'baseten-deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash (Baseten)', provider: 'Baseten', description: 'V4.1 Flash via Baseten Model APIs', supportsVision: true },
|
|
3613
3613
|
{ id: 'baseten-glm-5.3-fast', name: 'GLM 5.3 Fast (Baseten)', provider: 'Baseten', description: 'Lower-latency GLM 5.3 on Baseten Fast capacity', supportsVision: true },
|
|
3614
3614
|
{ id: 'baseten-glm-5.3-flash', name: 'GLM 5.3 Flash (Baseten)', provider: 'Baseten', description: 'Cheaper GLM via Baseten Model APIs', supportsVision: true },
|
|
3615
3615
|
{ id: 'zhipu-glm-5.3-flash', name: 'GLM 5.3 Flash (Z.ai)', provider: 'Zhipu', description: 'Cheapest vision + coding model', supportsVision: true },
|
|
3616
|
+
{ id: 'deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash (DeepSeek API)', provider: 'DeepSeek', description: 'V4.1 Flash via DeepSeek direct API', supportsVision: true },
|
|
3616
3617
|
{ id: 'deepseek-v4-pro', name: 'DeepSeek V4 Pro', provider: 'DeepSeek', description: 'Frontier coding model, 1M context', supportsVision: false },
|
|
3617
|
-
{ id: 'deepseek-v4-flash', name: 'DeepSeek V4 Flash', provider: 'DeepSeek', description: '
|
|
3618
|
-
{ id: 'openai-gpt-5.4', name: 'GPT-5.4 (OpenAI)', provider: 'OpenAI', description: 'Affordable model for coding', supportsVision: true },
|
|
3619
|
-
{ id: 'openai-gpt-5.5', name: 'GPT-5.5 (OpenAI)', provider: 'OpenAI', description: 'Frontier model for complex work', supportsVision: true },
|
|
3618
|
+
{ id: 'deepseek-v4-flash', name: 'DeepSeek V4 Flash', provider: 'DeepSeek', description: 'Legacy id; routes to V4.1 Flash', supportsVision: true },
|
|
3620
3619
|
{ id: 'openai-codex-3.2', name: 'Codex 3.2 (OpenAI)', provider: 'OpenAI', description: 'Coding-specialized model', supportsVision: false },
|
|
3621
|
-
{ id: '
|
|
3622
|
-
{ id: '
|
|
3620
|
+
{ id: 'openai-gpt-5.5', name: 'GPT-5.5 (OpenAI)', provider: 'OpenAI', description: 'Frontier model for complex work', supportsVision: true },
|
|
3621
|
+
{ id: 'gemini-3.8-flash', name: 'Gemini 3.8 Flash (Google)', provider: 'Google', description: 'Most capable Gemini Flash', supportsVision: true },
|
|
3623
3622
|
];
|
|
3624
3623
|
|
|
3625
3624
|
// Models loaded from API (single source of truth)
|
|
@@ -3645,10 +3644,18 @@
|
|
|
3645
3644
|
supportsVision: m.supportsVision === true,
|
|
3646
3645
|
}));
|
|
3647
3646
|
|
|
3648
|
-
// Model LIST
|
|
3647
|
+
// Model LIST from GET /models. Selection: host pin → first listed
|
|
3648
|
+
// (catalog puts DeepSeek V4.1 Flash Baseten first) → API default → fallback.
|
|
3649
3649
|
const apiCoderId = (response as any).defaultModelId || (response as any).data?.defaultModelId;
|
|
3650
3650
|
const firstListedId = availableModels[0]?.id;
|
|
3651
|
-
|
|
3651
|
+
const listedIds = new Set(availableModels.map((m) => m.id));
|
|
3652
|
+
const pickListed = (id: string | null | undefined) =>
|
|
3653
|
+
id && listedIds.has(id) ? id : null;
|
|
3654
|
+
defaultModelId =
|
|
3655
|
+
pickListed(hostCoderModelId) ||
|
|
3656
|
+
pickListed(firstListedId) ||
|
|
3657
|
+
pickListed(apiCoderId) ||
|
|
3658
|
+
defaultModelId;
|
|
3652
3659
|
userSelectedModelId = defaultModelId;
|
|
3653
3660
|
console.log('[WidgetCreator] Default coder model', {
|
|
3654
3661
|
defaultModelId,
|
|
@@ -3724,6 +3731,11 @@
|
|
|
3724
3731
|
|
|
3725
3732
|
// Get selected model object
|
|
3726
3733
|
$: selectedModel = availableModels.find(m => m.id === selectedModelId) || availableModels[0];
|
|
3734
|
+
$: modelDropdownItems = availableModels.map((model) => ({
|
|
3735
|
+
id: model.id,
|
|
3736
|
+
name: model.name,
|
|
3737
|
+
badge: model.provider,
|
|
3738
|
+
}));
|
|
3727
3739
|
|
|
3728
3740
|
// Handle model selection (only used in debug mode)
|
|
3729
3741
|
function handleModelSelect(modelId: string) {
|
|
@@ -3794,8 +3806,17 @@
|
|
|
3794
3806
|
|
|
3795
3807
|
function handleHostToggleAdvancedEvent() {
|
|
3796
3808
|
canvasAdvancedFeatures = !canvasAdvancedFeatures;
|
|
3797
|
-
// console.log('[CreatorApp] Advanced canvas features:', canvasAdvancedFeatures ? 'ON' : 'OFF');
|
|
3798
3809
|
emitCreatorAdvancedChanged();
|
|
3810
|
+
console.log('[CreatorApp] Advanced', canvasAdvancedFeatures ? 'ON' : 'OFF',
|
|
3811
|
+
'— composition steps', canvasAdvancedFeatures ? 'visible' : 'hidden');
|
|
3812
|
+
if (!canvasAdvancedFeatures) {
|
|
3813
|
+
widgetDetailsStep = 'create';
|
|
3814
|
+
for (const panelId of Object.keys(widgetPanelSnapshots || {})) {
|
|
3815
|
+
if (getPanelState(panelId).widgetDetailsStep !== 'create') {
|
|
3816
|
+
patchPanelSnapshot(panelId, { widgetDetailsStep: 'create' });
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3799
3820
|
}
|
|
3800
3821
|
|
|
3801
3822
|
function resolveHostTargetWidgetId(): string | null {
|
|
@@ -5499,7 +5520,7 @@
|
|
|
5499
5520
|
selectedWidgetId === deepLinkWidgetId && !!getWidgetById(deepLinkWidgetId);
|
|
5500
5521
|
if (alreadyLinked) {
|
|
5501
5522
|
if (embeddedInitialStepPending && initialStep) {
|
|
5502
|
-
widgetDetailsStep = initialStep;
|
|
5523
|
+
widgetDetailsStep = resolveCreatorWorkflowStep(initialStep);
|
|
5503
5524
|
embeddedInitialStepPending = false;
|
|
5504
5525
|
}
|
|
5505
5526
|
embeddedDeepLinkAutoOpenDone = true;
|
|
@@ -5550,7 +5571,7 @@
|
|
|
5550
5571
|
canvasRef.selectWidgetShape(target.id);
|
|
5551
5572
|
}
|
|
5552
5573
|
if (embeddedInitialStepPending && initialStep) {
|
|
5553
|
-
widgetDetailsStep = initialStep;
|
|
5574
|
+
widgetDetailsStep = resolveCreatorWorkflowStep(initialStep);
|
|
5554
5575
|
embeddedInitialStepPending = false;
|
|
5555
5576
|
}
|
|
5556
5577
|
|
|
@@ -5988,10 +6009,10 @@
|
|
|
5988
6009
|
}
|
|
5989
6010
|
if (!isGeneratingCode && !_isRestoringCheckpoint) {
|
|
5990
6011
|
if (embeddedInitialStepPending && initialStep) {
|
|
5991
|
-
widgetDetailsStep = initialStep;
|
|
6012
|
+
widgetDetailsStep = resolveCreatorWorkflowStep(initialStep);
|
|
5992
6013
|
embeddedInitialStepPending = false;
|
|
5993
6014
|
} else if (initialStep) {
|
|
5994
|
-
widgetDetailsStep = initialStep;
|
|
6015
|
+
widgetDetailsStep = resolveCreatorWorkflowStep(initialStep);
|
|
5995
6016
|
} else {
|
|
5996
6017
|
widgetDetailsStep = 'create';
|
|
5997
6018
|
}
|
|
@@ -6839,8 +6860,8 @@
|
|
|
6839
6860
|
if (quiet || isConvertOrDraftPanelOpen()) {
|
|
6840
6861
|
const shapeDataUrl = canvasRef?.getWidgetShapeDataUrl?.(widgetId);
|
|
6841
6862
|
if (shapeDataUrl && canvasRef) {
|
|
6842
|
-
// console.log('[handleRefreshScreenshot] Using canvas shape screenshot (panel closed):', widgetId.substring(0, 8));
|
|
6843
6863
|
canvasRef.updateWidgetImage(widgetId, shapeDataUrl);
|
|
6864
|
+
void persistWidgetListThumbnail(widgetId, shapeDataUrl);
|
|
6844
6865
|
} else {
|
|
6845
6866
|
// console.log('[handleRefreshScreenshot] Skip quiet capture — panel closed:', widgetId.substring(0, 8));
|
|
6846
6867
|
}
|
|
@@ -6892,6 +6913,7 @@
|
|
|
6892
6913
|
const json = canvasRef?.saveCanvas();
|
|
6893
6914
|
if (json) handleCanvasAutoSave(json);
|
|
6894
6915
|
}, 500);
|
|
6916
|
+
void persistWidgetListThumbnail(widgetId, result.data);
|
|
6895
6917
|
if (!quiet) {
|
|
6896
6918
|
showToast('success', 'Widget preview updated on canvas', { duration: 2000 });
|
|
6897
6919
|
} else {
|
|
@@ -8080,9 +8102,10 @@ Requirements:
|
|
|
8080
8102
|
selectedWidgetId = initialWidgetId;
|
|
8081
8103
|
saveSelectedWidgetId(initialWidgetId);
|
|
8082
8104
|
}
|
|
8083
|
-
// Site deep-link: Edit widget
|
|
8105
|
+
// Site deep-link: Edit widget / New Composition used to open Edit/Embed in Creator.
|
|
8106
|
+
// Canvas is Create-only now; /composition owns the editor.
|
|
8084
8107
|
if (embedded && initialStep) {
|
|
8085
|
-
widgetDetailsStep = initialStep;
|
|
8108
|
+
widgetDetailsStep = resolveCreatorWorkflowStep(initialStep);
|
|
8086
8109
|
}
|
|
8087
8110
|
if (embedded && projectId) {
|
|
8088
8111
|
// console.log('[CreatorApp] Embedded project context:', {
|
|
@@ -8733,7 +8756,7 @@ Requirements:
|
|
|
8733
8756
|
lastPublishedVersion: publishedVersion,
|
|
8734
8757
|
selectedWidgetJsPath: widget?.jsPath ?? null,
|
|
8735
8758
|
currentRepositoryId: normalizeRepositoryId(widget),
|
|
8736
|
-
widgetDetailsStep: publishedVersion !== null ? 'edit' : 'create',
|
|
8759
|
+
widgetDetailsStep: resolveCreatorWorkflowStep(publishedVersion !== null ? 'edit' : 'create'),
|
|
8737
8760
|
generateCodeStatus: null,
|
|
8738
8761
|
lastGenerationFailed: false,
|
|
8739
8762
|
lastGenerationError: null,
|
|
@@ -8949,12 +8972,18 @@ Requirements:
|
|
|
8949
8972
|
const resolvedVersion = resolvePublishedVersionFromWidget(widget);
|
|
8950
8973
|
const resolvedJsPath = ((widget as { jsPath?: string | null; js_path?: string | null })?.jsPath
|
|
8951
8974
|
?? (widget as { js_path?: string | null })?.js_path) as string | null;
|
|
8952
|
-
if (resolvedVersion !== null &&
|
|
8975
|
+
if (resolvedVersion !== null && snap.lastPublishedVersion === null) {
|
|
8953
8976
|
patchPanelSnapshot(widgetId, {
|
|
8954
8977
|
lastPublishedVersion: resolvedVersion,
|
|
8955
8978
|
selectedWidgetJsPath: snap.selectedWidgetJsPath ?? resolvedJsPath,
|
|
8956
|
-
widgetDetailsStep: 'edit',
|
|
8979
|
+
...(canvasAdvancedFeatures ? { widgetDetailsStep: 'edit' as const } : {}),
|
|
8957
8980
|
});
|
|
8981
|
+
} else if (
|
|
8982
|
+
canvasAdvancedFeatures
|
|
8983
|
+
&& resolvedVersion !== null
|
|
8984
|
+
&& snap.widgetDetailsStep === 'create'
|
|
8985
|
+
) {
|
|
8986
|
+
patchPanelSnapshot(widgetId, { widgetDetailsStep: 'edit' });
|
|
8958
8987
|
}
|
|
8959
8988
|
}
|
|
8960
8989
|
|
|
@@ -11889,6 +11918,10 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
11889
11918
|
}
|
|
11890
11919
|
}
|
|
11891
11920
|
|
|
11921
|
+
if (screenshot) {
|
|
11922
|
+
void persistWidgetListThumbnail(widgetId, screenshot);
|
|
11923
|
+
}
|
|
11924
|
+
|
|
11892
11925
|
// Auto-create repository so generation can start right away.
|
|
11893
11926
|
try {
|
|
11894
11927
|
const repoId = await ensureWidgetRepository(newWidget, { silent: true });
|
|
@@ -11907,11 +11940,15 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
11907
11940
|
// panel remounts: the freshly mounted Chat then activates this
|
|
11908
11941
|
// conversation (best-conversation = one with messages) instead of
|
|
11909
11942
|
// starting empty after a close/reopen.
|
|
11910
|
-
await ensureCodegenUserMessagePersisted(
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11943
|
+
await ensureCodegenUserMessagePersisted(
|
|
11944
|
+
widgetId,
|
|
11945
|
+
prompt,
|
|
11946
|
+
uniquePersistImageUrls([
|
|
11947
|
+
...extraImageUrls,
|
|
11948
|
+
...(draftPanelState.promptImages ?? []),
|
|
11949
|
+
screenshot,
|
|
11950
|
+
]),
|
|
11951
|
+
);
|
|
11915
11952
|
// NOTE: never copy currentChatConversationId from the draft — the draft
|
|
11916
11953
|
// chat's conversation is a local UUID that doesn't exist in the DB, and
|
|
11917
11954
|
// migrating it would point codegen/summaries at a phantom conversation.
|
|
@@ -12882,6 +12919,7 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
12882
12919
|
panelTopOffset={widgetsPanelTopOffset}
|
|
12883
12920
|
panelBottomMargin={embedded ? 24 : widgetsPanelBottomMargin}
|
|
12884
12921
|
fillViewport={false}
|
|
12922
|
+
showCompositionSteps={canvasAdvancedFeatures}
|
|
12885
12923
|
currentStep={ps.widgetDetailsStep}
|
|
12886
12924
|
on:focusPanel={() => focusWidgetPanel(panelWidgetId)}
|
|
12887
12925
|
on:stepChange={({ detail }) => {
|
|
@@ -13209,22 +13247,21 @@ $: if (codeGenerationPrompt && selectedWidgetId && promptLoadedForWidgetId === s
|
|
|
13209
13247
|
<div class="generation-chat-sticky-header shrink-0 z-20 bg-white border-b border-gray-200">
|
|
13210
13248
|
<!-- svelte-ignore a11y_label_has_associated_control -->
|
|
13211
13249
|
<div class="generation-models-header flex items-center justify-end gap-2 px-2 py-1.5 bg-gray-50">
|
|
13212
|
-
<span class="text-[10px] font-semibold text-gray-500 uppercase tracking-wide shrink-0">Model</span>
|
|
13213
13250
|
<div class="coder-model-selector flex items-center justify-end min-w-0">
|
|
13214
|
-
<
|
|
13215
|
-
class="coder-model-dropdown
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
{
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
{
|
|
13227
|
-
|
|
13251
|
+
<SelectorDropdown
|
|
13252
|
+
class="coder-model-dropdown"
|
|
13253
|
+
label="Model"
|
|
13254
|
+
labelPosition="inline"
|
|
13255
|
+
items={modelDropdownItems}
|
|
13256
|
+
selectedId={userSelectedModelId}
|
|
13257
|
+
placeholder={modelsLoading ? 'Loading…' : 'Select model'}
|
|
13258
|
+
nameWidth="240px"
|
|
13259
|
+
accentColor="#314F2F"
|
|
13260
|
+
showNewButton={false}
|
|
13261
|
+
allowRename={false}
|
|
13262
|
+
loading={modelsLoading}
|
|
13263
|
+
onselect={(item) => handleModelSelect(item.id)}
|
|
13264
|
+
/>
|
|
13228
13265
|
</div>
|
|
13229
13266
|
</div>
|
|
13230
13267
|
</div>
|
|
@@ -285,6 +285,12 @@
|
|
|
285
285
|
// ═══════════════════════════════════════════════════════════════════════
|
|
286
286
|
type WidgetStep = 'create' | 'edit' | 'embed';
|
|
287
287
|
export let currentStep: WidgetStep = 'create';
|
|
288
|
+
/**
|
|
289
|
+
* Canvas Creator is code-only (chat + preview). Edit/Embed header steps stay
|
|
290
|
+
* off until composition editing lives on /composition. Keep the Edit/Embed
|
|
291
|
+
* panels mounted (CSS-hidden) so we can turn this back on without ripping slots.
|
|
292
|
+
*/
|
|
293
|
+
export let showCompositionSteps: boolean = false;
|
|
288
294
|
/** After Set live, Create preview stays on that published artifact until the next Worker build. */
|
|
289
295
|
let pinnedPublishedPreviewUrl: string | null = null;
|
|
290
296
|
|
|
@@ -308,8 +314,15 @@
|
|
|
308
314
|
previewError = null;
|
|
309
315
|
if (!iframeSrc) previewLoading = true;
|
|
310
316
|
}
|
|
311
|
-
$: canEditStep = lastPublishedVersion !== null && !isGeneratingCode;
|
|
312
|
-
$: canEmbedStep = lastPublishedVersion !== null && !isGeneratingCode;
|
|
317
|
+
$: canEditStep = showCompositionSteps && lastPublishedVersion !== null && !isGeneratingCode;
|
|
318
|
+
$: canEmbedStep = showCompositionSteps && lastPublishedVersion !== null && !isGeneratingCode;
|
|
319
|
+
$: activeWorkflowStep = showCompositionSteps ? currentStep : 'create';
|
|
320
|
+
|
|
321
|
+
$: if (!showCompositionSteps && currentStep !== 'create') {
|
|
322
|
+
currentStep = 'create';
|
|
323
|
+
dispatch('stepChange', { step: 'create' });
|
|
324
|
+
switchPreviewUrlForStep('create');
|
|
325
|
+
}
|
|
313
326
|
|
|
314
327
|
// Auto-switch back to Create when code generation starts
|
|
315
328
|
let _justFinishedGenerating = false;
|
|
@@ -358,6 +371,7 @@
|
|
|
358
371
|
|
|
359
372
|
function canNavigateToStep(step: WidgetStep): boolean {
|
|
360
373
|
if (step === 'create') return true;
|
|
374
|
+
if (!showCompositionSteps) return false;
|
|
361
375
|
if (step === 'edit') return canEditStep;
|
|
362
376
|
if (step === 'embed') return canEmbedStep;
|
|
363
377
|
return false;
|
|
@@ -978,6 +992,7 @@
|
|
|
978
992
|
let detailsDragBarEl: HTMLElement | null = null;
|
|
979
993
|
let widgetDetailsNameCtEl: HTMLElement | null = null;
|
|
980
994
|
let widgetDetailsStepsRowEl: HTMLElement | null = null;
|
|
995
|
+
let widgetDetailsDebugButtonsEl: HTMLElement | null = null;
|
|
981
996
|
let headerNameMaxWidthPx = 96;
|
|
982
997
|
let detailsDragStartX = 0;
|
|
983
998
|
let detailsDragStartY = 0;
|
|
@@ -2818,10 +2833,14 @@
|
|
|
2818
2833
|
}
|
|
2819
2834
|
|
|
2820
2835
|
function updateHeaderNameMaxWidth() {
|
|
2821
|
-
if (!widgetDetailsNameCtEl
|
|
2836
|
+
if (!widgetDetailsNameCtEl) return;
|
|
2822
2837
|
const nameRect = widgetDetailsNameCtEl.getBoundingClientRect();
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2838
|
+
const rightEl = showCompositionSteps && widgetDetailsStepsRowEl
|
|
2839
|
+
? widgetDetailsStepsRowEl
|
|
2840
|
+
: widgetDetailsDebugButtonsEl;
|
|
2841
|
+
if (!rightEl) return;
|
|
2842
|
+
const rightRect = rightEl.getBoundingClientRect();
|
|
2843
|
+
const available = Math.floor(rightRect.left - nameRect.left - HEADER_ELEMENTS_GAP_PX);
|
|
2825
2844
|
const next = Math.max(HEADER_NAME_MIN_WIDTH_PX, available);
|
|
2826
2845
|
if (next !== headerNameMaxWidthPx) {
|
|
2827
2846
|
headerNameMaxWidthPx = next;
|
|
@@ -3359,6 +3378,7 @@
|
|
|
3359
3378
|
}}
|
|
3360
3379
|
/>
|
|
3361
3380
|
</div>
|
|
3381
|
+
{#if showCompositionSteps}
|
|
3362
3382
|
<div
|
|
3363
3383
|
class="widget-details-step-indicator-row pointer-events-none absolute left-1/2 top-1/2 z-0 flex -translate-x-1/2 -translate-y-1/2 justify-center"
|
|
3364
3384
|
bind:this={widgetDetailsStepsRowEl}
|
|
@@ -3421,8 +3441,12 @@
|
|
|
3421
3441
|
</HoverTooltip>
|
|
3422
3442
|
</div>
|
|
3423
3443
|
</div>
|
|
3444
|
+
{/if}
|
|
3424
3445
|
|
|
3425
|
-
<div
|
|
3446
|
+
<div
|
|
3447
|
+
class="widget-details-debug-buttons relative z-[1] ml-auto flex items-center shrink-0"
|
|
3448
|
+
bind:this={widgetDetailsDebugButtonsEl}
|
|
3449
|
+
>
|
|
3426
3450
|
<HoverTooltip text={previewColumnCollapsed ? 'Open live preview' : 'Close live preview'}>
|
|
3427
3451
|
<button
|
|
3428
3452
|
type="button"
|
|
@@ -3505,7 +3529,7 @@
|
|
|
3505
3529
|
<div class="actions-panel widget-details-actions-panel relative flex flex-col flex-1 min-h-0">
|
|
3506
3530
|
|
|
3507
3531
|
<!-- Keep all steps mounted — switching tabs must not destroy chat / PropsEditor state -->
|
|
3508
|
-
<div class="widget-details-create-step flex flex-col flex-1 min-h-0 overflow-hidden overflow-x-hidden {
|
|
3532
|
+
<div class="widget-details-create-step flex flex-col flex-1 min-h-0 overflow-hidden overflow-x-hidden {activeWorkflowStep === 'create' ? '' : 'hidden'}">
|
|
3509
3533
|
<div class="widget-details-create-models-ct shrink-0 empty:hidden">
|
|
3510
3534
|
<slot name="code-generation-models" />
|
|
3511
3535
|
</div>
|
|
@@ -3523,10 +3547,10 @@
|
|
|
3523
3547
|
<slot name="publish-widget" />
|
|
3524
3548
|
</div>
|
|
3525
3549
|
</div>
|
|
3526
|
-
<div class="widget-details-edit-step flex flex-col flex-1 min-h-0 overflow-hidden {
|
|
3550
|
+
<div class="widget-details-edit-step flex flex-col flex-1 min-h-0 overflow-hidden {activeWorkflowStep === 'edit' ? '' : 'hidden'}">
|
|
3527
3551
|
<slot name="composition-editor" />
|
|
3528
3552
|
</div>
|
|
3529
|
-
<div class="widget-details-embed-step flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden scrollbar-thin gap-2 p-2 {
|
|
3553
|
+
<div class="widget-details-embed-step flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden scrollbar-thin gap-2 p-2 {activeWorkflowStep === 'embed' ? '' : 'hidden'}">
|
|
3530
3554
|
<slot name="embed-section" />
|
|
3531
3555
|
</div>
|
|
3532
3556
|
|
|
@@ -99,6 +99,11 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
99
99
|
/** Cascade offset index (0 = centered, 1+ = shifted down-right). */ panelCascadeIndex?: number;
|
|
100
100
|
/** False when another panel has keyboard/chat focus. */ isFocusedPanel?: boolean;
|
|
101
101
|
currentStep?: "embed" | "create" | "edit";
|
|
102
|
+
/**
|
|
103
|
+
* Canvas Creator is code-only (chat + preview). Edit/Embed header steps stay
|
|
104
|
+
* off until composition editing lives on /composition. Keep the Edit/Embed
|
|
105
|
+
* panels mounted (CSS-hidden) so we can turn this back on without ripping slots.
|
|
106
|
+
*/ showCompositionSteps?: boolean;
|
|
102
107
|
beginCompositionHydration?: () => void;
|
|
103
108
|
showPublishedArtifact?: (url: string | null) => void;
|
|
104
109
|
finishCompositionHydration?: () => void;
|
package/dist/creator-types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface CreatorAppProps {
|
|
|
6
6
|
initialWidgetId?: string | null;
|
|
7
7
|
/** Select this composition when opening Edit step from site. */
|
|
8
8
|
initialCompositionId?: string | null;
|
|
9
|
-
/** Open Widget Details on this step when embedded from site (overrides auto step once). */
|
|
9
|
+
/** Open Widget Details on this step when embedded from site (overrides auto step once). Ignored while Creator composition steps are hidden. */
|
|
10
10
|
initialStep?: 'create' | 'edit' | 'embed';
|
|
11
11
|
/**
|
|
12
12
|
* Site "+ New widget": create an empty canvas + Untitled widget instead of
|
|
@@ -35,6 +35,7 @@ export interface CreatorAppProps {
|
|
|
35
35
|
/**
|
|
36
36
|
* Extra canvas tools (undo/frame/image/library, zoom 1:1/fit/shortcuts).
|
|
37
37
|
* Independent of debugMode — Debug is staging-only UI.
|
|
38
|
+
* Also shows Widget Details Edit/Embed steps while ON (dev testing).
|
|
38
39
|
* Site owns the header toggle; embed uses CustomEvent after mount.
|
|
39
40
|
*/
|
|
40
41
|
initialAdvancedFeatures?: boolean;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WidgetsApi } from '@widgetic/api-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API gateway.
|
|
4
|
+
*/
|
|
5
|
+
export declare function persistWidgetThumbnail(widgetsClient: WidgetsApi | null | undefined, widgetId: string | null | undefined, imageDataUrl: string | null | undefined): Promise<string | null>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API gateway.
|
|
3
|
+
*/
|
|
4
|
+
export async function persistWidgetThumbnail(widgetsClient, widgetId, imageDataUrl) {
|
|
5
|
+
if (!widgetsClient || !widgetId || typeof imageDataUrl !== 'string')
|
|
6
|
+
return null;
|
|
7
|
+
if (!imageDataUrl.startsWith('data:image/'))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const response = await widgetsClient.uploadWidgetThumbnailRaw({
|
|
11
|
+
id: widgetId,
|
|
12
|
+
uploadWidgetThumbnailRequest: { imageDataUrl },
|
|
13
|
+
});
|
|
14
|
+
const json = await response.raw.json();
|
|
15
|
+
const thumbnailUrl = json?.data?.thumbnailUrl ||
|
|
16
|
+
json?.data?.thumbnail_url ||
|
|
17
|
+
json?.thumbnailUrl ||
|
|
18
|
+
null;
|
|
19
|
+
if (thumbnailUrl) {
|
|
20
|
+
console.log('[persistWidgetThumbnail] stored', widgetId.slice(0, 8), thumbnailUrl.slice(0, 80));
|
|
21
|
+
}
|
|
22
|
+
return thumbnailUrl;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.warn('[persistWidgetThumbnail] failed', widgetId?.slice(0, 8), error);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|