@widgetic/creator 0.3.54 → 0.3.55
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 +1465 -980
- package/dist/components/EditableName.svelte +149 -36
- package/dist/components/EditableName.svelte.d.ts +0 -1
- package/dist/components/HoverTooltip.svelte +60 -0
- package/dist/components/HoverTooltip.svelte.d.ts +31 -0
- package/dist/components/WidgetDetails.svelte +485 -225
- package/dist/components/WidgetDetails.svelte.d.ts +7 -1
- package/dist/pageHelpers.d.ts +2 -0
- package/dist/pageHelpers.js +40 -6
- package/dist/stores/userSession.d.ts +1 -1
- package/dist/stores/userSession.js +19 -4
- package/dist/stores/websocketStore.js +2 -1
- package/dist/syncSiteAuth.js +1 -1
- package/dist/utils/embedCode.d.ts +3 -27
- package/dist/utils/embedCode.js +5 -34
- package/dist/utils/nativeScrollbarPadding.d.ts +5 -0
- package/dist/utils/nativeScrollbarPadding.js +27 -0
- package/dist/utils/widgeticChatUpload.js +2 -2
- package/package.json +13 -19
- package/dist/utils/models.d.ts +0 -4
- package/dist/utils/models.js +0 -94
- package/dist/utils/prototypes.d.ts +0 -0
- package/dist/utils/prototypes.js +0 -20
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
import { userSession as userSessionStore } from '../stores/userSession.js';
|
|
33
33
|
|
|
34
34
|
import { WidgetPreview } from '@widgetic/editor';
|
|
35
|
+
import HoverTooltip from './HoverTooltip.svelte';
|
|
36
|
+
import EditableName from './EditableName.svelte';
|
|
37
|
+
|
|
38
|
+
const HEADER_ELEMENTS_GAP_PX = 6;
|
|
39
|
+
const HEADER_PADDING_PX = HEADER_ELEMENTS_GAP_PX + 2;
|
|
40
|
+
const HEADER_NAME_MIN_WIDTH_PX = 22;
|
|
41
|
+
const DETAILS_DRAG_THRESHOLD_PX = 5;
|
|
35
42
|
|
|
36
43
|
const dispatch = createEventDispatcher();
|
|
37
44
|
|
|
@@ -278,6 +285,8 @@
|
|
|
278
285
|
// ═══════════════════════════════════════════════════════════════════════
|
|
279
286
|
type WidgetStep = 'create' | 'edit' | 'embed';
|
|
280
287
|
export let currentStep: WidgetStep = 'create';
|
|
288
|
+
/** After Set live, Create preview stays on that published artifact until the next Worker build. */
|
|
289
|
+
let pinnedPublishedPreviewUrl: string | null = null;
|
|
281
290
|
|
|
282
291
|
const STEPS: { id: WidgetStep; label: string; number: number }[] = [
|
|
283
292
|
{ id: 'create', label: 'Create', number: 1 },
|
|
@@ -314,6 +323,7 @@
|
|
|
314
323
|
previewError = null;
|
|
315
324
|
previewHasRuntimeError = false;
|
|
316
325
|
previewRuntimeErrorStatus = null;
|
|
326
|
+
pinnedPublishedPreviewUrl = null;
|
|
317
327
|
}
|
|
318
328
|
|
|
319
329
|
// Default step on widget switch: always open on Create
|
|
@@ -326,6 +336,7 @@
|
|
|
326
336
|
}
|
|
327
337
|
currentStep = 'create';
|
|
328
338
|
dispatch('stepChange', { step: 'create' });
|
|
339
|
+
pinnedPublishedPreviewUrl = null;
|
|
329
340
|
const createBase = normalizePreviewBaseUrl(
|
|
330
341
|
toPreviewFetchUrl(resolvePreviewUrlForStep('create') ?? '')
|
|
331
342
|
);
|
|
@@ -399,6 +410,10 @@
|
|
|
399
410
|
dynamicWorkerPreviewUrl = workerUrl;
|
|
400
411
|
syncPreviewDisplayUrlForStep('create');
|
|
401
412
|
void forceReloadPreviewIframe(workerUrl);
|
|
413
|
+
} else if ((step === 'edit' || step === 'embed') && nextResolved) {
|
|
414
|
+
// Always remount Edit/Embed on the published URL — skip-reload left the previous
|
|
415
|
+
// live version in the iframe after auto-publish on Create.
|
|
416
|
+
void forceReloadPreviewIframe(nextResolved);
|
|
402
417
|
} else {
|
|
403
418
|
switchPreviewUrlForStep(step, { forceReload: crossingPreviewFamily });
|
|
404
419
|
}
|
|
@@ -422,7 +437,7 @@
|
|
|
422
437
|
/** Resolve the canonical preview URL for a workflow step (without cache buster). */
|
|
423
438
|
function resolvePreviewUrlForStep(step: WidgetStep): string | null {
|
|
424
439
|
if (step === 'create') {
|
|
425
|
-
return dynamicWorkerPreviewUrl;
|
|
440
|
+
return pinnedPublishedPreviewUrl || dynamicWorkerPreviewUrl;
|
|
426
441
|
}
|
|
427
442
|
// Use published URL unless probe already confirmed it missing.
|
|
428
443
|
// Waiting for publishedArtifactReachable === true left Edit/Embed on the idle
|
|
@@ -454,6 +469,16 @@
|
|
|
454
469
|
}, 3000);
|
|
455
470
|
}
|
|
456
471
|
|
|
472
|
+
/** Load a published CDN artifact (Set live) even on Create — pin until next Worker build. */
|
|
473
|
+
export function showPublishedArtifact(url: string | null): void {
|
|
474
|
+
if (!url) return;
|
|
475
|
+
pinnedPublishedPreviewUrl = url;
|
|
476
|
+
publishedArtifactReachable = true;
|
|
477
|
+
_lastEditPreviewBase = null;
|
|
478
|
+
_lastEmbedPreviewBase = null;
|
|
479
|
+
void forceReloadPreviewIframe(url);
|
|
480
|
+
}
|
|
481
|
+
|
|
457
482
|
export function finishCompositionHydration(): void {
|
|
458
483
|
if (compositionHydrationTimeout) {
|
|
459
484
|
clearTimeout(compositionHydrationTimeout);
|
|
@@ -532,9 +557,12 @@
|
|
|
532
557
|
}
|
|
533
558
|
directPreviewUrl = toPreviewDisplayUrl(fetchUrl);
|
|
534
559
|
let nextFull = fetchUrl;
|
|
560
|
+
const isImmutableCdnUrl = /cdn\.widgetic\.com/i.test(fetchUrl) && /\/v\d+\//i.test(fetchUrl);
|
|
535
561
|
try {
|
|
536
562
|
const parsed = new URL(fetchUrl, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
|
|
537
|
-
|
|
563
|
+
if (!isImmutableCdnUrl || cacheBuster !== undefined) {
|
|
564
|
+
parsed.searchParams.set('_t', (cacheBuster ?? Date.now()).toString());
|
|
565
|
+
}
|
|
538
566
|
nextFull = parsed.toString();
|
|
539
567
|
} catch {
|
|
540
568
|
nextFull = fetchUrl;
|
|
@@ -666,8 +694,7 @@
|
|
|
666
694
|
const editBase = editSource ? normalizePreviewBaseUrl(toPreviewFetchUrl(editSource)) : null;
|
|
667
695
|
if (editBase && editBase !== _lastEditPreviewBase) {
|
|
668
696
|
_lastEditPreviewBase = editBase;
|
|
669
|
-
|
|
670
|
-
switchPreviewUrlForStep('edit', { forceReload: !_stepSwitchInProgress });
|
|
697
|
+
void forceReloadPreviewIframe(editSource);
|
|
671
698
|
}
|
|
672
699
|
}
|
|
673
700
|
|
|
@@ -753,7 +780,7 @@
|
|
|
753
780
|
export function markCodegenPreviewFresh(): void {
|
|
754
781
|
codegenPreviewFreshUntil = Date.now() + 30_000;
|
|
755
782
|
widgetNotFoundAutoRebuildAttempts = 1;
|
|
756
|
-
console.log('[WidgetDetails] Codegen preview marked fresh — skipping GitLab rebuilds for 30s');
|
|
783
|
+
// console.log('[WidgetDetails] Codegen preview marked fresh — skipping GitLab rebuilds for 30s');
|
|
757
784
|
}
|
|
758
785
|
|
|
759
786
|
/** True when the iframe is already showing this widget's Worker preview (not about:blank / error). */
|
|
@@ -812,6 +839,8 @@
|
|
|
812
839
|
|
|
813
840
|
function handleWidgetConsoleMessage(event: MessageEvent) {
|
|
814
841
|
if (!event.data || typeof event.data !== 'object') return;
|
|
842
|
+
const activeWin = previewIframe?.contentWindow;
|
|
843
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
815
844
|
const { type, level, message, args } = event.data;
|
|
816
845
|
if (type !== 'widgetic:console') return;
|
|
817
846
|
const entry: ConsoleLogEntry = {
|
|
@@ -869,19 +898,19 @@
|
|
|
869
898
|
$: detailsPanelTopOffset = panelTopOffset ?? legacyTopOffset;
|
|
870
899
|
$: detailsPanelBottomMargin = panelBottomMargin ?? legacyBottomMargin;
|
|
871
900
|
|
|
872
|
-
const DETAILS_PANEL_DEFAULT_WIDTH =
|
|
873
|
-
const DETAILS_PANEL_MAX_WIDTH =
|
|
901
|
+
const DETAILS_PANEL_DEFAULT_WIDTH = 1000;
|
|
902
|
+
const DETAILS_PANEL_MAX_WIDTH = 1200;
|
|
874
903
|
const DETAILS_PANEL_MAX_HEIGHT = 800;
|
|
875
|
-
const DETAILS_PANEL_MIN_WIDTH =
|
|
904
|
+
const DETAILS_PANEL_MIN_WIDTH = 480;
|
|
876
905
|
const DETAILS_PANEL_MIN_HEIGHT = 400;
|
|
877
906
|
const DETAILS_PANEL_FILL_INSET = 16;
|
|
878
907
|
const DETAILS_PANEL_SHAPE_GAP = 12;
|
|
879
|
-
/** Left column share of the two-col layout (chat / PropsEditor). */
|
|
880
|
-
const LEFT_COL_DEFAULT_PERCENT =
|
|
908
|
+
/** Left column share of the two-col layout (chat / PropsEditor). Preview gets the rest. */
|
|
909
|
+
const LEFT_COL_DEFAULT_PERCENT = 50;
|
|
881
910
|
const LEFT_COL_MIN_PERCENT = 32;
|
|
882
|
-
const LEFT_COL_MAX_PERCENT =
|
|
911
|
+
const LEFT_COL_MAX_PERCENT = 62;
|
|
883
912
|
const COL_RESIZER_PX = 10;
|
|
884
|
-
const PREVIEW_COL_MIN_PX =
|
|
913
|
+
const PREVIEW_COL_MIN_PX = 420;
|
|
885
914
|
/** Chat-first layout: preview column starts collapsed and opens on generate. */
|
|
886
915
|
let previewColumnCollapsed = true;
|
|
887
916
|
let lastAutoExpandForGenerating = false;
|
|
@@ -924,36 +953,32 @@
|
|
|
924
953
|
|
|
925
954
|
function collapsePreviewColumn() {
|
|
926
955
|
previewColumnCollapsed = true;
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}
|
|
932
|
-
console.log('[WidgetDetails] Preview column minimized, restored width', detailsPanelWidth);
|
|
956
|
+
widthBeforePreviewExpand = 0;
|
|
957
|
+
leftColPercent = LEFT_COL_DEFAULT_PERCENT;
|
|
958
|
+
detailsPanelWidth = DETAILS_PANEL_MIN_WIDTH;
|
|
959
|
+
console.log('[WidgetDetails] Close Preview — panel width', detailsPanelWidth);
|
|
933
960
|
}
|
|
934
961
|
|
|
935
962
|
function expandPreviewColumn() {
|
|
936
963
|
if (!previewColumnCollapsed) return;
|
|
937
|
-
widthBeforePreviewExpand = detailsPanelWidth;
|
|
938
964
|
previewColumnCollapsed = false;
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
leftColPercent = clampLeftColPercent((widthBeforePreviewExpand / usable) * 100);
|
|
946
|
-
console.log('[WidgetDetails] Preview column expanded', {
|
|
947
|
-
from: widthBeforePreviewExpand,
|
|
948
|
-
to: detailsPanelWidth,
|
|
949
|
-
leftColPercent,
|
|
950
|
-
});
|
|
965
|
+
leftColPercent = clampLeftColPercent(LEFT_COL_DEFAULT_PERCENT);
|
|
966
|
+
detailsPanelWidth = clampPanelWidth(DETAILS_PANEL_DEFAULT_WIDTH);
|
|
967
|
+
if (typeof window !== 'undefined' && detailsPanelX + detailsPanelWidth > window.innerWidth - 8) {
|
|
968
|
+
detailsPanelX = Math.max(8, window.innerWidth - 8 - detailsPanelWidth);
|
|
969
|
+
}
|
|
970
|
+
console.log('[WidgetDetails] Open Preview — panel width', detailsPanelWidth);
|
|
951
971
|
tick().then(() => requestAnimationFrame(() => fitPreviewToArea()));
|
|
952
972
|
}
|
|
953
973
|
|
|
954
974
|
let detailsPanelX = -1;
|
|
955
975
|
let detailsPanelY = -1;
|
|
956
976
|
let isDraggingDetails = false;
|
|
977
|
+
let pendingDetailsDrag = false;
|
|
978
|
+
let detailsDragBarEl: HTMLElement | null = null;
|
|
979
|
+
let widgetDetailsNameCtEl: HTMLElement | null = null;
|
|
980
|
+
let widgetDetailsStepsRowEl: HTMLElement | null = null;
|
|
981
|
+
let headerNameMaxWidthPx = 96;
|
|
957
982
|
let detailsDragStartX = 0;
|
|
958
983
|
let detailsDragStartY = 0;
|
|
959
984
|
let detailsInitialX = 0;
|
|
@@ -998,7 +1023,7 @@
|
|
|
998
1023
|
const pageOrigin = typeof window !== 'undefined' ? window.location.origin : '';
|
|
999
1024
|
const isApiProxy = !!(apiBaseUrl && fetchUrl.startsWith(apiBaseUrl));
|
|
1000
1025
|
if (pageOrigin && absolute.origin !== pageOrigin && !isApiProxy) {
|
|
1001
|
-
console.log('[WidgetDetails] Skipping cross-origin HEAD probe (CORS); loading in iframe:', absolute.origin);
|
|
1026
|
+
// console.log('[WidgetDetails] Skipping cross-origin HEAD probe (CORS); loading in iframe:', absolute.origin);
|
|
1002
1027
|
return true;
|
|
1003
1028
|
}
|
|
1004
1029
|
} catch {
|
|
@@ -1057,11 +1082,11 @@
|
|
|
1057
1082
|
if (iframeSrc && previewLoadedForWidgetId === widgetId) {
|
|
1058
1083
|
previewBuildDeferred = false;
|
|
1059
1084
|
previewLoading = false;
|
|
1060
|
-
console.log('[WidgetDetails] Preview already loaded, skipping pipeline:', widgetId.substring(0, 8));
|
|
1085
|
+
// console.log('[WidgetDetails] Preview already loaded, skipping pipeline:', widgetId.substring(0, 8));
|
|
1061
1086
|
return;
|
|
1062
1087
|
}
|
|
1063
1088
|
if (shouldDeferPreviewNetworkWork()) {
|
|
1064
|
-
console.log('[WidgetDetails] Deferring preview load (background panel):', widgetId.substring(0, 8));
|
|
1089
|
+
// console.log('[WidgetDetails] Deferring preview load (background panel):', widgetId.substring(0, 8));
|
|
1065
1090
|
previewBuildDeferred = true;
|
|
1066
1091
|
previewLoading = false;
|
|
1067
1092
|
previewError = null;
|
|
@@ -1092,10 +1117,10 @@
|
|
|
1092
1117
|
}
|
|
1093
1118
|
|
|
1094
1119
|
if (probeResult === 'not-found') {
|
|
1095
|
-
console.log('[WidgetDetails] Widget not on Worker — published artifact or build-from-repo', {
|
|
1096
|
-
widgetId: capturedWidgetId.substring(0, 8),
|
|
1097
|
-
hasJwt: !!getAuthJwt(),
|
|
1098
|
-
});
|
|
1120
|
+
// console.log('[WidgetDetails] Widget not on Worker — published artifact or build-from-repo', {
|
|
1121
|
+
// widgetId: capturedWidgetId.substring(0, 8),
|
|
1122
|
+
// hasJwt: !!getAuthJwt(),
|
|
1123
|
+
// });
|
|
1099
1124
|
if (isGeneratingCode) {
|
|
1100
1125
|
previewLoading = true;
|
|
1101
1126
|
previewError = null;
|
|
@@ -1108,13 +1133,13 @@
|
|
|
1108
1133
|
if (!hasRepo) {
|
|
1109
1134
|
const fallbackUrl = publishedHtmlUrlFromJsPath(widgetJsPath);
|
|
1110
1135
|
if (fallbackUrl) {
|
|
1111
|
-
console.log('[WidgetDetails] No repo yet — loading js_path artifact', fallbackUrl);
|
|
1136
|
+
// console.log('[WidgetDetails] No repo yet — loading js_path artifact', fallbackUrl);
|
|
1112
1137
|
previewError = null;
|
|
1113
1138
|
applyPreviewUrlToIframe(fallbackUrl);
|
|
1114
1139
|
previewLoading = false;
|
|
1115
1140
|
return;
|
|
1116
1141
|
}
|
|
1117
|
-
console.log('[WidgetDetails] No repository yet — skipping build-from-repo');
|
|
1142
|
+
// console.log('[WidgetDetails] No repository yet — skipping build-from-repo');
|
|
1118
1143
|
previewLoading = false;
|
|
1119
1144
|
previewError = 'NO_CODE_YET';
|
|
1120
1145
|
return;
|
|
@@ -1127,7 +1152,7 @@
|
|
|
1127
1152
|
directPreviewUrl = workerPreviewUrl;
|
|
1128
1153
|
previewLoading = true;
|
|
1129
1154
|
switchPreviewUrlForStep(currentStep);
|
|
1130
|
-
console.log('[WidgetDetails] Loading preview iframe:', workerPreviewUrl, 'step:', currentStep);
|
|
1155
|
+
// console.log('[WidgetDetails] Loading preview iframe:', workerPreviewUrl, 'step:', currentStep);
|
|
1131
1156
|
requestAnimationFrame(() => {
|
|
1132
1157
|
setTimeout(fitPreviewToArea, 150);
|
|
1133
1158
|
});
|
|
@@ -1139,7 +1164,7 @@
|
|
|
1139
1164
|
if (!previewBuildDeferred || !selectedWidgetId) return;
|
|
1140
1165
|
if (iframeSrc && previewLoadedForWidgetId === selectedWidgetId) {
|
|
1141
1166
|
previewBuildDeferred = false;
|
|
1142
|
-
console.log('[WidgetDetails] Skip deferred preview — already loaded for', selectedWidgetId.substring(0, 8));
|
|
1167
|
+
// console.log('[WidgetDetails] Skip deferred preview — already loaded for', selectedWidgetId.substring(0, 8));
|
|
1143
1168
|
return;
|
|
1144
1169
|
}
|
|
1145
1170
|
previewBuildDeferred = false;
|
|
@@ -1199,18 +1224,18 @@
|
|
|
1199
1224
|
|
|
1200
1225
|
await tick();
|
|
1201
1226
|
|
|
1202
|
-
console.log('WidgetDetails: loadWidget completed:', {
|
|
1203
|
-
widgetId,
|
|
1204
|
-
name: selectedWidget?.name,
|
|
1205
|
-
currentRepositoryId,
|
|
1206
|
-
});
|
|
1227
|
+
// console.log('WidgetDetails: loadWidget completed:', {
|
|
1228
|
+
// widgetId,
|
|
1229
|
+
// name: selectedWidget?.name,
|
|
1230
|
+
// currentRepositoryId,
|
|
1231
|
+
// });
|
|
1207
1232
|
|
|
1208
1233
|
dispatch('widgetReady', { widgetId: selectedWidgetId });
|
|
1209
1234
|
|
|
1210
1235
|
// If skipBasePreview is set, the caller will handle compiling via triggerBuildFromRepo
|
|
1211
1236
|
// (e.g. duplicates waiting for GitLab fork, or converts waiting for code generation)
|
|
1212
1237
|
if (skipBasePreview) {
|
|
1213
|
-
console.log('[WidgetDetails] skipBasePreview is set — deferring preview load to caller');
|
|
1238
|
+
// console.log('[WidgetDetails] skipBasePreview is set — deferring preview load to caller');
|
|
1214
1239
|
previewLoading = true;
|
|
1215
1240
|
previewError = null;
|
|
1216
1241
|
return;
|
|
@@ -1222,11 +1247,11 @@
|
|
|
1222
1247
|
|
|
1223
1248
|
const hasRepo = !!(normalizeRepositoryId(selectedWidget) || currentRepositoryId);
|
|
1224
1249
|
if (!lastCommitId && lastPublishedVersion === null && !widgetJsPath && !hasRepo) {
|
|
1225
|
-
console.log('[WidgetDetails] No code yet — skipping Worker probe/build-from-repo', {
|
|
1226
|
-
widgetId: widgetId?.substring(0, 8),
|
|
1227
|
-
hasRepo,
|
|
1228
|
-
repositorySetupStatus,
|
|
1229
|
-
});
|
|
1250
|
+
// console.log('[WidgetDetails] No code yet — skipping Worker probe/build-from-repo', {
|
|
1251
|
+
// widgetId: widgetId?.substring(0, 8),
|
|
1252
|
+
// hasRepo,
|
|
1253
|
+
// repositorySetupStatus,
|
|
1254
|
+
// });
|
|
1230
1255
|
previewLoading = false;
|
|
1231
1256
|
previewError = 'NO_CODE_YET';
|
|
1232
1257
|
return;
|
|
@@ -1251,7 +1276,7 @@
|
|
|
1251
1276
|
|
|
1252
1277
|
export async function refreshPreview(opts: { withRetry?: boolean; skipIfAlreadyShowing?: boolean } = {}): Promise<void> {
|
|
1253
1278
|
if (opts.skipIfAlreadyShowing && hasLiveWorkerPreview()) {
|
|
1254
|
-
console.log('[WidgetDetails] refreshPreview skipped — Worker preview already showing');
|
|
1279
|
+
// console.log('[WidgetDetails] refreshPreview skipped — Worker preview already showing');
|
|
1255
1280
|
previewLoading = false;
|
|
1256
1281
|
previewHydrating = false;
|
|
1257
1282
|
return;
|
|
@@ -1275,11 +1300,11 @@
|
|
|
1275
1300
|
middleware: cfg.middleware,
|
|
1276
1301
|
headers: cfg.headers,
|
|
1277
1302
|
});
|
|
1278
|
-
console.log('[WidgetDetails] buildWidgetFromRepo', {
|
|
1279
|
-
widgetId: widgetId.substring(0, 8),
|
|
1280
|
-
builderBasePath,
|
|
1281
|
-
sdkPath: '/v1/agent/widget-builder/build-from-repo/{widgetId}',
|
|
1282
|
-
});
|
|
1303
|
+
// console.log('[WidgetDetails] buildWidgetFromRepo', {
|
|
1304
|
+
// widgetId: widgetId.substring(0, 8),
|
|
1305
|
+
// builderBasePath,
|
|
1306
|
+
// sdkPath: '/v1/agent/widget-builder/build-from-repo/{widgetId}',
|
|
1307
|
+
// });
|
|
1283
1308
|
return builder.buildWidgetFromRepo({ widgetId });
|
|
1284
1309
|
}
|
|
1285
1310
|
|
|
@@ -1287,13 +1312,13 @@
|
|
|
1287
1312
|
const forceRebuild = options?.force === true;
|
|
1288
1313
|
if (!selectedWidgetId) return;
|
|
1289
1314
|
if (shouldDeferPreviewNetworkWork(forceRebuild)) {
|
|
1290
|
-
console.log('[WidgetDetails] triggerBuildFromRepo deferred (background panel)');
|
|
1315
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo deferred (background panel)');
|
|
1291
1316
|
previewBuildDeferred = true;
|
|
1292
1317
|
return;
|
|
1293
1318
|
}
|
|
1294
1319
|
const hasRepo = !!(normalizeRepositoryId(selectedWidget) || currentRepositoryId);
|
|
1295
1320
|
if (!hasRepo) {
|
|
1296
|
-
console.log('[WidgetDetails] triggerBuildFromRepo skipped — widget has no repository yet');
|
|
1321
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo skipped — widget has no repository yet');
|
|
1297
1322
|
previewLoading = false;
|
|
1298
1323
|
previewError = 'NO_CODE_YET';
|
|
1299
1324
|
return;
|
|
@@ -1301,6 +1326,7 @@
|
|
|
1301
1326
|
const capturedWidgetId = selectedWidgetId;
|
|
1302
1327
|
const runId = ++buildFromRepoRunId;
|
|
1303
1328
|
if (forceRebuild) isRebuildingPreview = true;
|
|
1329
|
+
pinnedPublishedPreviewUrl = null;
|
|
1304
1330
|
try {
|
|
1305
1331
|
const workerPreviewUrl = buildWorkerPreviewUrl(capturedWidgetId);
|
|
1306
1332
|
const workerPreviewDisplayUrl = buildWorkerPreviewUrl(capturedWidgetId, { forDisplay: true });
|
|
@@ -1309,7 +1335,7 @@
|
|
|
1309
1335
|
const freshProbe = await probeWorkerReachable(workerPreviewUrl);
|
|
1310
1336
|
if (selectedWidgetId !== capturedWidgetId || runId !== buildFromRepoRunId) return;
|
|
1311
1337
|
if (freshProbe === 'ok') {
|
|
1312
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: skipping GitLab rebuild — codegen preview is fresh');
|
|
1338
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: skipping GitLab rebuild — codegen preview is fresh');
|
|
1313
1339
|
dynamicWorkerPreviewUrl = workerPreviewUrl;
|
|
1314
1340
|
directPreviewUrl = workerPreviewDisplayUrl;
|
|
1315
1341
|
inlinePanelView = 'preview';
|
|
@@ -1349,7 +1375,7 @@
|
|
|
1349
1375
|
const probeResult = await probeWorkerReachable(workerPreviewUrl);
|
|
1350
1376
|
if (isStaleRun()) return;
|
|
1351
1377
|
if (!forceRebuild && probeResult === 'ok') {
|
|
1352
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: Worker already has widget, loading preview directly');
|
|
1378
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: Worker already has widget, loading preview directly');
|
|
1353
1379
|
dispatch('buildFromRepoSuccess', { widgetId: capturedWidgetId });
|
|
1354
1380
|
loadPreviewFromWorker();
|
|
1355
1381
|
return;
|
|
@@ -1363,7 +1389,7 @@
|
|
|
1363
1389
|
|
|
1364
1390
|
// Skip build-from-repo while codegen is running — WS / refreshPreview will load when ready
|
|
1365
1391
|
if (isGeneratingCode) {
|
|
1366
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: codegen in progress — deferring build-from-repo');
|
|
1392
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: codegen in progress — deferring build-from-repo');
|
|
1367
1393
|
previewLoading = true;
|
|
1368
1394
|
previewError = null;
|
|
1369
1395
|
return;
|
|
@@ -1381,7 +1407,7 @@
|
|
|
1381
1407
|
const buildData = await requestBuildFromRepo(capturedWidgetId);
|
|
1382
1408
|
if (isStaleRun()) return;
|
|
1383
1409
|
|
|
1384
|
-
console.log('[WidgetDetails] triggerBuildFromRepo succeeded', { buildTimeMs: buildData?.data?.buildTimeMs ?? buildData?.buildTimeMs, attempt });
|
|
1410
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo succeeded', { buildTimeMs: buildData?.data?.buildTimeMs ?? buildData?.buildTimeMs, attempt });
|
|
1385
1411
|
previewError = null;
|
|
1386
1412
|
previewLoading = false;
|
|
1387
1413
|
dispatch('widgetDataChanged', { widgetId: capturedWidgetId });
|
|
@@ -1409,7 +1435,7 @@
|
|
|
1409
1435
|
lastBuildErrorMsg = sanitizePreviewBuildError(lastBuildErrorMsg);
|
|
1410
1436
|
|
|
1411
1437
|
if (buildErrorCode === 'NO_CODE_YET') {
|
|
1412
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: repo exists but has no code yet — skipping retries');
|
|
1438
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: repo exists but has no code yet — skipping retries');
|
|
1413
1439
|
lastBuildErrorMsg = 'NO_CODE_YET';
|
|
1414
1440
|
break;
|
|
1415
1441
|
}
|
|
@@ -1417,7 +1443,7 @@
|
|
|
1417
1443
|
const status = buildErr?.response?.status ?? buildErr?.status;
|
|
1418
1444
|
const isRetryable = status === 404 || status === 500 || !status;
|
|
1419
1445
|
if (isRetryable && attempt < maxRetries) {
|
|
1420
|
-
console.log(`[WidgetDetails] triggerBuildFromRepo attempt ${attempt}/${maxRetries} failed (${lastBuildErrorMsg}), retrying in ${retryDelayMs}ms...`);
|
|
1446
|
+
// console.log(`[WidgetDetails] triggerBuildFromRepo attempt ${attempt}/${maxRetries} failed (${lastBuildErrorMsg}), retrying in ${retryDelayMs}ms...`);
|
|
1421
1447
|
await new Promise(r => setTimeout(r, retryDelayMs));
|
|
1422
1448
|
continue;
|
|
1423
1449
|
}
|
|
@@ -1439,14 +1465,14 @@
|
|
|
1439
1465
|
|
|
1440
1466
|
// Preview may have loaded via codegen/WS while build-from-repo was in flight
|
|
1441
1467
|
if (allowStalePreviewRecovery && iframeSrc && !previewHasRuntimeError) {
|
|
1442
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: build failed but preview iframe already loaded — keeping preview');
|
|
1468
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: build failed but preview iframe already loaded — keeping preview');
|
|
1443
1469
|
previewError = null;
|
|
1444
1470
|
previewLoading = false;
|
|
1445
1471
|
return;
|
|
1446
1472
|
}
|
|
1447
1473
|
|
|
1448
1474
|
if (allowStalePreviewRecovery && (await tryRecoverPreviewFromWorker())) {
|
|
1449
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: recovered preview from Worker after build-from-repo failure');
|
|
1475
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: recovered preview from Worker after build-from-repo failure');
|
|
1450
1476
|
return;
|
|
1451
1477
|
}
|
|
1452
1478
|
|
|
@@ -1454,7 +1480,7 @@
|
|
|
1454
1480
|
// For published widgets, try loading via API proxy (handles both R2 and CDN)
|
|
1455
1481
|
const sourceArtifactUrl = publishedHtmlUrlFromJsPath(widgetJsPath);
|
|
1456
1482
|
if (sourceArtifactUrl) {
|
|
1457
|
-
console.log('[WidgetDetails] triggerBuildFromRepo: using js_path artifact (not new-id proxy)', sourceArtifactUrl);
|
|
1483
|
+
// console.log('[WidgetDetails] triggerBuildFromRepo: using js_path artifact (not new-id proxy)', sourceArtifactUrl);
|
|
1458
1484
|
const reachable = await probePublishedArtifactUrl(sourceArtifactUrl);
|
|
1459
1485
|
if (reachable) {
|
|
1460
1486
|
previewError = null;
|
|
@@ -1586,7 +1612,7 @@
|
|
|
1586
1612
|
/** Duplicate/fork fallback: keep published iframe if it loaded; otherwise compile from repo. */
|
|
1587
1613
|
export async function retryPreviewIfEmpty(): Promise<void> {
|
|
1588
1614
|
if (iframeSrc && !previewError) {
|
|
1589
|
-
console.log('[WidgetDetails] retryPreviewIfEmpty skipped — preview already loaded');
|
|
1615
|
+
// console.log('[WidgetDetails] retryPreviewIfEmpty skipped — preview already loaded');
|
|
1590
1616
|
return;
|
|
1591
1617
|
}
|
|
1592
1618
|
previewLoading = true;
|
|
@@ -1595,7 +1621,7 @@
|
|
|
1595
1621
|
if (await loadPublishedPreviewForCurrentStep()) return;
|
|
1596
1622
|
const hasRepo = !!(normalizeRepositoryId(selectedWidget) || currentRepositoryId);
|
|
1597
1623
|
if (hasRepo) {
|
|
1598
|
-
console.log('[WidgetDetails] retryPreviewIfEmpty — compiling from repository');
|
|
1624
|
+
// console.log('[WidgetDetails] retryPreviewIfEmpty — compiling from repository');
|
|
1599
1625
|
await triggerBuildFromRepo();
|
|
1600
1626
|
return;
|
|
1601
1627
|
}
|
|
@@ -1664,10 +1690,9 @@
|
|
|
1664
1690
|
// Widget is alive — never keep a runtime-error overlay over a working preview.
|
|
1665
1691
|
clearPendingRuntimeErrorOverlay();
|
|
1666
1692
|
if (previewHasRuntimeError) {
|
|
1667
|
-
console.log('[WidgetDetails] Clearing runtime error overlay — widgetic:ready');
|
|
1668
1693
|
previewHasRuntimeError = false;
|
|
1669
1694
|
previewRuntimeErrorStatus = null;
|
|
1670
|
-
if (previewError && /runtime error|HTTP 500|compilation/i.test(previewError)) {
|
|
1695
|
+
if (previewError && /did not start|runtime error|HTTP 500|compilation/i.test(previewError)) {
|
|
1671
1696
|
previewError = null;
|
|
1672
1697
|
}
|
|
1673
1698
|
notifyPreviewCompileErrorChanged();
|
|
@@ -1684,6 +1709,17 @@
|
|
|
1684
1709
|
return typeof value === 'string' && /<[a-z][\s\S]*>/i.test(value);
|
|
1685
1710
|
}
|
|
1686
1711
|
|
|
1712
|
+
function contentOverlaySelectors(key: string): string {
|
|
1713
|
+
const exact = `[data-content="${CSS.escape(key)}"]`;
|
|
1714
|
+
if (/subtitle/i.test(key)) {
|
|
1715
|
+
return `${exact}, [data-content="widgetSubtitle"], [data-content="gallerySubtitle"], [data-content="subtitle"], .widget-subtitle, .gallery-subtitle`;
|
|
1716
|
+
}
|
|
1717
|
+
if (/^(widgetTitle|galleryTitle|title|heading)$/i.test(key)) {
|
|
1718
|
+
return `${exact}, [data-content="widgetTitle"], [data-content="galleryTitle"], h1, .widget-title`;
|
|
1719
|
+
}
|
|
1720
|
+
return exact;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1687
1723
|
/** Same-origin preview iframes (Vite /__widget_builder__ proxy): restore bold/italic after widgets set textContent. */
|
|
1688
1724
|
function applyRichTextInPreviewIframe(message: Record<string, unknown>): void {
|
|
1689
1725
|
if (message.type !== 'widgetic:update') return;
|
|
@@ -1694,10 +1730,16 @@
|
|
|
1694
1730
|
const content = message.content as Record<string, unknown> | undefined;
|
|
1695
1731
|
if (content) {
|
|
1696
1732
|
for (const [key, value] of Object.entries(content)) {
|
|
1697
|
-
if (
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
el
|
|
1733
|
+
if (typeof value !== 'string') continue;
|
|
1734
|
+
const selectors = contentOverlaySelectors(key);
|
|
1735
|
+
doc.querySelectorAll(selectors).forEach((el) => {
|
|
1736
|
+
if (el instanceof HTMLImageElement) {
|
|
1737
|
+
el.src = value;
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLAnchorElement) return;
|
|
1741
|
+
if (looksLikeHtmlMarkup(value)) el.innerHTML = value;
|
|
1742
|
+
else el.textContent = value;
|
|
1701
1743
|
});
|
|
1702
1744
|
}
|
|
1703
1745
|
}
|
|
@@ -1737,25 +1779,63 @@
|
|
|
1737
1779
|
}
|
|
1738
1780
|
|
|
1739
1781
|
function ensureGoogleFontInPreviewDoc(doc: Document, family: string): void {
|
|
1740
|
-
if (!family || /^(
|
|
1782
|
+
if (!family || /^(system-ui|sans-serif|serif)$/i.test(family)) return;
|
|
1741
1783
|
const id = `widgetic-gf-${family.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}`;
|
|
1742
1784
|
if (doc.getElementById(id)) return;
|
|
1743
1785
|
const link = doc.createElement('link');
|
|
1744
1786
|
link.id = id;
|
|
1745
1787
|
link.rel = 'stylesheet';
|
|
1746
|
-
link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap`;
|
|
1788
|
+
link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap`;
|
|
1747
1789
|
doc.head?.appendChild(link);
|
|
1748
|
-
console.log('[WidgetDetails] Loaded Google Font in preview iframe:', family);
|
|
1790
|
+
// console.log('[WidgetDetails] Loaded Google Font in preview iframe:', family);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
function parseOverlayFontFace(weight: string | undefined): { cssWeight: string; cssStyle: string } {
|
|
1794
|
+
const raw = String(weight || 'Regular');
|
|
1795
|
+
const cssStyle = /italic|oblique/i.test(raw) ? 'italic' : 'normal';
|
|
1796
|
+
const name = raw.toLowerCase().replace(/italic|oblique/g, '').replace(/[\s_-]/g, '');
|
|
1797
|
+
const table: Record<string, string> = {
|
|
1798
|
+
thin: '100', hairline: '100',
|
|
1799
|
+
extralight: '200', ultralight: '200',
|
|
1800
|
+
light: '300',
|
|
1801
|
+
regular: '400', normal: '400', '': '400',
|
|
1802
|
+
medium: '500',
|
|
1803
|
+
semibold: '600', demibold: '600',
|
|
1804
|
+
bold: '700',
|
|
1805
|
+
extrabold: '800', ultrabold: '800',
|
|
1806
|
+
black: '900', heavy: '900',
|
|
1807
|
+
};
|
|
1808
|
+
if (/^\d{3}$/.test(name)) return { cssWeight: name, cssStyle };
|
|
1809
|
+
return { cssWeight: table[name] || '400', cssStyle };
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
function flattenDesignOverlay(raw: Record<string, unknown> | undefined): Record<string, string> | null {
|
|
1813
|
+
if (!raw) return null;
|
|
1814
|
+
const out: Record<string, string> = {};
|
|
1815
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1816
|
+
if (value && typeof value === 'object' && 'family' in (value as Record<string, unknown>)) {
|
|
1817
|
+
const font = value as { family?: string; weight?: string };
|
|
1818
|
+
const family = String(font.family || 'Inter');
|
|
1819
|
+
const face = parseOverlayFontFace(font.weight);
|
|
1820
|
+
out[key] = `"${family}", sans-serif`;
|
|
1821
|
+
out[`${key}Family`] = family;
|
|
1822
|
+
out[`${key}Weight`] = face.cssWeight;
|
|
1823
|
+
out[`${key}Style`] = face.cssStyle;
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (typeof value === 'string' && value) out[key] = value;
|
|
1827
|
+
}
|
|
1828
|
+
return Object.keys(out).length ? out : null;
|
|
1749
1829
|
}
|
|
1750
1830
|
|
|
1751
1831
|
/** Same-origin preview: apply CSS vars, Google Fonts, title/subtitle, tile aspect-ratio. */
|
|
1752
1832
|
function applyDesignOverlayInPreviewIframe(message: Record<string, unknown>): void {
|
|
1753
1833
|
if (message.type !== 'widgetic:update') return;
|
|
1754
|
-
const design = message.design as Record<string,
|
|
1834
|
+
const design = flattenDesignOverlay(message.design as Record<string, unknown> | undefined);
|
|
1755
1835
|
if (!design) return;
|
|
1756
1836
|
const doc = previewIframe?.contentDocument;
|
|
1757
1837
|
if (!doc?.documentElement) {
|
|
1758
|
-
console.log('[WidgetDetails] Design overlay skipped — preview iframe not same-origin');
|
|
1838
|
+
// console.log('[WidgetDetails] Design overlay skipped — preview iframe not same-origin');
|
|
1759
1839
|
return;
|
|
1760
1840
|
}
|
|
1761
1841
|
const apply = () => {
|
|
@@ -1776,46 +1856,69 @@
|
|
|
1776
1856
|
}
|
|
1777
1857
|
families.forEach((family) => ensureGoogleFontInPreviewDoc(doc, family));
|
|
1778
1858
|
|
|
1779
|
-
const
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
extractCssFontFamily(design.subtitleFont) ||
|
|
1788
|
-
design.descriptionFontFamily ||
|
|
1789
|
-
extractCssFontFamily(design.descriptionFont);
|
|
1790
|
-
const subtitleWeight = design.subtitleFontWeight || design.descriptionFontWeight || '';
|
|
1791
|
-
|
|
1792
|
-
if (titleFamily || titleWeight) {
|
|
1793
|
-
const titleStack = titleFamily ? `"${titleFamily}", sans-serif` : '';
|
|
1794
|
-
doc.querySelectorAll(
|
|
1795
|
-
'h1, h2, h3, .widget-title, [data-content="title"], [data-content="widgetTitle"], [data-content="heading"]'
|
|
1796
|
-
).forEach((el) => {
|
|
1797
|
-
if (!(el instanceof HTMLElement)) return;
|
|
1798
|
-
if (titleStack) el.style.setProperty('font-family', titleStack, 'important');
|
|
1799
|
-
if (titleWeight) el.style.setProperty('font-weight', titleWeight, 'important');
|
|
1859
|
+
const applyFace = (family: string, weight: string, style: string, selectors: string) => {
|
|
1860
|
+
if (!family && !weight && !style) return;
|
|
1861
|
+
const stack = family ? `"${family}", sans-serif` : '';
|
|
1862
|
+
doc.querySelectorAll(selectors).forEach((node) => {
|
|
1863
|
+
if (!(node instanceof HTMLElement)) return;
|
|
1864
|
+
if (stack) node.style.setProperty('font-family', stack, 'important');
|
|
1865
|
+
if (weight) node.style.setProperty('font-weight', weight, 'important');
|
|
1866
|
+
if (style) node.style.setProperty('font-style', style, 'important');
|
|
1800
1867
|
});
|
|
1868
|
+
};
|
|
1869
|
+
|
|
1870
|
+
const cssChunks: string[] = [];
|
|
1871
|
+
const fontPrefixes = new Set<string>();
|
|
1872
|
+
for (const key of Object.keys(design)) {
|
|
1873
|
+
const match = key.match(/^(.*)Font(?:Family|Weight|Style)?$/i);
|
|
1874
|
+
if (match) fontPrefixes.add(match[1] || '');
|
|
1801
1875
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
const
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1876
|
+
|
|
1877
|
+
for (const prefix of fontPrefixes) {
|
|
1878
|
+
const family =
|
|
1879
|
+
design[`${prefix}FontFamily`] ||
|
|
1880
|
+
extractCssFontFamily(design[`${prefix}Font`]) ||
|
|
1881
|
+
'';
|
|
1882
|
+
const weight = design[`${prefix}FontWeight`] || '';
|
|
1883
|
+
const style = design[`${prefix}FontStyle`] || '';
|
|
1884
|
+
const isTitleLike =
|
|
1885
|
+
/^(title|heading|tite)?$/i.test(prefix) ||
|
|
1886
|
+
/title|heading|tite/i.test(prefix);
|
|
1887
|
+
const isSubtitleLike = /subtitle|description|caption|label/i.test(prefix);
|
|
1888
|
+
const varFamily = `--${prefix}Font`;
|
|
1889
|
+
const varWeight = `--${prefix}FontWeight`;
|
|
1890
|
+
const varStyle = `--${prefix}FontStyle`;
|
|
1891
|
+
if (isSubtitleLike) {
|
|
1892
|
+
cssChunks.push(
|
|
1893
|
+
`[data-content="caption"], [data-content="subtitle"], [data-content="description"], [data-content="widgetSubtitle"], .caption-bottom p, [class*="caption"], [class*="subtitle"] { font-family: var(${varFamily}, inherit) !important; font-weight: var(${varWeight}, inherit) !important; font-style: var(${varStyle}, normal) !important; }`
|
|
1894
|
+
);
|
|
1895
|
+
applyFace(
|
|
1896
|
+
family,
|
|
1897
|
+
weight,
|
|
1898
|
+
style,
|
|
1899
|
+
'.widget-subtitle, .gallery-subtitle, .gallery-caption, .caption-bottom p, [data-content="subtitle"], [data-content="description"], [data-content="widgetSubtitle"], [data-content="caption"], [class*="caption"], [class*="subtitle"]'
|
|
1900
|
+
);
|
|
1901
|
+
} else if (isTitleLike || fontPrefixes.size === 1) {
|
|
1902
|
+
cssChunks.push(
|
|
1903
|
+
`h1, h2, h3, [data-content="widgetTitle"], [data-content="title"], [data-content="heading"], .widget-title, .gallery-title, [class*="title"] { font-family: var(${varFamily}, inherit) !important; font-weight: var(${varWeight}, inherit) !important; font-style: var(${varStyle}, normal) !important; }`
|
|
1904
|
+
);
|
|
1905
|
+
applyFace(
|
|
1906
|
+
family,
|
|
1907
|
+
weight,
|
|
1908
|
+
style,
|
|
1909
|
+
'h1, h2, h3, .widget-title, .gallery-title, [data-content="title"], [data-content="widgetTitle"], [data-content="heading"], [class*="title"]'
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
if (cssChunks.length) {
|
|
1915
|
+
let sheet = doc.getElementById('widgetic-font-overlay') as HTMLStyleElement | null;
|
|
1916
|
+
if (!sheet) {
|
|
1917
|
+
sheet = doc.createElement('style');
|
|
1918
|
+
sheet.id = 'widgetic-font-overlay';
|
|
1919
|
+
doc.head.appendChild(sheet);
|
|
1818
1920
|
}
|
|
1921
|
+
sheet.textContent = cssChunks.join('\n');
|
|
1819
1922
|
}
|
|
1820
1923
|
|
|
1821
1924
|
const aspectEntry = Object.entries(design).find(([key, value]) =>
|
|
@@ -1832,7 +1935,7 @@
|
|
|
1832
1935
|
el.style.height = 'auto';
|
|
1833
1936
|
}
|
|
1834
1937
|
});
|
|
1835
|
-
console.log('[WidgetDetails] Applied tile aspect-ratio overlay:', cssRatio);
|
|
1938
|
+
// console.log('[WidgetDetails] Applied tile aspect-ratio overlay:', cssRatio);
|
|
1836
1939
|
}
|
|
1837
1940
|
} catch (error) {
|
|
1838
1941
|
console.warn('[WidgetDetails] Design overlay skipped:', error);
|
|
@@ -1899,7 +2002,7 @@
|
|
|
1899
2002
|
|
|
1900
2003
|
function startPreviewTokenAutoRefresh() {
|
|
1901
2004
|
stopPreviewTokenAutoRefresh();
|
|
1902
|
-
console.log('[TokenAutoRefresh] Started - checking every', TOKEN_CHECK_INTERVAL_MS / 1000, 'seconds');
|
|
2005
|
+
// console.log('[TokenAutoRefresh] Started - checking every', TOKEN_CHECK_INTERVAL_MS / 1000, 'seconds');
|
|
1903
2006
|
previewTokenRefreshInterval = setInterval(async () => {
|
|
1904
2007
|
const isPreviewVisible = showPreviewModal || (showWidgetDetails && inlinePanelView === 'preview');
|
|
1905
2008
|
if (!isPreviewVisible || !lastPreviewUrlFetchTime || !directPreviewUrl) return;
|
|
@@ -2008,7 +2111,7 @@
|
|
|
2008
2111
|
const retryCount = opts._retryCount || 0;
|
|
2009
2112
|
const withRetry = opts.withRetry || false;
|
|
2010
2113
|
|
|
2011
|
-
console.log('WidgetDetails: Triggering preview refresh via src manipulation', retryCount > 0 ? `(retry ${retryCount})` : '');
|
|
2114
|
+
// console.log('WidgetDetails: Triggering preview refresh via src manipulation', retryCount > 0 ? `(retry ${retryCount})` : '');
|
|
2012
2115
|
const newCacheBuster = Date.now();
|
|
2013
2116
|
previewCacheBuster = newCacheBuster;
|
|
2014
2117
|
|
|
@@ -2018,7 +2121,7 @@
|
|
|
2018
2121
|
const workerPreviewDisplayUrl = buildWorkerPreviewUrl(selectedWidgetId, { forDisplay: true });
|
|
2019
2122
|
dynamicWorkerPreviewUrl = workerPreviewUrl;
|
|
2020
2123
|
directPreviewUrl = workerPreviewDisplayUrl;
|
|
2021
|
-
console.log('[WidgetDetails] refreshPreview: initialized worker preview URL');
|
|
2124
|
+
// console.log('[WidgetDetails] refreshPreview: initialized worker preview URL');
|
|
2022
2125
|
}
|
|
2023
2126
|
|
|
2024
2127
|
// After publish (or any time the CDN artifact may have changed), edit/embed steps must
|
|
@@ -2044,7 +2147,7 @@
|
|
|
2044
2147
|
// If not reachable and we have retry budget, retry after a short delay (CDN propagation).
|
|
2045
2148
|
if (withRetry && retryCount < 3) {
|
|
2046
2149
|
const delay = (retryCount + 1) * 2000;
|
|
2047
|
-
console.log(`[WidgetDetails] Published artifact not reachable yet, retrying in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
|
2150
|
+
// console.log(`[WidgetDetails] Published artifact not reachable yet, retrying in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
|
2048
2151
|
setTimeout(() => refreshPreviewIframe({ withRetry: true, _retryCount: retryCount + 1 }), delay);
|
|
2049
2152
|
return;
|
|
2050
2153
|
}
|
|
@@ -2073,14 +2176,14 @@
|
|
|
2073
2176
|
if (probeResult === 'not-found') {
|
|
2074
2177
|
if (withRetry && retryCount < 3) {
|
|
2075
2178
|
const delay = (retryCount + 1) * 2000;
|
|
2076
|
-
console.log(`[WidgetDetails] Preview not found, retrying in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
|
2179
|
+
// console.log(`[WidgetDetails] Preview not found, retrying in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
|
2077
2180
|
previewLoading = true;
|
|
2078
2181
|
setTimeout(() => refreshPreviewIframe({ withRetry: true, _retryCount: retryCount + 1 }), delay);
|
|
2079
2182
|
return;
|
|
2080
2183
|
}
|
|
2081
2184
|
// Worker cache miss after codegen — compile from GitLab repo
|
|
2082
2185
|
if (lastCommitId && !isGeneratingCode && !shouldDeferPreviewNetworkWork()) {
|
|
2083
|
-
console.log('[WidgetDetails] Preview not on Worker after retries — trying build-from-repo');
|
|
2186
|
+
// console.log('[WidgetDetails] Preview not on Worker after retries — trying build-from-repo');
|
|
2084
2187
|
await triggerBuildFromRepo();
|
|
2085
2188
|
if (iframeSrc) return;
|
|
2086
2189
|
}
|
|
@@ -2196,9 +2299,16 @@
|
|
|
2196
2299
|
if (!previewWidgetReady) {
|
|
2197
2300
|
if (previewReadyFallbackTimeout) clearTimeout(previewReadyFallbackTimeout);
|
|
2198
2301
|
previewReadyFallbackTimeout = setTimeout(() => {
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2302
|
+
if (previewWidgetReady) return;
|
|
2303
|
+
console.warn('[WidgetDetails] Create step: iframe loaded but widget did not signal ready');
|
|
2304
|
+
previewLoading = false;
|
|
2305
|
+
previewHydrating = false;
|
|
2306
|
+
previewHasRuntimeError = true;
|
|
2307
|
+
previewRuntimeErrorStatus = 500;
|
|
2308
|
+
previewError =
|
|
2309
|
+
'Preview loaded but the widget did not start. Open in a new tab or tap Rebuild. If the new tab is also blank, the generated code likely failed at runtime.';
|
|
2310
|
+
notifyPreviewCompileErrorChanged();
|
|
2311
|
+
}, 4000);
|
|
2202
2312
|
}
|
|
2203
2313
|
scheduleFitPreviewToArea();
|
|
2204
2314
|
}
|
|
@@ -2376,8 +2486,13 @@
|
|
|
2376
2486
|
return Math.max(DETAILS_PANEL_MIN_HEIGHT, Math.min(DETAILS_PANEL_MAX_HEIGHT, available));
|
|
2377
2487
|
}
|
|
2378
2488
|
|
|
2489
|
+
function getDetailsPanelMinWidth(): number {
|
|
2490
|
+
if (previewColumnCollapsed) return DETAILS_PANEL_MIN_WIDTH;
|
|
2491
|
+
return DETAILS_PANEL_MIN_WIDTH + COL_RESIZER_PX + 240;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2379
2494
|
function clampPanelWidth(w: number): number {
|
|
2380
|
-
return Math.max(
|
|
2495
|
+
return Math.max(getDetailsPanelMinWidth(), Math.min(w, DETAILS_PANEL_MAX_WIDTH));
|
|
2381
2496
|
}
|
|
2382
2497
|
|
|
2383
2498
|
function clampPanelHeight(h: number): number {
|
|
@@ -2420,7 +2535,14 @@
|
|
|
2420
2535
|
}
|
|
2421
2536
|
const cascade = panelCascadeIndex * 32;
|
|
2422
2537
|
detailsPanelX = Math.max(8, Math.round((window.innerWidth - detailsPanelWidth) / 2) + cascade);
|
|
2423
|
-
|
|
2538
|
+
const availableTop = detailsPanelTopOffset;
|
|
2539
|
+
detailsPanelY = Math.max(
|
|
2540
|
+
availableTop,
|
|
2541
|
+
Math.round(availableTop + (window.innerHeight - availableTop - detailsPanelHeight) / 2) + cascade,
|
|
2542
|
+
);
|
|
2543
|
+
if (detailsPanelY + detailsPanelHeight > window.innerHeight - 8) {
|
|
2544
|
+
detailsPanelY = Math.max(availableTop, window.innerHeight - detailsPanelHeight - 8);
|
|
2545
|
+
}
|
|
2424
2546
|
}
|
|
2425
2547
|
|
|
2426
2548
|
/** Keep the panel on-screen after a browser resize. Does not reset to default 600×800. */
|
|
@@ -2446,8 +2568,7 @@
|
|
|
2446
2568
|
}
|
|
2447
2569
|
|
|
2448
2570
|
/**
|
|
2449
|
-
*
|
|
2450
|
-
* shape Props panel. Can hang halfway outside the viewport.
|
|
2571
|
+
* Center Widget Details in the canvas viewport (X and Y), clamped on-screen.
|
|
2451
2572
|
*/
|
|
2452
2573
|
export function positionBesideShape(shapeRect: {
|
|
2453
2574
|
left: number;
|
|
@@ -2464,24 +2585,28 @@
|
|
|
2464
2585
|
_hasBeenOpenedOnce = true;
|
|
2465
2586
|
if (!shapeRect) {
|
|
2466
2587
|
centerDetailsPanel();
|
|
2467
|
-
console.log('[WidgetDetails] No shape rect — centered floating panel', detailsPanelWidth, detailsPanelHeight);
|
|
2588
|
+
// console.log('[WidgetDetails] No shape rect — centered floating panel', detailsPanelWidth, detailsPanelHeight);
|
|
2468
2589
|
return;
|
|
2469
2590
|
}
|
|
2470
2591
|
|
|
2471
|
-
|
|
2472
|
-
detailsPanelWidth = clampPanelWidth(DETAILS_PANEL_MAX_WIDTH);
|
|
2592
|
+
detailsPanelWidth = clampPanelWidth(DETAILS_PANEL_DEFAULT_WIDTH);
|
|
2473
2593
|
detailsPanelHeight = clampPanelHeight(DETAILS_PANEL_MAX_HEIGHT);
|
|
2474
2594
|
|
|
2475
|
-
const
|
|
2476
|
-
const
|
|
2595
|
+
const viewportLeft = shapeRect.viewportLeft ?? 0;
|
|
2596
|
+
const viewportWidth = shapeRect.viewportWidth || window.innerWidth;
|
|
2597
|
+
const viewportTop = shapeRect.viewportTop ?? detailsPanelTopOffset;
|
|
2598
|
+
const viewportHeight = shapeRect.viewportHeight || (window.innerHeight - viewportTop);
|
|
2599
|
+
|
|
2600
|
+
const x = Math.round(viewportLeft + (viewportWidth - detailsPanelWidth) / 2);
|
|
2601
|
+
const y = Math.round(viewportTop + Math.max(0, (viewportHeight - detailsPanelHeight) / 2));
|
|
2602
|
+
const pos = clampDetailsPanelPosition(x, y);
|
|
2477
2603
|
detailsPanelX = pos.x;
|
|
2478
2604
|
detailsPanelY = pos.y;
|
|
2479
|
-
console.log('[WidgetDetails]
|
|
2605
|
+
console.log('[WidgetDetails] Centered in canvas viewport', {
|
|
2480
2606
|
x: Math.round(detailsPanelX),
|
|
2481
2607
|
y: Math.round(detailsPanelY),
|
|
2482
2608
|
w: detailsPanelWidth,
|
|
2483
2609
|
h: detailsPanelHeight,
|
|
2484
|
-
shapeLeft: Math.round(shapeRect.left),
|
|
2485
2610
|
});
|
|
2486
2611
|
}
|
|
2487
2612
|
|
|
@@ -2546,7 +2671,7 @@
|
|
|
2546
2671
|
|
|
2547
2672
|
if (selectedWidgetId) {
|
|
2548
2673
|
if (iframeSrc && previewLoadedForWidgetId === selectedWidgetId) {
|
|
2549
|
-
console.log('[WidgetDetails] Reopened with existing preview, no reload needed');
|
|
2674
|
+
// console.log('[WidgetDetails] Reopened with existing preview, no reload needed');
|
|
2550
2675
|
return;
|
|
2551
2676
|
}
|
|
2552
2677
|
loadWidget(selectedWidgetId);
|
|
@@ -2570,7 +2695,7 @@
|
|
|
2570
2695
|
function handleCloseButtonClick(e: MouseEvent) {
|
|
2571
2696
|
e.preventDefault();
|
|
2572
2697
|
e.stopPropagation();
|
|
2573
|
-
console.log('[WidgetDetails] Close button clicked');
|
|
2698
|
+
// console.log('[WidgetDetails] Close button clicked');
|
|
2574
2699
|
closeWidgetDetails();
|
|
2575
2700
|
}
|
|
2576
2701
|
|
|
@@ -2620,7 +2745,7 @@
|
|
|
2620
2745
|
if (!isResizingDetails) return;
|
|
2621
2746
|
const deltaX = e.clientX - detailsResizeStartX;
|
|
2622
2747
|
const deltaY = e.clientY - detailsResizeStartY;
|
|
2623
|
-
const minW =
|
|
2748
|
+
const minW = getDetailsPanelMinWidth();
|
|
2624
2749
|
const minH = DETAILS_PANEL_MIN_HEIGHT;
|
|
2625
2750
|
const maxRight = window.innerWidth - 8;
|
|
2626
2751
|
const maxBottom = window.innerHeight - 8;
|
|
@@ -2692,23 +2817,79 @@
|
|
|
2692
2817
|
};
|
|
2693
2818
|
}
|
|
2694
2819
|
|
|
2820
|
+
function updateHeaderNameMaxWidth() {
|
|
2821
|
+
if (!widgetDetailsNameCtEl || !widgetDetailsStepsRowEl) return;
|
|
2822
|
+
const nameRect = widgetDetailsNameCtEl.getBoundingClientRect();
|
|
2823
|
+
const stepsRect = widgetDetailsStepsRowEl.getBoundingClientRect();
|
|
2824
|
+
const available = Math.floor(stepsRect.left - nameRect.left - HEADER_ELEMENTS_GAP_PX);
|
|
2825
|
+
const next = Math.max(HEADER_NAME_MIN_WIDTH_PX, available);
|
|
2826
|
+
if (next !== headerNameMaxWidthPx) {
|
|
2827
|
+
headerNameMaxWidthPx = next;
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
function trackHeaderNameMaxWidth(node: HTMLElement) {
|
|
2832
|
+
detailsDragBarEl = node;
|
|
2833
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
2834
|
+
requestAnimationFrame(updateHeaderNameMaxWidth);
|
|
2835
|
+
});
|
|
2836
|
+
resizeObserver.observe(node);
|
|
2837
|
+
requestAnimationFrame(updateHeaderNameMaxWidth);
|
|
2838
|
+
return {
|
|
2839
|
+
destroy() {
|
|
2840
|
+
resizeObserver.disconnect();
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2695
2845
|
function startDetailsDrag(e: PointerEvent) {
|
|
2696
2846
|
const target = e.target as HTMLElement | null;
|
|
2697
2847
|
if (target?.closest('.step-button, .widget-details-close-bt, .widget-details-debug-buttons, button, a, input, select, textarea')) {
|
|
2698
2848
|
return;
|
|
2699
2849
|
}
|
|
2700
|
-
handlePanelFocusRequest();
|
|
2701
|
-
e.preventDefault();
|
|
2702
|
-
e.stopPropagation();
|
|
2703
|
-
isDraggingDetails = true;
|
|
2704
2850
|
detailsDragStartX = e.clientX;
|
|
2705
2851
|
detailsDragStartY = e.clientY;
|
|
2706
2852
|
detailsInitialX = detailsPanelX < 0 ? 0 : detailsPanelX;
|
|
2707
2853
|
detailsInitialY = detailsPanelY < 0 ? detailsPanelTopOffset : detailsPanelY;
|
|
2854
|
+
detailsDragBarEl = e.currentTarget as HTMLElement | null;
|
|
2855
|
+
const fromName = !!target?.closest('.widget-details-name-ct');
|
|
2856
|
+
if (fromName) {
|
|
2857
|
+
pendingDetailsDrag = true;
|
|
2858
|
+
window.addEventListener('pointermove', onPendingDetailsDrag);
|
|
2859
|
+
window.addEventListener('pointerup', cancelPendingDetailsDrag);
|
|
2860
|
+
window.addEventListener('pointercancel', cancelPendingDetailsDrag);
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
beginDetailsDrag(e);
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
function onPendingDetailsDrag(e: PointerEvent) {
|
|
2867
|
+
if (!pendingDetailsDrag) return;
|
|
2868
|
+
const moved = Math.hypot(e.clientX - detailsDragStartX, e.clientY - detailsDragStartY);
|
|
2869
|
+
if (moved < DETAILS_DRAG_THRESHOLD_PX) return;
|
|
2870
|
+
pendingDetailsDrag = false;
|
|
2871
|
+
window.removeEventListener('pointermove', onPendingDetailsDrag);
|
|
2872
|
+
window.removeEventListener('pointerup', cancelPendingDetailsDrag);
|
|
2873
|
+
window.removeEventListener('pointercancel', cancelPendingDetailsDrag);
|
|
2874
|
+
beginDetailsDrag(e);
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
function cancelPendingDetailsDrag() {
|
|
2878
|
+
pendingDetailsDrag = false;
|
|
2879
|
+
window.removeEventListener('pointermove', onPendingDetailsDrag);
|
|
2880
|
+
window.removeEventListener('pointerup', cancelPendingDetailsDrag);
|
|
2881
|
+
window.removeEventListener('pointercancel', cancelPendingDetailsDrag);
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
function beginDetailsDrag(e: PointerEvent) {
|
|
2885
|
+
handlePanelFocusRequest();
|
|
2886
|
+
e.preventDefault();
|
|
2887
|
+
e.stopPropagation();
|
|
2888
|
+
isDraggingDetails = true;
|
|
2708
2889
|
document.body.style.cursor = 'grabbing';
|
|
2709
2890
|
document.body.style.userSelect = 'none';
|
|
2710
2891
|
try {
|
|
2711
|
-
|
|
2892
|
+
detailsDragBarEl?.setPointerCapture?.(e.pointerId);
|
|
2712
2893
|
} catch {
|
|
2713
2894
|
/* ignore capture failures */
|
|
2714
2895
|
}
|
|
@@ -2735,6 +2916,7 @@
|
|
|
2735
2916
|
}
|
|
2736
2917
|
|
|
2737
2918
|
function stopDetailsDrag() {
|
|
2919
|
+
cancelPendingDetailsDrag();
|
|
2738
2920
|
if (!isDraggingDetails) return;
|
|
2739
2921
|
isDraggingDetails = false;
|
|
2740
2922
|
document.body.style.cursor = '';
|
|
@@ -2883,7 +3065,7 @@
|
|
|
2883
3065
|
if (currentToken) setWsAuthToken(currentToken);
|
|
2884
3066
|
const userId = userSession?.user?.id;
|
|
2885
3067
|
if (userId) {
|
|
2886
|
-
console.log('[WidgetDetails] Connecting WebSocket for user events');
|
|
3068
|
+
// console.log('[WidgetDetails] Connecting WebSocket for user events');
|
|
2887
3069
|
connectWebSocket(userId);
|
|
2888
3070
|
}
|
|
2889
3071
|
}
|
|
@@ -2891,17 +3073,18 @@
|
|
|
2891
3073
|
let lastWsPreviewSignature: string | null = null;
|
|
2892
3074
|
|
|
2893
3075
|
async function applyWorkerPreviewFromEvent(workerUrl: string, buildTimeMs?: number | null): Promise<void> {
|
|
3076
|
+
if (pinnedPublishedPreviewUrl) return;
|
|
2894
3077
|
const fetchUrl = toWorkerPreviewFetchUrl(workerUrl);
|
|
2895
3078
|
const signature = `${fetchUrl}:${buildTimeMs ?? 'na'}:${selectedWidgetId ?? ''}`;
|
|
2896
3079
|
if (signature === lastWsPreviewSignature) return;
|
|
2897
3080
|
lastWsPreviewSignature = signature;
|
|
2898
3081
|
|
|
2899
|
-
console.log('[WidgetDetails] Worker preview ready — force reloading iframe', {
|
|
2900
|
-
widgetId: selectedWidgetId,
|
|
2901
|
-
previewUrl: fetchUrl,
|
|
2902
|
-
displayUrl: toWorkerPreviewDisplayUrl(fetchUrl),
|
|
2903
|
-
buildTimeMs,
|
|
2904
|
-
});
|
|
3082
|
+
// console.log('[WidgetDetails] Worker preview ready — force reloading iframe', {
|
|
3083
|
+
// widgetId: selectedWidgetId,
|
|
3084
|
+
// previewUrl: fetchUrl,
|
|
3085
|
+
// displayUrl: toWorkerPreviewDisplayUrl(fetchUrl),
|
|
3086
|
+
// buildTimeMs,
|
|
3087
|
+
// });
|
|
2905
3088
|
|
|
2906
3089
|
dynamicWorkerPreviewUrl = fetchUrl;
|
|
2907
3090
|
directPreviewUrl = toWorkerPreviewDisplayUrl(fetchUrl);
|
|
@@ -2947,25 +3130,29 @@
|
|
|
2947
3130
|
// Worker-built widgets signal readiness via widgetic:ready (not sveltekit-hydration-complete)
|
|
2948
3131
|
const handleWidgeticReady = (event: MessageEvent) => {
|
|
2949
3132
|
if (event.data?.type !== 'widgetic:ready') return;
|
|
2950
|
-
|
|
3133
|
+
const activeWin = previewIframe?.contentWindow;
|
|
3134
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
3135
|
+
// console.log('[WidgetDetails] widgetic:ready received — applying queued composition updates');
|
|
2951
3136
|
markPreviewWidgetReady();
|
|
2952
3137
|
};
|
|
2953
3138
|
window.addEventListener('message', handleWidgeticReady);
|
|
2954
3139
|
|
|
2955
3140
|
// Listen for SvelteKit hydration completion signal (legacy VM / published SvelteKit artifacts)
|
|
2956
3141
|
const handleHydrationSignal = (event: MessageEvent) => {
|
|
2957
|
-
if (event.data?.type
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
3142
|
+
if (event.data?.type !== 'sveltekit-hydration-complete') return;
|
|
3143
|
+
const activeWin = previewIframe?.contentWindow;
|
|
3144
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
3145
|
+
previewHydrating = false;
|
|
3146
|
+
if (hydrationSafetyTimeout) { clearTimeout(hydrationSafetyTimeout); hydrationSafetyTimeout = null; }
|
|
3147
|
+
markPreviewWidgetReady();
|
|
2963
3148
|
};
|
|
2964
3149
|
window.addEventListener('message', handleHydrationSignal);
|
|
2965
3150
|
|
|
2966
3151
|
// Listen for runtime error signals from the preview iframe
|
|
2967
3152
|
const handleIframeRuntimeError = (event: MessageEvent) => {
|
|
2968
3153
|
if (event.data?.type !== 'sveltekit-runtime-error') return;
|
|
3154
|
+
const activeWin = previewIframe?.contentWindow;
|
|
3155
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
2969
3156
|
|
|
2970
3157
|
const runtimeMsg = String(event.data?.message || event.data?.error || '').trim();
|
|
2971
3158
|
const runtimeSource = event.data?.source ? String(event.data.source) : '';
|
|
@@ -2976,16 +3163,10 @@
|
|
|
2976
3163
|
console.warn('[WidgetDetails] Ignoring runtime error during code generation:', runtimeMsg);
|
|
2977
3164
|
return;
|
|
2978
3165
|
}
|
|
2979
|
-
if (isCodegenPreviewFresh()) {
|
|
3166
|
+
if (isCodegenPreviewFresh() && !looksLikeHardCompileFailure(runtimeMsg)) {
|
|
2980
3167
|
console.warn('[WidgetDetails] Ignoring runtime error during codegen preview window:', runtimeMsg);
|
|
2981
3168
|
return;
|
|
2982
3169
|
}
|
|
2983
|
-
// Stale iframe (previous build) must not poison the active preview
|
|
2984
|
-
const activeWin = previewIframe?.contentWindow;
|
|
2985
|
-
if (activeWin && event.source && event.source !== activeWin) {
|
|
2986
|
-
console.warn('[WidgetDetails] Ignoring runtime error from inactive iframe');
|
|
2987
|
-
return;
|
|
2988
|
-
}
|
|
2989
3170
|
if (isBenignPreviewRuntimeError(runtimeMsg)) {
|
|
2990
3171
|
console.warn('[WidgetDetails] Ignoring benign preview runtime error:', runtimeMsg);
|
|
2991
3172
|
return;
|
|
@@ -3036,20 +3217,21 @@
|
|
|
3036
3217
|
|
|
3037
3218
|
// Listen for expired preview token signal
|
|
3038
3219
|
const handleTokenExpired = async (event: MessageEvent) => {
|
|
3039
|
-
if (event.data?.type
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3220
|
+
if (event.data?.type !== 'preview-token-expired') return;
|
|
3221
|
+
const activeWin = previewIframe?.contentWindow;
|
|
3222
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
3223
|
+
directPreviewUrl = null;
|
|
3224
|
+
lastPreviewUrlFetchTime = 0;
|
|
3225
|
+
try {
|
|
3226
|
+
const freshUrl = await fetchDirectPreviewUrl();
|
|
3227
|
+
if (freshUrl) {
|
|
3228
|
+
const cacheBuster = Date.now();
|
|
3229
|
+
const url = new URL(freshUrl);
|
|
3230
|
+
url.searchParams.set('_t', cacheBuster.toString());
|
|
3231
|
+
iframeSrc = url.toString();
|
|
3232
|
+
previewCacheBuster = cacheBuster;
|
|
3233
|
+
}
|
|
3234
|
+
} catch (err) { console.error('Error refreshing preview token:', err); }
|
|
3053
3235
|
};
|
|
3054
3236
|
window.addEventListener('message', handleTokenExpired);
|
|
3055
3237
|
|
|
@@ -3059,12 +3241,14 @@
|
|
|
3059
3241
|
// "Rebuild" whenever we lose the L1/L2 cache. (Reset happens on widget switch.)
|
|
3060
3242
|
const handleWidgetNotFound = async (event: MessageEvent) => {
|
|
3061
3243
|
if (event.data?.type !== 'widgetic:widget-not-found') return;
|
|
3244
|
+
const activeWin = previewIframe?.contentWindow;
|
|
3245
|
+
if (!activeWin || event.source !== activeWin) return;
|
|
3062
3246
|
if (isCodegenPreviewFresh()) {
|
|
3063
|
-
console.log('[WidgetDetails] Ignoring widget-not-found during codegen preview window');
|
|
3247
|
+
// console.log('[WidgetDetails] Ignoring widget-not-found during codegen preview window');
|
|
3064
3248
|
return;
|
|
3065
3249
|
}
|
|
3066
3250
|
if (dwLoadTimeoutHandle) { clearTimeout(dwLoadTimeoutHandle); dwLoadTimeoutHandle = null; }
|
|
3067
|
-
console.log('[WidgetDetails] Worker reports widget not found — checking if repo has code');
|
|
3251
|
+
// console.log('[WidgetDetails] Worker reports widget not found — checking if repo has code');
|
|
3068
3252
|
dynamicWorkerPreviewUrl = null;
|
|
3069
3253
|
directPreviewUrl = null;
|
|
3070
3254
|
iframeSrc = null;
|
|
@@ -3075,7 +3259,7 @@
|
|
|
3075
3259
|
const hasRepo = !!currentRepositoryId;
|
|
3076
3260
|
if (hasRepo && capturedWidgetId && widgetNotFoundAutoRebuildAttempts === 0) {
|
|
3077
3261
|
widgetNotFoundAutoRebuildAttempts = 1;
|
|
3078
|
-
console.log('[WidgetDetails] Widget missing from Worker — auto-triggering build-from-repo');
|
|
3262
|
+
// console.log('[WidgetDetails] Widget missing from Worker — auto-triggering build-from-repo');
|
|
3079
3263
|
previewLoading = true;
|
|
3080
3264
|
try {
|
|
3081
3265
|
await triggerBuildFromRepo({ force: true });
|
|
@@ -3105,6 +3289,7 @@
|
|
|
3105
3289
|
if (contentContainerEl) {
|
|
3106
3290
|
hasScrollbar = contentContainerEl.scrollHeight > contentContainerEl.clientHeight;
|
|
3107
3291
|
}
|
|
3292
|
+
updateHeaderNameMaxWidth();
|
|
3108
3293
|
});
|
|
3109
3294
|
|
|
3110
3295
|
onDestroy(() => {
|
|
@@ -3133,13 +3318,13 @@
|
|
|
3133
3318
|
onclick={handleCloseButtonClick}
|
|
3134
3319
|
onmousedown={(e) => e.stopPropagation()}
|
|
3135
3320
|
onpointerdown={(e) => e.stopPropagation()}
|
|
3136
|
-
class="widget-details-close-bt pointer-events-auto absolute -top-
|
|
3321
|
+
class="widget-details-close-bt pointer-events-auto absolute -top-3 -right-3 z-40 w-7 h-7 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-md hover:bg-red-50 hover:border-red-300 hover:text-red-600 text-gray-500 cursor-pointer transition-colors text-sm"
|
|
3137
3322
|
title="Close widget details"
|
|
3138
3323
|
>
|
|
3139
3324
|
✕
|
|
3140
3325
|
</button>
|
|
3141
3326
|
<div
|
|
3142
|
-
|
|
3327
|
+
class="widget-details-floating-panel pointer-events-auto relative flex flex-col h-full w-full min-w-[480px]
|
|
3143
3328
|
{fillViewport
|
|
3144
3329
|
? 'bg-white rounded-none border-x border-b border-t-0 ring-0 shadow-none overflow-hidden'
|
|
3145
3330
|
: 'bg-white/95 backdrop-blur-sm shadow-2xl rounded-xl border overflow-hidden'}
|
|
@@ -3151,23 +3336,43 @@
|
|
|
3151
3336
|
<!-- Widget Details Top Bar — title, steps, and close on one row -->
|
|
3152
3337
|
<div class="widget-details-header-ct flex flex-col shrink-0 bg-gray-100 border-b border-gray-200">
|
|
3153
3338
|
<div
|
|
3154
|
-
class="widget-details-drag-bar relative flex items-center
|
|
3339
|
+
class="widget-details-drag-bar relative flex items-center py-1.5 cursor-grab active:cursor-grabbing select-none min-h-[40px] touch-none flex-nowrap min-w-0"
|
|
3340
|
+
style="--header-elements-gap: {HEADER_ELEMENTS_GAP_PX}px; gap: {HEADER_ELEMENTS_GAP_PX}px; padding-left: {HEADER_PADDING_PX}px; padding-right: {HEADER_PADDING_PX}px;"
|
|
3341
|
+
bind:this={detailsDragBarEl}
|
|
3342
|
+
use:trackHeaderNameMaxWidth
|
|
3155
3343
|
onpointerdown={startDetailsDrag}
|
|
3156
3344
|
>
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3345
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
3346
|
+
<div
|
|
3347
|
+
class="widget-details-name-ct relative z-[1] min-w-0 w-fit overflow-hidden"
|
|
3348
|
+
bind:this={widgetDetailsNameCtEl}
|
|
3349
|
+
style="min-width: {HEADER_NAME_MIN_WIDTH_PX}px; max-width: {headerNameMaxWidthPx}px;"
|
|
3160
3350
|
>
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3351
|
+
<EditableName
|
|
3352
|
+
class="widget-details-name min-w-0 truncate text-sm font-bold text-gray-800"
|
|
3353
|
+
name={selectedWidget?.name || ''}
|
|
3354
|
+
placeholder={isDraftConvertPanel ? 'Widget Draft' : 'Widget'}
|
|
3355
|
+
tooltipText="Double-click to rename"
|
|
3356
|
+
on:rename={(e) => {
|
|
3357
|
+
if (!selectedWidgetId) return;
|
|
3358
|
+
dispatch('widgetRename', { widgetId: selectedWidgetId, newName: e.detail.newName });
|
|
3359
|
+
}}
|
|
3360
|
+
/>
|
|
3361
|
+
</div>
|
|
3362
|
+
<div
|
|
3363
|
+
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
|
+
bind:this={widgetDetailsStepsRowEl}
|
|
3365
|
+
>
|
|
3366
|
+
<div
|
|
3367
|
+
class="widget-details-step-indicator pointer-events-auto flex items-center flex-nowrap justify-center whitespace-nowrap"
|
|
3368
|
+
style="gap: {HEADER_ELEMENTS_GAP_PX}px;"
|
|
3369
|
+
>
|
|
3370
|
+
<HoverTooltip text="Create widget code" class="inline-flex shrink-0">
|
|
3166
3371
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
3167
3372
|
<button
|
|
3168
3373
|
type="button"
|
|
3169
3374
|
onclick={() => goToStep('create')}
|
|
3170
|
-
class="step-button step-button-create flex items-center gap-
|
|
3375
|
+
class="step-button step-button-create flex items-center gap-0.5 px-1 py-0.5 rounded-md text-xs font-medium transition-all
|
|
3171
3376
|
{currentStep === 'create'
|
|
3172
3377
|
? 'bg-blue-100 text-blue-700 border border-blue-300 cursor-pointer'
|
|
3173
3378
|
: 'text-gray-600 hover:bg-gray-200 border border-transparent cursor-pointer'}"
|
|
@@ -3175,15 +3380,16 @@
|
|
|
3175
3380
|
<span class="step-number inline-flex items-center justify-center w-5 h-5 rounded-full text-[11px] font-bold leading-none {currentStep === 'create' ? 'bg-blue-600 text-white' : 'bg-gray-300 text-gray-700'}">1</span>
|
|
3176
3381
|
<span class="step-label">Create</span>
|
|
3177
3382
|
</button>
|
|
3178
|
-
|
|
3383
|
+
</HoverTooltip>
|
|
3384
|
+
<span class="step-connector text-sm font-semibold leading-none {canEditStep ? 'text-gray-500' : 'text-gray-300'}" aria-hidden="true">→</span>
|
|
3385
|
+
<HoverTooltip text={canEditStep ? 'Edit composition' : 'Publish the widget to edit composition'} class="inline-flex shrink-0">
|
|
3179
3386
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
3180
3387
|
<button
|
|
3181
3388
|
type="button"
|
|
3182
3389
|
onclick={() => goToStep('edit')}
|
|
3183
3390
|
tabindex={canEditStep ? 0 : -1}
|
|
3184
3391
|
aria-disabled={!canEditStep}
|
|
3185
|
-
|
|
3186
|
-
class="step-button step-button-edit flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium transition-all
|
|
3392
|
+
class="step-button step-button-edit flex items-center gap-0.5 px-1 py-0.5 rounded-md text-xs font-medium transition-all
|
|
3187
3393
|
{currentStep === 'edit'
|
|
3188
3394
|
? 'bg-blue-100 text-blue-700 border border-blue-300 cursor-pointer'
|
|
3189
3395
|
: canEditStep
|
|
@@ -3193,15 +3399,16 @@
|
|
|
3193
3399
|
<span class="step-number inline-flex items-center justify-center w-5 h-5 rounded-full text-[11px] font-bold leading-none {currentStep === 'edit' ? 'bg-blue-600 text-white' : canEditStep ? 'bg-gray-300 text-gray-700' : 'bg-gray-200 text-gray-400'}">2</span>
|
|
3194
3400
|
<span class="step-label">Edit</span>
|
|
3195
3401
|
</button>
|
|
3196
|
-
|
|
3402
|
+
</HoverTooltip>
|
|
3403
|
+
<span class="step-connector text-sm font-semibold leading-none {canEmbedStep ? 'text-gray-500' : 'text-gray-300'}" aria-hidden="true">→</span>
|
|
3404
|
+
<HoverTooltip text={canEmbedStep ? 'Embed the published widget' : 'Publish the widget to get embed code'} class="inline-flex shrink-0">
|
|
3197
3405
|
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
3198
3406
|
<button
|
|
3199
3407
|
type="button"
|
|
3200
3408
|
onclick={() => goToStep('embed')}
|
|
3201
3409
|
tabindex={canEmbedStep ? 0 : -1}
|
|
3202
3410
|
aria-disabled={!canEmbedStep}
|
|
3203
|
-
|
|
3204
|
-
class="step-button step-button-embed flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium transition-all
|
|
3411
|
+
class="step-button step-button-embed flex items-center gap-0.5 px-1 py-0.5 rounded-md text-xs font-medium transition-all
|
|
3205
3412
|
{currentStep === 'embed'
|
|
3206
3413
|
? 'bg-blue-100 text-blue-700 border border-blue-300 cursor-pointer'
|
|
3207
3414
|
: canEmbedStep
|
|
@@ -3211,41 +3418,57 @@
|
|
|
3211
3418
|
<span class="step-number inline-flex items-center justify-center w-5 h-5 rounded-full text-[11px] font-bold leading-none {currentStep === 'embed' ? 'bg-blue-600 text-white' : canEmbedStep ? 'bg-gray-300 text-gray-700' : 'bg-gray-200 text-gray-400'}">3</span>
|
|
3212
3419
|
<span class="step-label">Embed</span>
|
|
3213
3420
|
</button>
|
|
3421
|
+
</HoverTooltip>
|
|
3214
3422
|
</div>
|
|
3215
3423
|
</div>
|
|
3216
3424
|
|
|
3217
|
-
<div class="widget-details-debug-buttons flex items-center
|
|
3425
|
+
<div class="widget-details-debug-buttons relative z-[1] ml-auto flex items-center shrink-0">
|
|
3426
|
+
<HoverTooltip text={previewColumnCollapsed ? 'Open live preview' : 'Close live preview'}>
|
|
3218
3427
|
<button
|
|
3219
3428
|
type="button"
|
|
3220
3429
|
onclick={() => previewColumnCollapsed ? expandPreviewColumn() : collapsePreviewColumn()}
|
|
3221
3430
|
onmousedown={(e) => e.stopPropagation()}
|
|
3222
3431
|
onpointerdown={(e) => e.stopPropagation()}
|
|
3223
|
-
|
|
3432
|
+
aria-label={previewColumnCollapsed ? 'Open live preview' : 'Close live preview'}
|
|
3433
|
+
class="widget-details-preview-toggle-bt h-6 w-6 flex items-center justify-center rounded-lg cursor-pointer transition-colors
|
|
3224
3434
|
{previewColumnCollapsed
|
|
3225
3435
|
? 'bg-white border border-gray-300 text-gray-600 hover:bg-gray-100'
|
|
3226
3436
|
: 'bg-blue-50 border border-blue-300 text-blue-700 hover:bg-blue-100'}"
|
|
3227
|
-
title={previewColumnCollapsed ? 'Open live preview column' : 'Close live preview column'}
|
|
3228
3437
|
>
|
|
3229
|
-
{previewColumnCollapsed
|
|
3438
|
+
{#if previewColumnCollapsed}
|
|
3439
|
+
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
3440
|
+
<rect x="3" y="4" width="18" height="16" rx="2"/>
|
|
3441
|
+
<path d="M14 4v16"/>
|
|
3442
|
+
<path d="M17 9l3 3-3 3"/>
|
|
3443
|
+
</svg>
|
|
3444
|
+
{:else}
|
|
3445
|
+
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
3446
|
+
<rect x="3" y="4" width="18" height="16" rx="2"/>
|
|
3447
|
+
<path d="M14 4v16"/>
|
|
3448
|
+
<path d="M10 15L7 12l3-3"/>
|
|
3449
|
+
</svg>
|
|
3450
|
+
{/if}
|
|
3230
3451
|
</button>
|
|
3452
|
+
</HoverTooltip>
|
|
3231
3453
|
</div>
|
|
3232
3454
|
</div>
|
|
3233
3455
|
</div>
|
|
3234
3456
|
|
|
3235
3457
|
<!-- Widget Details Content: Two-column layout -->
|
|
3236
|
-
<div class="widget-details-content-wrapper relative m-2.5 min-w-
|
|
3237
|
-
<div class="widget-details-two-col flex h-full min-h-0 min-w-
|
|
3458
|
+
<div class="widget-details-content-wrapper relative m-2.5 min-w-[460px] min-h-0 flex-1 overflow-hidden">
|
|
3459
|
+
<div class="widget-details-two-col flex h-full min-h-0 min-w-[460px] gap-0 overflow-hidden">
|
|
3238
3460
|
|
|
3239
3461
|
<!-- ═══ LEFT COLUMN: Step Content (scrollable) ═══ -->
|
|
3240
3462
|
<div
|
|
3241
|
-
class="widget-details-left-col flex flex-col min-w-
|
|
3463
|
+
class="widget-details-left-col flex flex-col min-w-[460px] min-h-0 overflow-hidden relative"
|
|
3242
3464
|
style="width: {previewColumnCollapsed ? 100 : leftColPercent}%; flex: 0 0 {previewColumnCollapsed ? 100 : leftColPercent}%;"
|
|
3243
3465
|
>
|
|
3244
3466
|
<div class="widget-details-left-border absolute inset-y-0 left-0 right-0 border border-gray-300 rounded-lg pointer-events-none z-10"></div>
|
|
3245
3467
|
|
|
3246
3468
|
{#if selectedWidgetId}
|
|
3469
|
+
<div class="widget-details-left-stack relative z-0 flex flex-col h-full min-h-0 min-w-[460px] overflow-hidden">
|
|
3247
3470
|
<!-- Sticky Widget Info Bar (always visible above scrollable content) -->
|
|
3248
|
-
<div class="widget-info-sticky-bar flex items-center justify-between px-3 py-1.5 border-b border-gray-200 bg-gray-50 rounded-t-lg shrink-0 gap-2 min-w-0 z-
|
|
3471
|
+
<div class="widget-info-sticky-bar relative flex items-center justify-between px-3 py-1.5 border-b border-gray-200 bg-gray-50 rounded-t-lg shrink-0 gap-2 min-w-0 z-20 overflow-visible">
|
|
3249
3472
|
<div class="widget-info-name flex items-center min-w-0 gap-1 text-sm text-gray-700">
|
|
3250
3473
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
3251
3474
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
@@ -3263,12 +3486,14 @@
|
|
|
3263
3486
|
{:else if saveStatus === 'error'}
|
|
3264
3487
|
<span class="text-[10px] px-1.5 py-0.5 rounded bg-red-100 text-red-700">Save Error</span>
|
|
3265
3488
|
{/if}
|
|
3489
|
+
{#if debugMode}
|
|
3266
3490
|
<span class="text-[10px] text-gray-500">
|
|
3267
3491
|
Commit: <span class="font-mono font-medium text-gray-600" title={lastCommitId || ''}>{lastCommitId ? lastCommitId.substring(0, 8) : '—'}</span>
|
|
3268
3492
|
</span>
|
|
3269
|
-
{#if lastPublishedVersion !== null}
|
|
3270
|
-
<a href="/test-widget?widgetId={selectedWidgetId}&version={lastPublishedVersion}" target="_blank" rel="noopener noreferrer" class="text-[10px] px-1.5 py-0.5 rounded bg-emerald-600 border border-emerald-700 text-white hover:bg-emerald-700 transition-colors whitespace-nowrap cursor-pointer" title="Open published widget v{lastPublishedVersion}">Live: v{lastPublishedVersion} ↗</a>
|
|
3271
3493
|
{/if}
|
|
3494
|
+
<div class="widget-info-version-history-slot relative z-20 shrink-0">
|
|
3495
|
+
<slot name="version-history" />
|
|
3496
|
+
</div>
|
|
3272
3497
|
</div>
|
|
3273
3498
|
</div>
|
|
3274
3499
|
|
|
@@ -3281,22 +3506,33 @@
|
|
|
3281
3506
|
|
|
3282
3507
|
<!-- Keep all steps mounted — switching tabs must not destroy chat / PropsEditor state -->
|
|
3283
3508
|
<div class="widget-details-create-step flex flex-col flex-1 min-h-0 overflow-hidden overflow-x-hidden {currentStep === 'create' ? '' : 'hidden'}">
|
|
3284
|
-
<div class="widget-details-create-
|
|
3285
|
-
<slot name="code-generation" />
|
|
3509
|
+
<div class="widget-details-create-models-ct shrink-0 empty:hidden">
|
|
3510
|
+
<slot name="code-generation-models" />
|
|
3286
3511
|
</div>
|
|
3287
|
-
<div
|
|
3512
|
+
<div
|
|
3513
|
+
class="widget-details-create-chat-ct flex flex-col flex-1 min-h-0 overflow-hidden overflow-x-hidden"
|
|
3514
|
+
>
|
|
3515
|
+
<div class="widget-details-create-status-slot shrink-0 empty:hidden">
|
|
3516
|
+
<slot name="code-generation-status" />
|
|
3517
|
+
</div>
|
|
3518
|
+
<div class="widget-details-create-chat-body flex flex-col flex-1 min-h-0 overflow-hidden">
|
|
3519
|
+
<slot name="code-generation" />
|
|
3520
|
+
</div>
|
|
3521
|
+
</div>
|
|
3522
|
+
<div class="widget-details-create-publish-ct shrink-0 empty:hidden">
|
|
3288
3523
|
<slot name="publish-widget" />
|
|
3289
3524
|
</div>
|
|
3290
3525
|
</div>
|
|
3291
|
-
<div class="widget-details-edit-step flex flex-col
|
|
3526
|
+
<div class="widget-details-edit-step flex flex-col flex-1 min-h-0 overflow-hidden {currentStep === 'edit' ? '' : 'hidden'}">
|
|
3292
3527
|
<slot name="composition-editor" />
|
|
3293
3528
|
</div>
|
|
3294
|
-
<div class="widget-details-embed-step flex flex-col gap-2 p-2 {currentStep === 'embed' ? '' : 'hidden'}">
|
|
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 {currentStep === 'embed' ? '' : 'hidden'}">
|
|
3295
3530
|
<slot name="embed-section" />
|
|
3296
3531
|
</div>
|
|
3297
3532
|
|
|
3298
3533
|
</div> <!-- end of actions-panel -->
|
|
3299
3534
|
</div> <!-- end of widget-details-left-content -->
|
|
3535
|
+
</div> <!-- end of widget-details-left-stack -->
|
|
3300
3536
|
{/if}
|
|
3301
3537
|
|
|
3302
3538
|
</div> <!-- end of widget-details-left-col -->
|
|
@@ -3581,14 +3817,38 @@
|
|
|
3581
3817
|
{/if}
|
|
3582
3818
|
|
|
3583
3819
|
<style>
|
|
3584
|
-
.widget-details-
|
|
3585
|
-
|
|
3586
|
-
|
|
3820
|
+
.widget-details-drag-bar button:focus,
|
|
3821
|
+
.widget-details-drag-bar button:focus-visible,
|
|
3822
|
+
.widget-details-drag-bar a:focus,
|
|
3823
|
+
.widget-details-drag-bar a:focus-visible {
|
|
3824
|
+
outline: none;
|
|
3825
|
+
box-shadow: none;
|
|
3826
|
+
}
|
|
3827
|
+
.widget-details-create-models-ct :global(button:focus),
|
|
3828
|
+
.widget-details-create-models-ct :global(button:focus-visible),
|
|
3829
|
+
.widget-details-create-models-ct :global(select:focus),
|
|
3830
|
+
.widget-details-create-models-ct :global(select:focus-visible) {
|
|
3831
|
+
outline: none;
|
|
3832
|
+
box-shadow: none;
|
|
3833
|
+
}
|
|
3834
|
+
.widget-details-create-chat-ct :global(.prompt-chat-messages) {
|
|
3835
|
+
overflow-y: auto;
|
|
3587
3836
|
scrollbar-width: thin;
|
|
3588
|
-
scrollbar-color: rgba(0, 0, 0, 0.
|
|
3837
|
+
scrollbar-color: rgba(0, 0, 0, 0.22) transparent;
|
|
3838
|
+
}
|
|
3839
|
+
.widget-details-create-chat-ct :global(.prompt-chat-messages::-webkit-scrollbar) {
|
|
3840
|
+
width: 6px;
|
|
3841
|
+
background: transparent;
|
|
3842
|
+
}
|
|
3843
|
+
.widget-details-create-chat-ct :global(.prompt-chat-messages::-webkit-scrollbar-track) {
|
|
3844
|
+
background: transparent;
|
|
3845
|
+
}
|
|
3846
|
+
.widget-details-create-chat-ct :global(.prompt-chat-messages::-webkit-scrollbar-thumb) {
|
|
3847
|
+
background: rgba(0, 0, 0, 0.2);
|
|
3848
|
+
border-radius: 4px;
|
|
3589
3849
|
}
|
|
3590
3850
|
.widget-details-left-content::-webkit-scrollbar {
|
|
3591
|
-
width:
|
|
3851
|
+
width: 6px;
|
|
3592
3852
|
background: transparent;
|
|
3593
3853
|
}
|
|
3594
3854
|
.widget-details-left-content::-webkit-scrollbar-track {
|