@widgetic/creator 0.3.65 → 0.3.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/CreatorApp.svelte +418 -137
- package/dist/CreatorApp.svelte.d.ts +3 -1
- package/dist/components/WidgetDetails.svelte +34 -14
- package/dist/components/WidgetDetails.svelte.d.ts +3 -0
- package/dist/components/WidgetShapeLivePreviews.svelte +28 -2
- package/dist/utils/freezeWatchdog.d.ts +16 -0
- package/dist/utils/freezeWatchdog.js +87 -2
- package/dist/utils/persistWidgetThumbnail.d.ts +12 -1
- package/dist/utils/persistWidgetThumbnail.js +14 -7
- package/package.json +4 -2
|
@@ -37,6 +37,8 @@ declare const CreatorApp: $$__sveltets_2_IsomorphicComponent<{
|
|
|
37
37
|
style?: string;
|
|
38
38
|
}, {
|
|
39
39
|
[evt: string]: CustomEvent<any>;
|
|
40
|
-
}, {}, {
|
|
40
|
+
}, {}, {
|
|
41
|
+
onBack: CreatorAppProps["onBack"];
|
|
42
|
+
}, string>;
|
|
41
43
|
type CreatorApp = InstanceType<typeof CreatorApp>;
|
|
42
44
|
export default CreatorApp;
|
|
@@ -69,7 +69,9 @@
|
|
|
69
69
|
| { type: 'save' };
|
|
70
70
|
|
|
71
71
|
export let userSession: { jwt?: string; user?: { id?: string } } | null = null;
|
|
72
|
-
|
|
72
|
+
// Passed by CreatorApp but never read here — export const keeps the
|
|
73
|
+
// init-set contract without the unused-reactive warning.
|
|
74
|
+
export const hasValidToken: boolean = false;
|
|
73
75
|
export let selectedWidgetId: string | null = null;
|
|
74
76
|
export let selectedWidget: WidgetSummary | null = null;
|
|
75
77
|
export let isGeneratingCode: boolean = false;
|
|
@@ -81,11 +83,13 @@
|
|
|
81
83
|
/** 'pending' = repo being created (show loader); 'failed' = show Create Repository; 'ready' = normal. */
|
|
82
84
|
export let repositorySetupStatus: 'pending' | 'ready' | 'failed' = 'ready';
|
|
83
85
|
export let generateCodeStatus: string | null = null;
|
|
84
|
-
export
|
|
86
|
+
export const isCheckingActiveGeneration: boolean = false;
|
|
85
87
|
export let lastCommitId: string | null = null;
|
|
86
88
|
export let debugMode: boolean = false;
|
|
87
89
|
export let forceMockReady: boolean = false;
|
|
88
|
-
|
|
90
|
+
// Passed by CreatorApp but never read here — export const keeps the
|
|
91
|
+
// init-set contract without the unused-reactive warning.
|
|
92
|
+
export const debugModeAvailable: boolean = false;
|
|
89
93
|
|
|
90
94
|
// Code gen / save / publish state — passed through to parent's slot content.
|
|
91
95
|
// svelte-ignore export_let_unused — these are consumed by <svelte:fragment> slots in the parent.
|
|
@@ -767,6 +771,7 @@
|
|
|
767
771
|
getResizeLimits?: () => { maxWidth: number; maxHeight: number };
|
|
768
772
|
fitToContainer?: () => void;
|
|
769
773
|
resetPreviewPosition?: () => void;
|
|
774
|
+
nudgeIframePaint?: () => void;
|
|
770
775
|
} | undefined;
|
|
771
776
|
let previewIframe: HTMLIFrameElement | null = null;
|
|
772
777
|
let isLoadingDirectPreviewUrl = false;
|
|
@@ -806,6 +811,14 @@
|
|
|
806
811
|
/** Parent calls this when a generation starts for THIS widget's panel. */
|
|
807
812
|
export function markCodegenStarted(): void {
|
|
808
813
|
codegenStartedAt = Date.now();
|
|
814
|
+
// A pinned published artifact — a version picked from the history dropdown —
|
|
815
|
+
// makes the Worker build event a no-op, so the panel went on showing that
|
|
816
|
+
// older version after the generation finished. The code being generated is
|
|
817
|
+
// what the user just asked to see, so the pin does not survive a generation.
|
|
818
|
+
pinnedPublishedPreviewUrl = null;
|
|
819
|
+
// The build event is judged against the previous generation's signature
|
|
820
|
+
// otherwise, and a Worker cache hit can reproduce it exactly.
|
|
821
|
+
lastWsPreviewSignature = null;
|
|
809
822
|
}
|
|
810
823
|
|
|
811
824
|
/**
|
|
@@ -2046,7 +2059,8 @@
|
|
|
2046
2059
|
|
|
2047
2060
|
function normalizeRepositoryId(source: WidgetSummary | null): string | null {
|
|
2048
2061
|
if (!source) return null;
|
|
2049
|
-
|
|
2062
|
+
const candidate = source.repositoryId || source.repo_id || source.repoId;
|
|
2063
|
+
return typeof candidate === 'string' ? candidate : null;
|
|
2050
2064
|
}
|
|
2051
2065
|
|
|
2052
2066
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -3159,10 +3173,18 @@
|
|
|
3159
3173
|
|
|
3160
3174
|
let lastWsPreviewSignature: string | null = null;
|
|
3161
3175
|
|
|
3162
|
-
async function applyWorkerPreviewFromEvent(
|
|
3176
|
+
async function applyWorkerPreviewFromEvent(
|
|
3177
|
+
workerUrl: string,
|
|
3178
|
+
buildTimeMs?: number | null,
|
|
3179
|
+
eventTimestamp?: string | null,
|
|
3180
|
+
): Promise<void> {
|
|
3163
3181
|
if (pinnedPublishedPreviewUrl) return;
|
|
3164
3182
|
const fetchUrl = toWorkerPreviewFetchUrl(workerUrl);
|
|
3165
|
-
|
|
3183
|
+
// The Worker serves every build of a widget at the same /preview URL, and
|
|
3184
|
+
// buildTimeMs repeats across builds (0 on a cache hit), so url + buildTimeMs
|
|
3185
|
+
// alone made a second generation look like a replay of the first and the
|
|
3186
|
+
// iframe kept the previous version. The per-event timestamp separates them.
|
|
3187
|
+
const signature = `${fetchUrl}:${buildTimeMs ?? 'na'}:${eventTimestamp ?? 'na'}:${selectedWidgetId ?? ''}`;
|
|
3166
3188
|
if (signature === lastWsPreviewSignature) return;
|
|
3167
3189
|
lastWsPreviewSignature = signature;
|
|
3168
3190
|
|
|
@@ -3189,6 +3211,7 @@
|
|
|
3189
3211
|
void applyWorkerPreviewFromEvent(
|
|
3190
3212
|
$dynamicWorkerPreviewStatus.previewUrl,
|
|
3191
3213
|
$dynamicWorkerPreviewStatus.buildTimeMs,
|
|
3214
|
+
$dynamicWorkerPreviewStatus.timestamp,
|
|
3192
3215
|
);
|
|
3193
3216
|
}
|
|
3194
3217
|
|
|
@@ -3685,14 +3708,11 @@
|
|
|
3685
3708
|
<Button onclick={() => { showPreviewModal = false; }} variant="outline" size="sm" class="close-preview-modal-btn shrink-0 w-6 h-6 p-[3px] cursor-pointer border border-black text-md bg-white text-black hover:bg-gray-100 rounded-lg">X</Button>
|
|
3686
3709
|
</div>
|
|
3687
3710
|
</div>
|
|
3688
|
-
{/if}
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
resizeMode="concentric"
|
|
3694
|
-
defaultStageAlign="center"
|
|
3695
|
-
autoFitStage={showPreviewModal}
|
|
3711
|
+
{/if} <WidgetPreview
|
|
3712
|
+
bind:this={widgetPreviewComponentRef}
|
|
3713
|
+
mode="controlled"
|
|
3714
|
+
enableResize={true}
|
|
3715
|
+
autoFitStage={showPreviewModal}
|
|
3696
3716
|
src={iframeSrc || getFallbackPreviewUrl()}
|
|
3697
3717
|
displayUrl={directPreviewUrl || iframeSrc}
|
|
3698
3718
|
bind:resizeWidth={previewResizeWidth}
|
|
@@ -201,6 +201,9 @@ declare const WidgetDetails: $$__sveltets_2_IsomorphicComponent<{
|
|
|
201
201
|
'composition-editor': {};
|
|
202
202
|
'embed-section': {};
|
|
203
203
|
}, {
|
|
204
|
+
hasValidToken: boolean;
|
|
205
|
+
isCheckingActiveGeneration: boolean;
|
|
206
|
+
debugModeAvailable: boolean;
|
|
204
207
|
beginCompositionHydration: () => void;
|
|
205
208
|
showPublishedArtifact: (url: string | null) => void;
|
|
206
209
|
finishCompositionHydration: () => void;
|
|
@@ -187,14 +187,38 @@
|
|
|
187
187
|
* at the same URL (codegen rebuild). `sync()` only reloads when `src` changes,
|
|
188
188
|
* so without a cache-bust token the overlay keeps showing pre-codegen content.
|
|
189
189
|
* Bumps the reload token → `{#key}` remounts the iframe with a fresh `_t`.
|
|
190
|
-
*
|
|
190
|
+
*
|
|
191
|
+
* Reloads closer together than RELOAD_COALESCE_MS are collapsed into one, so
|
|
192
|
+
* the WS build event and the codegen `completed` handler do not flicker the
|
|
193
|
+
* overlay twice. The extra call is deferred rather than dropped: the later
|
|
194
|
+
* request is the authoritative one (codegen reloads after the build event, and
|
|
195
|
+
* a build-repair loop builds more than once), and dropping it left the overlay
|
|
196
|
+
* showing the version from before the generation.
|
|
191
197
|
*/
|
|
198
|
+
const RELOAD_COALESCE_MS = 1200;
|
|
192
199
|
let lastReloadAtByWidgetId: Record<string, number> = {};
|
|
200
|
+
const coalescedReloadTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
201
|
+
|
|
193
202
|
export function requestReload(widgetId: string): void {
|
|
194
203
|
const now = Date.now();
|
|
195
|
-
|
|
204
|
+
const sinceLast = now - (lastReloadAtByWidgetId[widgetId] || 0);
|
|
205
|
+
if (sinceLast < RELOAD_COALESCE_MS) {
|
|
206
|
+
if (!coalescedReloadTimers.has(widgetId)) {
|
|
207
|
+
coalescedReloadTimers.set(
|
|
208
|
+
widgetId,
|
|
209
|
+
setTimeout(() => {
|
|
210
|
+
coalescedReloadTimers.delete(widgetId);
|
|
211
|
+
requestReload(widgetId);
|
|
212
|
+
}, RELOAD_COALESCE_MS - sinceLast),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
196
215
|
return;
|
|
197
216
|
}
|
|
217
|
+
const coalesced = coalescedReloadTimers.get(widgetId);
|
|
218
|
+
if (coalesced) {
|
|
219
|
+
clearTimeout(coalesced);
|
|
220
|
+
coalescedReloadTimers.delete(widgetId);
|
|
221
|
+
}
|
|
198
222
|
lastReloadAtByWidgetId[widgetId] = now;
|
|
199
223
|
cancelPendingRefresh(widgetId);
|
|
200
224
|
const token = Math.max(now, (reloadTokens[widgetId] || 0) + 1);
|
|
@@ -541,6 +565,8 @@
|
|
|
541
565
|
return () => {
|
|
542
566
|
cancelAnimationFrame(initialSyncFrame);
|
|
543
567
|
for (const widgetId of [...pendingRefreshTokens.keys()]) cancelPendingRefresh(widgetId);
|
|
568
|
+
for (const timer of coalescedReloadTimers.values()) clearTimeout(timer);
|
|
569
|
+
coalescedReloadTimers.clear();
|
|
544
570
|
document.removeEventListener('pointerdown', handleDocumentPointerDown, true);
|
|
545
571
|
document.removeEventListener('keydown', handleDocumentKeydown);
|
|
546
572
|
window.removeEventListener('widgetic:widget-shape-pointer-block', onShapePointerBlock);
|
|
@@ -13,10 +13,26 @@
|
|
|
13
13
|
* (worker console output shows in DevTools) with the block duration and the last
|
|
14
14
|
* codegen status the main thread reported. It also reports recovery, so each
|
|
15
15
|
* freeze episode produces exactly two lines: block start + recovery.
|
|
16
|
+
*
|
|
17
|
+
* The duration alone never said *what* blocked, which is why the freeze survived
|
|
18
|
+
* several rounds of fixes. So the main thread also keeps breadcrumbs — long tasks
|
|
19
|
+
* seen by PerformanceObserver (with their iframe attribution, since a widget
|
|
20
|
+
* preview blocking the shared main thread looks identical to the page blocking
|
|
21
|
+
* itself) and explicit `measureSync` spans — and ships the ones it has not
|
|
22
|
+
* reported yet on the next pong. The pong that lands on recovery therefore
|
|
23
|
+
* carries the task that caused the freeze, named and measured.
|
|
16
24
|
*/
|
|
17
25
|
export interface FreezeWatchdog {
|
|
18
26
|
/** Main thread reports the current codegen status; included in freeze reports. */
|
|
19
27
|
setStatus(status: string): void;
|
|
28
|
+
/** Record a synchronous span so a freeze report can name it. */
|
|
29
|
+
addBreadcrumb(label: string, duration: number, source?: string): void;
|
|
20
30
|
dispose(): void;
|
|
21
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Times a synchronous step and hands it to the watchdog when it blocks long
|
|
34
|
+
* enough to matter. Cheap enough to leave on any suspect path permanently: two
|
|
35
|
+
* `performance.now()` reads and, for ordinary fast calls, nothing else.
|
|
36
|
+
*/
|
|
37
|
+
export declare function measureSync<T>(label: string, run: () => T): T;
|
|
22
38
|
export declare function installFreezeWatchdog(): FreezeWatchdog | null;
|
|
@@ -13,14 +13,31 @@
|
|
|
13
13
|
* (worker console output shows in DevTools) with the block duration and the last
|
|
14
14
|
* codegen status the main thread reported. It also reports recovery, so each
|
|
15
15
|
* freeze episode produces exactly two lines: block start + recovery.
|
|
16
|
+
*
|
|
17
|
+
* The duration alone never said *what* blocked, which is why the freeze survived
|
|
18
|
+
* several rounds of fixes. So the main thread also keeps breadcrumbs — long tasks
|
|
19
|
+
* seen by PerformanceObserver (with their iframe attribution, since a widget
|
|
20
|
+
* preview blocking the shared main thread looks identical to the page blocking
|
|
21
|
+
* itself) and explicit `measureSync` spans — and ships the ones it has not
|
|
22
|
+
* reported yet on the next pong. The pong that lands on recovery therefore
|
|
23
|
+
* carries the task that caused the freeze, named and measured.
|
|
16
24
|
*/
|
|
17
25
|
const WATCHDOG_SOURCE = `
|
|
18
26
|
let lastPongAt = performance.now();
|
|
19
27
|
let freezeStartedAt = null;
|
|
20
28
|
let reported = false;
|
|
21
29
|
let lastStatus = '';
|
|
30
|
+
let breadcrumbs = [];
|
|
22
31
|
const PING_MS = 500;
|
|
23
32
|
const STALL_MS = 4000;
|
|
33
|
+
|
|
34
|
+
function describeBreadcrumbs() {
|
|
35
|
+
if (breadcrumbs.length === 0) return 'no instrumented task over 200ms — the block is somewhere not yet measured';
|
|
36
|
+
return breadcrumbs
|
|
37
|
+
.map((b) => b.label + ' ' + Math.round(b.duration) + 'ms' + (b.source ? ' [' + b.source + ']' : ''))
|
|
38
|
+
.join(', ');
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
setInterval(() => {
|
|
25
42
|
postMessage({ type: 'ping' });
|
|
26
43
|
const now = performance.now();
|
|
@@ -33,18 +50,71 @@ setInterval(() => {
|
|
|
33
50
|
}
|
|
34
51
|
} else if (freezeStartedAt) {
|
|
35
52
|
console.warn('[FreezeReport] Main thread recovered after ' + Math.round(now - freezeStartedAt) +
|
|
36
|
-
'ms. Last status: "' + (lastStatus || '—') + '"');
|
|
53
|
+
'ms. Last status: "' + (lastStatus || '—') + '". Blocking work: ' + describeBreadcrumbs());
|
|
37
54
|
freezeStartedAt = null;
|
|
38
55
|
reported = false;
|
|
39
56
|
}
|
|
57
|
+
breadcrumbs = [];
|
|
40
58
|
}, PING_MS);
|
|
41
59
|
onmessage = (e) => {
|
|
42
60
|
if (e.data && e.data.type === 'pong') {
|
|
43
61
|
lastPongAt = performance.now();
|
|
44
62
|
if (e.data.status) lastStatus = e.data.status;
|
|
63
|
+
if (e.data.breadcrumbs && e.data.breadcrumbs.length) {
|
|
64
|
+
breadcrumbs = breadcrumbs.concat(e.data.breadcrumbs).slice(-12);
|
|
65
|
+
}
|
|
45
66
|
}
|
|
46
67
|
};
|
|
47
68
|
`;
|
|
69
|
+
/** Long tasks and spans below this are ordinary work, not a freeze. */
|
|
70
|
+
const BREADCRUMB_MIN_MS = 200;
|
|
71
|
+
/**
|
|
72
|
+
* Times a synchronous step and hands it to the watchdog when it blocks long
|
|
73
|
+
* enough to matter. Cheap enough to leave on any suspect path permanently: two
|
|
74
|
+
* `performance.now()` reads and, for ordinary fast calls, nothing else.
|
|
75
|
+
*/
|
|
76
|
+
export function measureSync(label, run) {
|
|
77
|
+
if (typeof performance === 'undefined')
|
|
78
|
+
return run();
|
|
79
|
+
const startedAt = performance.now();
|
|
80
|
+
try {
|
|
81
|
+
return run();
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
const duration = performance.now() - startedAt;
|
|
85
|
+
if (duration >= BREADCRUMB_MIN_MS) {
|
|
86
|
+
const watchdog = globalThis.__widgeticFreezeWatchdog;
|
|
87
|
+
watchdog?.addBreadcrumb(label, duration);
|
|
88
|
+
console.warn(`[MainThreadBlock] ${label} took ${Math.round(duration)}ms`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Long tasks the page did not measure itself, including ones inside an iframe. */
|
|
93
|
+
function observeLongTasks(watchdog) {
|
|
94
|
+
if (typeof PerformanceObserver === 'undefined')
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
const observer = new PerformanceObserver((list) => {
|
|
98
|
+
for (const entry of list.getEntries()) {
|
|
99
|
+
if (entry.duration < BREADCRUMB_MIN_MS)
|
|
100
|
+
continue;
|
|
101
|
+
// `containerSrc` is the one signal that separates the page blocking
|
|
102
|
+
// itself from a widget preview blocking the main thread it shares.
|
|
103
|
+
const attribution = entry.attribution?.[0];
|
|
104
|
+
const source = attribution?.containerSrc
|
|
105
|
+
|| attribution?.containerName
|
|
106
|
+
|| attribution?.containerType
|
|
107
|
+
|| undefined;
|
|
108
|
+
watchdog.addBreadcrumb('longtask', entry.duration, source);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
observer.observe({ entryTypes: ['longtask'] });
|
|
112
|
+
return () => observer.disconnect();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
48
118
|
export function installFreezeWatchdog() {
|
|
49
119
|
if (typeof window === 'undefined' || typeof Worker === 'undefined' || typeof Blob === 'undefined') {
|
|
50
120
|
return null;
|
|
@@ -57,20 +127,35 @@ export function installFreezeWatchdog() {
|
|
|
57
127
|
const worker = new Worker(url);
|
|
58
128
|
URL.revokeObjectURL(url);
|
|
59
129
|
let lastStatus = '';
|
|
130
|
+
let pendingBreadcrumbs = [];
|
|
60
131
|
worker.onmessage = (event) => {
|
|
61
132
|
if (event.data?.type === 'ping') {
|
|
62
|
-
worker.postMessage({ type: 'pong', status: lastStatus });
|
|
133
|
+
worker.postMessage({ type: 'pong', status: lastStatus, breadcrumbs: pendingBreadcrumbs });
|
|
134
|
+
pendingBreadcrumbs = [];
|
|
63
135
|
}
|
|
64
136
|
};
|
|
137
|
+
let stopLongTaskObserver = null;
|
|
65
138
|
const watchdog = {
|
|
66
139
|
setStatus(status) {
|
|
67
140
|
lastStatus = status;
|
|
68
141
|
},
|
|
142
|
+
addBreadcrumb(label, duration, source) {
|
|
143
|
+
if (duration < BREADCRUMB_MIN_MS)
|
|
144
|
+
return;
|
|
145
|
+
pendingBreadcrumbs.push({ label, duration, source });
|
|
146
|
+
// Keep only the worst offenders: a freeze is one big task, not fifty.
|
|
147
|
+
if (pendingBreadcrumbs.length > 12) {
|
|
148
|
+
pendingBreadcrumbs.sort((a, b) => b.duration - a.duration);
|
|
149
|
+
pendingBreadcrumbs = pendingBreadcrumbs.slice(0, 12);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
69
152
|
dispose() {
|
|
153
|
+
stopLongTaskObserver?.();
|
|
70
154
|
worker.terminate();
|
|
71
155
|
delete window.__widgeticFreezeWatchdog;
|
|
72
156
|
},
|
|
73
157
|
};
|
|
74
158
|
window.__widgeticFreezeWatchdog = watchdog;
|
|
159
|
+
stopLongTaskObserver = observeLongTasks(watchdog);
|
|
75
160
|
return watchdog;
|
|
76
161
|
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { WidgetsApi } from '@widgetic/api-sdk';
|
|
2
2
|
/**
|
|
3
|
-
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API
|
|
3
|
+
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API
|
|
4
|
+
* gateway.
|
|
5
|
+
*
|
|
6
|
+
* This used to be a hand-rolled fetch: the gateway route
|
|
7
|
+
* (`POST /widgets/{id}/thumbnail`) postdated the last SDK generation, so the
|
|
8
|
+
* module read `basePath` and the bearer token back out of the `WidgetsApi`
|
|
9
|
+
* instance's `Configuration` and hit the endpoint itself, unwrapping the
|
|
10
|
+
* response against four guessed shapes. The spec now carries the operation
|
|
11
|
+
* with a typed 200 (`UploadWidgetThumbnail200Response.data.thumbnailUrl`), the
|
|
12
|
+
* SDK was regenerated, and the fetch is retired — the typed method targets the
|
|
13
|
+
* same host the client was constructed with, which is what the Configuration
|
|
14
|
+
* read-back was approximating.
|
|
4
15
|
*/
|
|
5
16
|
export declare function persistWidgetThumbnail(widgetsClient: WidgetsApi | null | undefined, widgetId: string | null | undefined, imageDataUrl: string | null | undefined): Promise<string | null>;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API
|
|
2
|
+
* Upload a convert/preview screenshot to widgets.thumbnail_url via the API
|
|
3
|
+
* gateway.
|
|
4
|
+
*
|
|
5
|
+
* This used to be a hand-rolled fetch: the gateway route
|
|
6
|
+
* (`POST /widgets/{id}/thumbnail`) postdated the last SDK generation, so the
|
|
7
|
+
* module read `basePath` and the bearer token back out of the `WidgetsApi`
|
|
8
|
+
* instance's `Configuration` and hit the endpoint itself, unwrapping the
|
|
9
|
+
* response against four guessed shapes. The spec now carries the operation
|
|
10
|
+
* with a typed 200 (`UploadWidgetThumbnail200Response.data.thumbnailUrl`), the
|
|
11
|
+
* SDK was regenerated, and the fetch is retired — the typed method targets the
|
|
12
|
+
* same host the client was constructed with, which is what the Configuration
|
|
13
|
+
* read-back was approximating.
|
|
3
14
|
*/
|
|
4
15
|
export async function persistWidgetThumbnail(widgetsClient, widgetId, imageDataUrl) {
|
|
5
16
|
if (!widgetsClient || !widgetId || typeof imageDataUrl !== 'string')
|
|
@@ -7,15 +18,11 @@ export async function persistWidgetThumbnail(widgetsClient, widgetId, imageDataU
|
|
|
7
18
|
if (!imageDataUrl.startsWith('data:image/'))
|
|
8
19
|
return null;
|
|
9
20
|
try {
|
|
10
|
-
const response = await widgetsClient.
|
|
21
|
+
const response = await widgetsClient.uploadWidgetThumbnail({
|
|
11
22
|
id: widgetId,
|
|
12
23
|
uploadWidgetThumbnailRequest: { imageDataUrl },
|
|
13
24
|
});
|
|
14
|
-
const
|
|
15
|
-
const thumbnailUrl = json?.data?.thumbnailUrl ||
|
|
16
|
-
json?.data?.thumbnail_url ||
|
|
17
|
-
json?.thumbnailUrl ||
|
|
18
|
-
null;
|
|
25
|
+
const thumbnailUrl = response.data?.thumbnailUrl ?? null;
|
|
19
26
|
if (thumbnailUrl) {
|
|
20
27
|
console.log('[persistWidgetThumbnail] stored', widgetId.slice(0, 8), thumbnailUrl.slice(0, 80));
|
|
21
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@widgetic/creator",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.66",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@sveltejs/kit": "^2.20.0",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"dev-sv": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") VITE_BUILD_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo local) NODE_ENV=development vite dev --port 5174",
|
|
46
46
|
"lps": "volta run npm link --save @widgetic/design-system && volta run npm link --save @widgetic/api-sdk && volta run npm link --save @widgetic/canvas",
|
|
47
47
|
"llinks": "ls -la node_modules/@widgetic",
|
|
48
|
+
"ldt": "volta run npm link --save @widgetic/dev-tools",
|
|
48
49
|
"clean-npm-cache": "npm cache clean --force",
|
|
49
50
|
"clean-svelte-cache": "rm -rf .svelte-kit && npx svelte-kit sync",
|
|
50
51
|
"clean-vite-cache": "rm -rf node_modules/.vite",
|
|
@@ -67,12 +68,13 @@
|
|
|
67
68
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
68
69
|
"@tailwindcss/postcss": "^4.1.4",
|
|
69
70
|
"@tailwindcss/typography": "^0.5.15",
|
|
70
|
-
"@types/
|
|
71
|
+
"@types/node": "26.4.0",
|
|
71
72
|
"@widgetic/api-sdk": "^1.0.10",
|
|
72
73
|
"@widgetic/cache-layer": "^0.1.2",
|
|
73
74
|
"@widgetic/canvas": "^0.5.4",
|
|
74
75
|
"@widgetic/chat": "^0.1.4",
|
|
75
76
|
"@widgetic/design-system": "^0.5.8",
|
|
77
|
+
"@widgetic/dev-tools": "^0.2.0",
|
|
76
78
|
"@widgetic/editor": "^4.0.1",
|
|
77
79
|
"@widgetic/file-browser": "^0.5.17",
|
|
78
80
|
"autoprefixer": "^10.4.20",
|