@widgetic/creator 0.3.49
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 +116 -0
- package/dist/CreatorApp.svelte +11821 -0
- package/dist/CreatorApp.svelte.d.ts +41 -0
- package/dist/components/EditableName.svelte +94 -0
- package/dist/components/EditableName.svelte.d.ts +27 -0
- package/dist/components/SelectorDropdown.svelte +238 -0
- package/dist/components/SelectorDropdown.svelte.d.ts +41 -0
- package/dist/components/WidgetDetails.svelte +3127 -0
- package/dist/components/WidgetDetails.svelte.d.ts +235 -0
- package/dist/components/index.d.ts +0 -0
- package/dist/components/index.js +4 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/creator-types.d.ts +64 -0
- package/dist/creator-types.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/localCacheGate.d.ts +7 -0
- package/dist/localCacheGate.js +29 -0
- package/dist/pageHelpers.d.ts +15 -0
- package/dist/pageHelpers.js +194 -0
- package/dist/stores/userSession.d.ts +7 -0
- package/dist/stores/userSession.js +32 -0
- package/dist/stores/websocketStore.d.ts +53 -0
- package/dist/stores/websocketStore.js +289 -0
- package/dist/syncSiteAuth.d.ts +9 -0
- package/dist/syncSiteAuth.js +53 -0
- package/dist/utils/creatorDraftStorage.d.ts +17 -0
- package/dist/utils/creatorDraftStorage.js +87 -0
- package/dist/utils/embedCode.d.ts +51 -0
- package/dist/utils/embedCode.js +69 -0
- package/dist/utils/models.d.ts +4 -0
- package/dist/utils/models.js +94 -0
- package/dist/utils/operationStream.d.ts +29 -0
- package/dist/utils/operationStream.js +116 -0
- package/dist/utils/prototypes.d.ts +0 -0
- package/dist/utils/prototypes.js +20 -0
- package/dist/utils/widgeticChatUpload.d.ts +26 -0
- package/dist/utils/widgeticChatUpload.js +148 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +38 -0
- package/package.json +124 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Store
|
|
3
|
+
*
|
|
4
|
+
* Manages WebSocket connection for real-time events:
|
|
5
|
+
* - Code generation status (operation progress)
|
|
6
|
+
* - Widget publish status (build completion)
|
|
7
|
+
* - Dynamic Worker preview readiness
|
|
8
|
+
*/
|
|
9
|
+
import { writable, get } from 'svelte/store';
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Stores
|
|
12
|
+
// =============================================================================
|
|
13
|
+
export const connectionState = writable({
|
|
14
|
+
connected: false,
|
|
15
|
+
joinedRooms: [],
|
|
16
|
+
error: null
|
|
17
|
+
});
|
|
18
|
+
export const dynamicWorkerPreviewStatus = writable({
|
|
19
|
+
widgetId: null,
|
|
20
|
+
previewUrl: null,
|
|
21
|
+
buildTimeMs: null,
|
|
22
|
+
timestamp: null
|
|
23
|
+
});
|
|
24
|
+
export const widgetPublishStatus = writable({
|
|
25
|
+
widgetId: null,
|
|
26
|
+
status: null,
|
|
27
|
+
version: undefined,
|
|
28
|
+
artifactUrl: undefined,
|
|
29
|
+
message: undefined,
|
|
30
|
+
error: undefined,
|
|
31
|
+
timestamp: null
|
|
32
|
+
});
|
|
33
|
+
export const codeGenerationStatus = writable({
|
|
34
|
+
operationId: null,
|
|
35
|
+
status: null,
|
|
36
|
+
previousStatus: null,
|
|
37
|
+
progress: 0,
|
|
38
|
+
error: null,
|
|
39
|
+
timestamp: null
|
|
40
|
+
});
|
|
41
|
+
export const agentHarnessOutput = writable(null);
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Socket.IO Connection
|
|
44
|
+
// =============================================================================
|
|
45
|
+
let socket = null;
|
|
46
|
+
let socketImportPromise = null;
|
|
47
|
+
let currentAuthToken = null;
|
|
48
|
+
let lastTokenUsed = null;
|
|
49
|
+
let activeUserId = null;
|
|
50
|
+
/** Runtime API root for embedded hosts (site PUBLIC_API_URL). Overrides VITE_API_URL. */
|
|
51
|
+
let runtimeApiUrl = null;
|
|
52
|
+
const showLogs = true;
|
|
53
|
+
export function setAuthToken(token) {
|
|
54
|
+
currentAuthToken = token;
|
|
55
|
+
if (token !== lastTokenUsed && socket) {
|
|
56
|
+
if (showLogs)
|
|
57
|
+
console.log('[WsStore] Token changed, will recreate socket on next connect');
|
|
58
|
+
if (socket.connected) {
|
|
59
|
+
socket.disconnect();
|
|
60
|
+
}
|
|
61
|
+
socket = null;
|
|
62
|
+
socketImportPromise = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Prefer host-provided API URL so embedded creator does not socket.io to localhost:3000. */
|
|
66
|
+
export function setWebsocketApiUrl(apiUrl) {
|
|
67
|
+
const normalized = apiUrl ? String(apiUrl).replace(/\/+$/, '') : null;
|
|
68
|
+
if (normalized === runtimeApiUrl)
|
|
69
|
+
return;
|
|
70
|
+
runtimeApiUrl = normalized;
|
|
71
|
+
if (socket) {
|
|
72
|
+
if (showLogs)
|
|
73
|
+
console.log('[WsStore] API URL changed, recreating socket on next connect:', runtimeApiUrl);
|
|
74
|
+
if (socket.connected) {
|
|
75
|
+
socket.disconnect();
|
|
76
|
+
}
|
|
77
|
+
socket = null;
|
|
78
|
+
socketImportPromise = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function getSocket() {
|
|
82
|
+
if (socket && currentAuthToken !== lastTokenUsed) {
|
|
83
|
+
if (showLogs)
|
|
84
|
+
console.log('[WsStore] Recreating socket with new token');
|
|
85
|
+
if (socket.connected) {
|
|
86
|
+
socket.disconnect();
|
|
87
|
+
}
|
|
88
|
+
socket = null;
|
|
89
|
+
socketImportPromise = null;
|
|
90
|
+
}
|
|
91
|
+
if (socket)
|
|
92
|
+
return socket;
|
|
93
|
+
if (!socketImportPromise) {
|
|
94
|
+
socketImportPromise = import('socket.io-client').then(({ io }) => {
|
|
95
|
+
// Prefer runtime override (embedded hosts), then build-time VITE_API_URL
|
|
96
|
+
const apiUrl = (runtimeApiUrl || import.meta.env.VITE_API_URL || 'http://localhost:3000').replace(/\/+$/, '');
|
|
97
|
+
lastTokenUsed = currentAuthToken;
|
|
98
|
+
if (showLogs)
|
|
99
|
+
console.log('[WsStore] Creating socket with auth token:', currentAuthToken ? 'present' : 'missing', 'api:', apiUrl);
|
|
100
|
+
socket = io(apiUrl, {
|
|
101
|
+
autoConnect: false,
|
|
102
|
+
transports: ['websocket', 'polling'],
|
|
103
|
+
reconnection: true,
|
|
104
|
+
reconnectionAttempts: 5,
|
|
105
|
+
reconnectionDelay: 1000,
|
|
106
|
+
auth: currentAuthToken ? { token: currentAuthToken } : undefined
|
|
107
|
+
});
|
|
108
|
+
setupSocketListeners(socket);
|
|
109
|
+
return socket;
|
|
110
|
+
}).catch(err => {
|
|
111
|
+
console.error('[WsStore] Failed to load socket.io-client:', err);
|
|
112
|
+
return null;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return socketImportPromise;
|
|
116
|
+
}
|
|
117
|
+
function setupSocketListeners(sock) {
|
|
118
|
+
if (!sock)
|
|
119
|
+
return;
|
|
120
|
+
sock.on('connect', () => {
|
|
121
|
+
if (showLogs)
|
|
122
|
+
console.log('[WsStore] WebSocket connected');
|
|
123
|
+
connectionState.update(s => ({ ...s, connected: true, error: null }));
|
|
124
|
+
if (activeUserId) {
|
|
125
|
+
sock.emit('joinRoom', { room: `user-${activeUserId}` });
|
|
126
|
+
if (showLogs)
|
|
127
|
+
console.log('[WsStore] Re-joining user room after reconnect:', `user-${activeUserId}`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
sock.on('disconnect', (reason) => {
|
|
131
|
+
if (showLogs)
|
|
132
|
+
console.log('[WsStore] WebSocket disconnected:', reason);
|
|
133
|
+
connectionState.update(s => ({ ...s, connected: false, joinedRooms: [] }));
|
|
134
|
+
});
|
|
135
|
+
sock.on('connect_error', (err) => {
|
|
136
|
+
if (showLogs)
|
|
137
|
+
console.error('[WsStore] WebSocket connection error:', err.message);
|
|
138
|
+
connectionState.update(s => ({ ...s, connected: false, error: err.message }));
|
|
139
|
+
});
|
|
140
|
+
// =========================================================================
|
|
141
|
+
// Widget Publish Events
|
|
142
|
+
// =========================================================================
|
|
143
|
+
sock.on('widget_publish_status', (data) => {
|
|
144
|
+
if (showLogs)
|
|
145
|
+
console.log('[WsStore] Widget publish status received:', data);
|
|
146
|
+
widgetPublishStatus.set({
|
|
147
|
+
widgetId: data.widgetId ?? null,
|
|
148
|
+
status: data.status ?? null,
|
|
149
|
+
version: data.version,
|
|
150
|
+
artifactUrl: data.artifactUrl,
|
|
151
|
+
message: data.message,
|
|
152
|
+
error: data.error,
|
|
153
|
+
timestamp: new Date().toISOString()
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
sock.on('publish:complete', (data) => {
|
|
157
|
+
if (showLogs)
|
|
158
|
+
console.log('[WsStore] Publish complete received:', data);
|
|
159
|
+
widgetPublishStatus.set({
|
|
160
|
+
widgetId: data.widgetId ?? null,
|
|
161
|
+
status: 'success',
|
|
162
|
+
version: data.versionPublished,
|
|
163
|
+
artifactUrl: data.artifactUrl,
|
|
164
|
+
message: data.message,
|
|
165
|
+
error: undefined,
|
|
166
|
+
timestamp: new Date().toISOString()
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
sock.on('build:error', (data) => {
|
|
170
|
+
if (showLogs)
|
|
171
|
+
console.log('[WsStore] Build error received:', data);
|
|
172
|
+
widgetPublishStatus.set({
|
|
173
|
+
widgetId: data.widgetId ?? null,
|
|
174
|
+
status: 'failure',
|
|
175
|
+
version: data.version,
|
|
176
|
+
artifactUrl: undefined,
|
|
177
|
+
message: data.message ?? 'Build failed.',
|
|
178
|
+
error: data.error,
|
|
179
|
+
timestamp: new Date().toISOString()
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
// =========================================================================
|
|
183
|
+
// Code Generation Status
|
|
184
|
+
// =========================================================================
|
|
185
|
+
sock.on('code_generation_status', (data) => {
|
|
186
|
+
if (showLogs)
|
|
187
|
+
console.log('[WsStore] Code generation status received:', data);
|
|
188
|
+
codeGenerationStatus.set({
|
|
189
|
+
operationId: data.operationId ?? null,
|
|
190
|
+
status: data.status ?? null,
|
|
191
|
+
previousStatus: data.previousStatus ?? null,
|
|
192
|
+
progress: data.progress ?? 0,
|
|
193
|
+
error: data.error ?? null,
|
|
194
|
+
timestamp: data.timestamp ?? new Date().toISOString()
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
// =========================================================================
|
|
198
|
+
// Dynamic Worker Preview
|
|
199
|
+
// =========================================================================
|
|
200
|
+
sock.on('vmConsole:aiderOutput', (data) => {
|
|
201
|
+
const output = typeof data?.output === 'string' ? data.output.trim() : '';
|
|
202
|
+
if (!output)
|
|
203
|
+
return;
|
|
204
|
+
if (showLogs)
|
|
205
|
+
console.log('[WsStore] Agent harness output:', output.slice(0, 120));
|
|
206
|
+
agentHarnessOutput.set({
|
|
207
|
+
output,
|
|
208
|
+
timestamp: new Date().toISOString()
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
sock.on('dynamic_worker_preview_ready', (data) => {
|
|
212
|
+
if (showLogs)
|
|
213
|
+
console.log('[WsStore] Dynamic Worker preview ready!', {
|
|
214
|
+
widgetId: data.widgetId,
|
|
215
|
+
previewUrl: data.previewUrl?.substring(0, 60) + '...',
|
|
216
|
+
buildTimeMs: data.buildTimeMs
|
|
217
|
+
});
|
|
218
|
+
dynamicWorkerPreviewStatus.set({
|
|
219
|
+
widgetId: data.widgetId,
|
|
220
|
+
previewUrl: data.previewUrl,
|
|
221
|
+
buildTimeMs: data.buildTimeMs,
|
|
222
|
+
timestamp: new Date().toISOString()
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// =============================================================================
|
|
227
|
+
// Public Functions
|
|
228
|
+
// =============================================================================
|
|
229
|
+
/**
|
|
230
|
+
* Connect to WebSocket and join user room for receiving events.
|
|
231
|
+
*/
|
|
232
|
+
export async function connectWebSocket(userId) {
|
|
233
|
+
const sock = await getSocket();
|
|
234
|
+
if (!sock) {
|
|
235
|
+
if (showLogs)
|
|
236
|
+
console.error('[WsStore] Socket not available');
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
if (userId)
|
|
240
|
+
activeUserId = userId;
|
|
241
|
+
if (!sock.connected) {
|
|
242
|
+
sock.connect();
|
|
243
|
+
}
|
|
244
|
+
return new Promise((resolve) => {
|
|
245
|
+
const timeout = setTimeout(() => {
|
|
246
|
+
if (showLogs)
|
|
247
|
+
console.error('[WsStore] Connection timeout');
|
|
248
|
+
resolve(false);
|
|
249
|
+
}, 5000);
|
|
250
|
+
const tryJoin = () => {
|
|
251
|
+
if (sock.connected) {
|
|
252
|
+
clearTimeout(timeout);
|
|
253
|
+
if (userId) {
|
|
254
|
+
sock.emit('joinRoom', { room: `user-${userId}` });
|
|
255
|
+
if (showLogs)
|
|
256
|
+
console.log('[WsStore] Joined user room:', `user-${userId}`);
|
|
257
|
+
}
|
|
258
|
+
connectionState.set({
|
|
259
|
+
connected: true,
|
|
260
|
+
joinedRooms: userId ? [`user-${userId}`] : [],
|
|
261
|
+
error: null
|
|
262
|
+
});
|
|
263
|
+
resolve(true);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
sock.once('connect', tryJoin);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
tryJoin();
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Disconnect from WebSocket.
|
|
274
|
+
*/
|
|
275
|
+
export async function disconnectWebSocket() {
|
|
276
|
+
const sock = await getSocket();
|
|
277
|
+
if (!sock)
|
|
278
|
+
return;
|
|
279
|
+
const state = get(connectionState);
|
|
280
|
+
if (state.joinedRooms.length > 0) {
|
|
281
|
+
state.joinedRooms.forEach(room => {
|
|
282
|
+
sock.emit('leave', room);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
connectionState.update(s => ({
|
|
286
|
+
...s,
|
|
287
|
+
joinedRooms: []
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync creator session from site auth (localStorage.session.accessToken).
|
|
3
|
+
* Used when CreatorApp is embedded in Frontend/site on the same origin.
|
|
4
|
+
*
|
|
5
|
+
* Dexie L1 owner is applied only when enableLocalCache was turned on via
|
|
6
|
+
* setEnableLocalCache (CreatorApp prop). Site's authStore owns cache in the
|
|
7
|
+
* site bundle; creator has a separate in-memory owner and must opt in.
|
|
8
|
+
*/
|
|
9
|
+
export declare function syncSessionFromSiteAuth(): boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { decodeJWT } from './pageHelpers';
|
|
2
|
+
import { setSession } from './stores/userSession';
|
|
3
|
+
import { syncLocalCacheOwner } from './localCacheGate.js';
|
|
4
|
+
/**
|
|
5
|
+
* Sync creator session from site auth (localStorage.session.accessToken).
|
|
6
|
+
* Used when CreatorApp is embedded in Frontend/site on the same origin.
|
|
7
|
+
*
|
|
8
|
+
* Dexie L1 owner is applied only when enableLocalCache was turned on via
|
|
9
|
+
* setEnableLocalCache (CreatorApp prop). Site's authStore owns cache in the
|
|
10
|
+
* site bundle; creator has a separate in-memory owner and must opt in.
|
|
11
|
+
*/
|
|
12
|
+
export function syncSessionFromSiteAuth() {
|
|
13
|
+
if (typeof window === 'undefined')
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
const sessionStr = localStorage.getItem('session');
|
|
17
|
+
if (!sessionStr) {
|
|
18
|
+
console.warn('[CreatorApp] No site session found in localStorage');
|
|
19
|
+
syncLocalCacheOwner(null);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const session = JSON.parse(sessionStr);
|
|
23
|
+
const token = session?.accessToken || session?.access_token;
|
|
24
|
+
if (!token) {
|
|
25
|
+
console.warn('[CreatorApp] Site session has no access token');
|
|
26
|
+
syncLocalCacheOwner(null);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const decoded = decodeJWT(token);
|
|
30
|
+
const user = decoded
|
|
31
|
+
? {
|
|
32
|
+
id: decoded.sub || decoded.user_id || decoded.id || 'site-user',
|
|
33
|
+
email: decoded.email || decoded.user_metadata?.email || session?.user?.email,
|
|
34
|
+
name: decoded.user_metadata?.name ||
|
|
35
|
+
decoded.user_metadata?.full_name ||
|
|
36
|
+
decoded.name ||
|
|
37
|
+
session?.user?.name ||
|
|
38
|
+
'User',
|
|
39
|
+
role: decoded.role || 'user',
|
|
40
|
+
avatar: decoded.user_metadata?.avatar_url || decoded.user_metadata?.picture
|
|
41
|
+
}
|
|
42
|
+
: session?.user || { id: 'site-user', email: 'user@widgetic.com', name: 'User' };
|
|
43
|
+
// setSession → syncLocalCacheOwner when enableLocalCache is on
|
|
44
|
+
setSession(user, token);
|
|
45
|
+
console.log('[CreatorApp] Session synced from site auth');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('[CreatorApp] Failed to sync session from site:', error);
|
|
50
|
+
syncLocalCacheOwner(null);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface WidgetDraft {
|
|
2
|
+
promptText: string;
|
|
3
|
+
chatDraft: string;
|
|
4
|
+
images: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function saveWidgetDraft(widgetId: string, draft: Partial<WidgetDraft>): Promise<void>;
|
|
7
|
+
export declare function readWidgetDraft(widgetId: string): Promise<Partial<WidgetDraft> | null>;
|
|
8
|
+
export declare function deleteWidgetDraft(widgetId: string): Promise<void>;
|
|
9
|
+
/** Bulk-read all drafts for the owner (keyed by widgetId). */
|
|
10
|
+
export declare function readAllWidgetDrafts(): Promise<Record<string, WidgetDraft>>;
|
|
11
|
+
/** Copy one widget's draft to another (duplicate-widget support). */
|
|
12
|
+
export declare function copyWidgetDraft(sourceWidgetId: string, targetWidgetId: string): Promise<void>;
|
|
13
|
+
export declare function saveCanvasState(canvasId: string | null, json: string): void;
|
|
14
|
+
export declare function readCanvasState(canvasId: string | null): Promise<string | null>;
|
|
15
|
+
export declare function saveActiveGeneration(widgetId: string, operationId: string): void;
|
|
16
|
+
export declare function readActiveGeneration(widgetId: string): Promise<string | null>;
|
|
17
|
+
export declare function clearActiveGeneration(widgetId: string): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable draft + selection storage for the widget creator.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the per-widget localStorage family (prompt text, prompt images,
|
|
5
|
+
* chat drafts, canvas-state fallback JSON, active generation id) with the
|
|
6
|
+
* shared L1 store. Keys are owner-scoped inside @widgetic/cache-layer.
|
|
7
|
+
* Cross-tab generation/publishing flags intentionally remain in
|
|
8
|
+
* localStorage — they must be visible to other tabs, not just this one.
|
|
9
|
+
*/
|
|
10
|
+
import { deleteEntity, getCacheOwner, readAllEntitiesForKind, readEntity, writeEntity } from '@widgetic/cache-layer';
|
|
11
|
+
const DRAFT_KIND = 'creatorDrafts';
|
|
12
|
+
const CANVAS_KIND = 'creatorCanvasState';
|
|
13
|
+
const GENERATION_KIND = 'creatorActiveGeneration';
|
|
14
|
+
function ownerIdOrNull() {
|
|
15
|
+
return getCacheOwner();
|
|
16
|
+
}
|
|
17
|
+
export async function saveWidgetDraft(widgetId, draft) {
|
|
18
|
+
const ownerId = ownerIdOrNull();
|
|
19
|
+
if (!ownerId)
|
|
20
|
+
return;
|
|
21
|
+
const existing = (await readEntity(ownerId, DRAFT_KIND, widgetId)) ?? {
|
|
22
|
+
promptText: '',
|
|
23
|
+
chatDraft: '',
|
|
24
|
+
images: []
|
|
25
|
+
};
|
|
26
|
+
await writeEntity(ownerId, DRAFT_KIND, widgetId, { ...existing, ...draft });
|
|
27
|
+
}
|
|
28
|
+
export async function readWidgetDraft(widgetId) {
|
|
29
|
+
const ownerId = ownerIdOrNull();
|
|
30
|
+
if (!ownerId)
|
|
31
|
+
return null;
|
|
32
|
+
return readEntity(ownerId, DRAFT_KIND, widgetId);
|
|
33
|
+
}
|
|
34
|
+
export async function deleteWidgetDraft(widgetId) {
|
|
35
|
+
const ownerId = ownerIdOrNull();
|
|
36
|
+
if (!ownerId)
|
|
37
|
+
return;
|
|
38
|
+
await deleteEntity(ownerId, DRAFT_KIND, widgetId);
|
|
39
|
+
}
|
|
40
|
+
/** Bulk-read all drafts for the owner (keyed by widgetId). */
|
|
41
|
+
export async function readAllWidgetDrafts() {
|
|
42
|
+
const ownerId = ownerIdOrNull();
|
|
43
|
+
if (!ownerId)
|
|
44
|
+
return {};
|
|
45
|
+
return readAllEntitiesForKind(ownerId, DRAFT_KIND);
|
|
46
|
+
}
|
|
47
|
+
/** Copy one widget's draft to another (duplicate-widget support). */
|
|
48
|
+
export async function copyWidgetDraft(sourceWidgetId, targetWidgetId) {
|
|
49
|
+
const source = await readWidgetDraft(sourceWidgetId);
|
|
50
|
+
if (!source)
|
|
51
|
+
return;
|
|
52
|
+
await saveWidgetDraft(targetWidgetId, source);
|
|
53
|
+
}
|
|
54
|
+
// ─── Canvas state fallback ───────────────────────────────────────
|
|
55
|
+
export function saveCanvasState(canvasId, json) {
|
|
56
|
+
const ownerId = ownerIdOrNull();
|
|
57
|
+
if (!ownerId)
|
|
58
|
+
return;
|
|
59
|
+
void writeEntity(ownerId, CANVAS_KIND, canvasId ?? 'default', json).catch((error) => {
|
|
60
|
+
console.warn('[creatorDraftStorage] Failed to persist canvas state:', error);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export async function readCanvasState(canvasId) {
|
|
64
|
+
const ownerId = ownerIdOrNull();
|
|
65
|
+
if (!ownerId)
|
|
66
|
+
return null;
|
|
67
|
+
return readEntity(ownerId, CANVAS_KIND, canvasId ?? 'default');
|
|
68
|
+
}
|
|
69
|
+
// ─── Active generation id ────────────────────────────────────────
|
|
70
|
+
export function saveActiveGeneration(widgetId, operationId) {
|
|
71
|
+
const ownerId = ownerIdOrNull();
|
|
72
|
+
if (!ownerId)
|
|
73
|
+
return;
|
|
74
|
+
void writeEntity(ownerId, GENERATION_KIND, widgetId, operationId);
|
|
75
|
+
}
|
|
76
|
+
export async function readActiveGeneration(widgetId) {
|
|
77
|
+
const ownerId = ownerIdOrNull();
|
|
78
|
+
if (!ownerId)
|
|
79
|
+
return null;
|
|
80
|
+
return readEntity(ownerId, GENERATION_KIND, widgetId);
|
|
81
|
+
}
|
|
82
|
+
export async function clearActiveGeneration(widgetId) {
|
|
83
|
+
const ownerId = ownerIdOrNull();
|
|
84
|
+
if (!ownerId)
|
|
85
|
+
return;
|
|
86
|
+
await deleteEntity(ownerId, GENERATION_KIND, widgetId);
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed code generation utilities for the Embed step.
|
|
3
|
+
*
|
|
4
|
+
* Two formats are supported:
|
|
5
|
+
* - `iframe`: classic `<iframe>` pointing at the published widget HTML on the CDN.
|
|
6
|
+
* - `web-component`: customer-facing `<widgetic>` host tag. The embed SDK
|
|
7
|
+
* scans for this tag and hydrates it. (Autonomous custom elements must
|
|
8
|
+
* contain a hyphen, so the SDK does not `define("widgetic")` — it finds
|
|
9
|
+
* `<widgetic>` in the DOM and renders into it.)
|
|
10
|
+
*
|
|
11
|
+
* The SDK script is served from `cdn.widgetic.com/sdks/browser/embed-sdk.js`
|
|
12
|
+
* (R2 custom domain on the `widgets` bucket — see AGENTS.md embed architecture).
|
|
13
|
+
*/
|
|
14
|
+
export type EmbedFormat = 'iframe' | 'web-component';
|
|
15
|
+
export declare const EMBED_FORMATS: EmbedFormat[];
|
|
16
|
+
export declare const EMBED_FORMAT_LABELS: Record<EmbedFormat, string>;
|
|
17
|
+
export interface EmbedCodeParams {
|
|
18
|
+
/** Direct widget HTML URL on the CDN (or API proxy in dev). */
|
|
19
|
+
embedSrc: string;
|
|
20
|
+
/** Composition id, if a composition is selected. */
|
|
21
|
+
compositionId?: string | null;
|
|
22
|
+
/** Embed width in pixels. */
|
|
23
|
+
width: number;
|
|
24
|
+
/** Embed height in pixels. */
|
|
25
|
+
height: number;
|
|
26
|
+
/** CDN base URL for the embed SDK script. Defaults to https://cdn.widgetic.com */
|
|
27
|
+
sdkBaseUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the iframe embed snippet. Backward compatible with the previous
|
|
31
|
+
* hardcoded block in `CreatorApp.svelte`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateIframeEmbedCode({ embedSrc, width, height }: EmbedCodeParams): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build the Web Component embed snippet using `<widgetic>`.
|
|
36
|
+
* Requires a composition id — the custom element loader fetches the
|
|
37
|
+
* composition + widget module from the CDN via the embed-sdk batch API.
|
|
38
|
+
*
|
|
39
|
+
* If no composition is selected we fall back to the iframe format
|
|
40
|
+
* (caller should check `compositionId` before calling this).
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateWebComponentEmbedCode({ compositionId, width, height, sdkBaseUrl }: EmbedCodeParams): string;
|
|
43
|
+
/**
|
|
44
|
+
* Pick the right embed code for the requested format, with fallback to
|
|
45
|
+
* iframe when the Web Component format is requested but no composition
|
|
46
|
+
* is selected (custom element requires `composition-id`).
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateEmbedCode(format: EmbedFormat, params: EmbedCodeParams): {
|
|
49
|
+
code: string;
|
|
50
|
+
effectiveFormat: EmbedFormat;
|
|
51
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed code generation utilities for the Embed step.
|
|
3
|
+
*
|
|
4
|
+
* Two formats are supported:
|
|
5
|
+
* - `iframe`: classic `<iframe>` pointing at the published widget HTML on the CDN.
|
|
6
|
+
* - `web-component`: customer-facing `<widgetic>` host tag. The embed SDK
|
|
7
|
+
* scans for this tag and hydrates it. (Autonomous custom elements must
|
|
8
|
+
* contain a hyphen, so the SDK does not `define("widgetic")` — it finds
|
|
9
|
+
* `<widgetic>` in the DOM and renders into it.)
|
|
10
|
+
*
|
|
11
|
+
* The SDK script is served from `cdn.widgetic.com/sdks/browser/embed-sdk.js`
|
|
12
|
+
* (R2 custom domain on the `widgets` bucket — see AGENTS.md embed architecture).
|
|
13
|
+
*/
|
|
14
|
+
export const EMBED_FORMATS = ['iframe', 'web-component'];
|
|
15
|
+
export const EMBED_FORMAT_LABELS = {
|
|
16
|
+
iframe: 'iframe',
|
|
17
|
+
'web-component': 'Web Component'
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Build the iframe embed snippet. Backward compatible with the previous
|
|
21
|
+
* hardcoded block in `CreatorApp.svelte`.
|
|
22
|
+
*/
|
|
23
|
+
export function generateIframeEmbedCode({ embedSrc, width, height }) {
|
|
24
|
+
return `<iframe src="${embedSrc}" width="${width}" height="${height}" frameborder="0" allowfullscreen></iframe>`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build the Web Component embed snippet using `<widgetic>`.
|
|
28
|
+
* Requires a composition id — the custom element loader fetches the
|
|
29
|
+
* composition + widget module from the CDN via the embed-sdk batch API.
|
|
30
|
+
*
|
|
31
|
+
* If no composition is selected we fall back to the iframe format
|
|
32
|
+
* (caller should check `compositionId` before calling this).
|
|
33
|
+
*/
|
|
34
|
+
export function generateWebComponentEmbedCode({ compositionId, width, height, sdkBaseUrl = 'https://cdn.widgetic.com' }) {
|
|
35
|
+
if (!compositionId) {
|
|
36
|
+
// Defensive: caller should have fallen back to iframe already.
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
return `<script src="${sdkBaseUrl}/sdks/browser/embed-sdk.js" async></script>
|
|
40
|
+
<widgetic
|
|
41
|
+
composition-id="${compositionId}"
|
|
42
|
+
width="${width}"
|
|
43
|
+
height="${height}"
|
|
44
|
+
render-mode="custom-element">
|
|
45
|
+
</widgetic>`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Pick the right embed code for the requested format, with fallback to
|
|
49
|
+
* iframe when the Web Component format is requested but no composition
|
|
50
|
+
* is selected (custom element requires `composition-id`).
|
|
51
|
+
*/
|
|
52
|
+
export function generateEmbedCode(format, params) {
|
|
53
|
+
if (format === 'web-component') {
|
|
54
|
+
if (!params.compositionId) {
|
|
55
|
+
return {
|
|
56
|
+
code: generateIframeEmbedCode(params),
|
|
57
|
+
effectiveFormat: 'iframe'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
code: generateWebComponentEmbedCode(params),
|
|
62
|
+
effectiveFormat: 'web-component'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
code: generateIframeEmbedCode(params),
|
|
67
|
+
effectiveFormat: 'iframe'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function generateAudioFromText(description: string, duration?: any): Promise<any>;
|
|
2
|
+
export declare function generateImageFromText(str: string, imgWidth: number, imgHeight: number, genMetaObj: any): Promise<any>;
|
|
3
|
+
export declare function generateSuperprompt(str: string): Promise<any>;
|
|
4
|
+
export declare function wakeUpImageGenModel(): Promise<any>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// local endoints methods(proxy)
|
|
2
|
+
// genenerate audio from text using Baseten model
|
|
3
|
+
export async function generateAudioFromText(description, duration = 5) {
|
|
4
|
+
// return { data: "https://replicate.delivery/pbxt/EKK8MxXO69LfeEhhWsy7FA2ku3KTvxrYFO2WxvLochuk1tUSA/output.wav" };
|
|
5
|
+
// basten model id
|
|
6
|
+
const model_id = "7qrj60r3"; // audio-gen model deployment id from the service we use to host models
|
|
7
|
+
// call the model
|
|
8
|
+
const result = await callModel({ prompts: [description], duration: Number(duration), model_host: 'baseten', model_id });
|
|
9
|
+
console.log('Audio Gen Model response data:', result);
|
|
10
|
+
// if no error return the data
|
|
11
|
+
if (!result.error && result.status !== 'error' && result.data) {
|
|
12
|
+
let res = result.data.length > 0 ? result.data[0] : result.data;
|
|
13
|
+
// add the audio data type
|
|
14
|
+
res = 'data:audio/mpeg;base64,' + res;
|
|
15
|
+
// return the audio data object
|
|
16
|
+
return { data: res };
|
|
17
|
+
}
|
|
18
|
+
// if error return the error
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
// generate images from text calling an AI Model
|
|
22
|
+
const img_gen_model_id = "1vn88"; // image-gen model deployment id from the service we use to host models
|
|
23
|
+
export async function generateImageFromText(str, imgWidth, imgHeight, genMetaObj) {
|
|
24
|
+
// create the model input
|
|
25
|
+
let input = {
|
|
26
|
+
prompt: str,
|
|
27
|
+
// image size
|
|
28
|
+
width: imgWidth, height: imgHeight,
|
|
29
|
+
// other params
|
|
30
|
+
...genMetaObj,
|
|
31
|
+
// model id
|
|
32
|
+
model_id: img_gen_model_id
|
|
33
|
+
};
|
|
34
|
+
console.log('Model call input:', input);
|
|
35
|
+
// call the model
|
|
36
|
+
let response = await callModel(input);
|
|
37
|
+
console.log('Image Gen Model response:', response);
|
|
38
|
+
// check if no result
|
|
39
|
+
if (!response)
|
|
40
|
+
response = { error: 'No data generated!' };
|
|
41
|
+
// if no error add image header and return the data
|
|
42
|
+
if (!response.error && response.data) {
|
|
43
|
+
// TODO: check if it's an array of images and use first: response.data[0]
|
|
44
|
+
// console.log("Model data response:", response.data.length);
|
|
45
|
+
// let res = response.data.length > 0 ? response.data[0] : response.data;
|
|
46
|
+
// add the image data type
|
|
47
|
+
// res = 'data:image/png;base64,' + res;
|
|
48
|
+
let res = 'data:image/png;base64,' + response.data;
|
|
49
|
+
// return the image data object
|
|
50
|
+
return { data: res };
|
|
51
|
+
}
|
|
52
|
+
// if error return the error
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
55
|
+
let super_prompt_model_id = "oc3j2"; // superprompt-gen model deployment id from the service we use to host models
|
|
56
|
+
export async function generateSuperprompt(str) {
|
|
57
|
+
// call the model
|
|
58
|
+
let input = { prompt: str, lcm: true, model_host: 'groq', model_id: super_prompt_model_id };
|
|
59
|
+
// console.log('Model call input:', input);
|
|
60
|
+
const result = await callModel(input);
|
|
61
|
+
// console.log('Model response data:', result);
|
|
62
|
+
// if no error modify and return the data
|
|
63
|
+
if (!result.error && result.data) {
|
|
64
|
+
// TODO: check if it's an array of results then use first: results.data[0]
|
|
65
|
+
// console.log("Model data result:", result.data.length);
|
|
66
|
+
// let res = result.data.length > 0 ? result.data[0] : result.data;
|
|
67
|
+
// read the response data
|
|
68
|
+
let res = result.data;
|
|
69
|
+
// return the image data object
|
|
70
|
+
return { data: res };
|
|
71
|
+
}
|
|
72
|
+
// if error return the error
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
export async function wakeUpImageGenModel() {
|
|
76
|
+
// call the model
|
|
77
|
+
const result = await callModel({ model_id: img_gen_model_id, method: 'wake' });
|
|
78
|
+
// if no error return the data
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
async function callModel(input) {
|
|
82
|
+
// read model host service name
|
|
83
|
+
let model_host = input.model_host || 'beam';
|
|
84
|
+
// make a request to local endpoint that calls the model on server side
|
|
85
|
+
const response = await fetch(`/api/call-${model_host}-model`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify(input),
|
|
89
|
+
});
|
|
90
|
+
// read the data from the response
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
// return response data
|
|
93
|
+
return data;
|
|
94
|
+
}
|