@atomm-developer/generator-workbench 0.1.7 → 0.1.9

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/index.es.js CHANGED
@@ -166,7 +166,11 @@ function createExportController(args) {
166
166
  element
167
167
  }));
168
168
  await ensureLoggedIn();
169
- const usage = await ensureUsageAllowsExport(await getBillingUsage());
169
+ const initialUsage = await getBillingUsage();
170
+ const usage = await ensureUsageAllowsExport(initialUsage);
171
+ if (!initialUsage) {
172
+ return sdk.export.download({ format: "svg" });
173
+ }
170
174
  if (!usage) {
171
175
  return {
172
176
  success: false,
@@ -185,7 +189,11 @@ function createExportController(args) {
185
189
  element
186
190
  }));
187
191
  await ensureLoggedIn();
188
- const usage = await ensureUsageAllowsExport(await getBillingUsage());
192
+ const initialUsage = await getBillingUsage();
193
+ const usage = await ensureUsageAllowsExport(initialUsage);
194
+ if (!initialUsage) {
195
+ return sdk.export.openInStudio({ format: "svg" });
196
+ }
189
197
  if (!usage) {
190
198
  return {
191
199
  success: false
@@ -470,7 +478,7 @@ function createTemplateController(args) {
470
478
  };
471
479
  },
472
480
  prepareTemplateExport,
473
- async exportTemplate(selectedFieldPaths) {
481
+ buildTemplate(selectedFieldPaths) {
474
482
  var _a;
475
483
  const prepared = prepareTemplateExport();
476
484
  const finalSelectedFieldPaths = (selectedFieldPaths == null ? void 0 : selectedFieldPaths.length) ? collectPanelFieldPaths(prepared.panelSchema, selectedFieldPaths) : prepared.selectedFieldPaths;
@@ -485,6 +493,10 @@ function createTemplateController(args) {
485
493
  selectedFieldPaths: finalSelectedFieldPaths,
486
494
  templateMeta: (_a = config.getTemplateMeta) == null ? void 0 : _a.call(config)
487
495
  });
496
+ return template;
497
+ },
498
+ async exportTemplate(selectedFieldPaths) {
499
+ const template = this.buildTemplate(selectedFieldPaths);
488
500
  sdk.template.download(template);
489
501
  return { template };
490
502
  }
@@ -578,6 +590,7 @@ const ATOMM_UI_CDN_SCRIPT_URL = "https://minio-download.makeblock.com/resource/a
578
590
  const CREDIT_ICON_DATA_URI = "data:image/svg+xml,%3csvg%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3ccircle%20cx='12'%20cy='12'%20r='9'%20fill='url(%23paint0_linear_503_69253)'/%3e%3cg%20filter='url(%23filter0_d_503_69253)'%3e%3crect%20x='7.75781'%20y='12'%20width='6'%20height='6'%20rx='1'%20transform='rotate(-45%207.75781%2012)'%20fill='url(%23paint1_linear_503_69253)'/%3e%3c/g%3e%3cdefs%3e%3cfilter%20id='filter0_d_503_69253'%20x='2.17188'%20y='4.17188'%20width='19.6572'%20height='19.6562'%20filterUnits='userSpaceOnUse'%20color-interpolation-filters='sRGB'%3e%3cfeFlood%20flood-opacity='0'%20result='BackgroundImageFix'/%3e%3cfeColorMatrix%20in='SourceAlpha'%20type='matrix'%20values='0%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%20127%200'%20result='hardAlpha'/%3e%3cfeOffset%20dy='2'/%3e%3cfeGaussianBlur%20stdDeviation='3'/%3e%3cfeComposite%20in2='hardAlpha'%20operator='out'/%3e%3cfeColorMatrix%20type='matrix'%20values='0%200%200%200%201%200%200%200%200%200.206957%200%200%200%200%200.0670085%200%200%200%201%200'/%3e%3cfeBlend%20mode='normal'%20in2='BackgroundImageFix'%20result='effect1_dropShadow_503_69253'/%3e%3cfeBlend%20mode='normal'%20in='SourceGraphic'%20in2='effect1_dropShadow_503_69253'%20result='shape'/%3e%3c/filter%3e%3clinearGradient%20id='paint0_linear_503_69253'%20x1='12'%20y1='3'%20x2='12'%20y2='21'%20gradientUnits='userSpaceOnUse'%3e%3cstop%20stop-color='%23FFA346'/%3e%3cstop%20offset='1'%20stop-color='%23FF7C23'/%3e%3c/linearGradient%3e%3clinearGradient%20id='paint1_linear_503_69253'%20x1='13.2327'%20y1='12.5252'%20x2='8.63652'%20y2='18.5356'%20gradientUnits='userSpaceOnUse'%3e%3cstop%20stop-color='white'/%3e%3cstop%20offset='1'%20stop-color='%23FFD3B5'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e";
579
591
  const EXPORT_ICON_DATA_URI = "data:image/svg+xml,%3Csvg%20viewBox%3D'0%200%201088%201024'%20xmlns%3D'http%3A//www.w3.org/2000/svg'%3E%3Cpath%20d%3D'M416%20128v85.312h-128A42.688%2042.688%200%200%200%20245.312%20256v512c0%2023.552%2019.136%2042.688%2042.688%2042.688h512a42.688%2042.688%200%200%200%2042.688-42.688V597.312H928V768a128%20128%200%200%201-128%20128h-512a128%20128%200%200%201-128-128V256a128%20128%200%200%201%20128-128h128z'%20fill%3D'%23ffffff'/%3E%3Cpath%20d%3D'M723.52%20520.832L924.352%20320l-200.832-200.832-60.352%2060.352%2097.856%2097.792h-67.712A298.688%20298.688%200%200%200%20394.688%20576v85.312H480V576a213.312%20213.312%200%200%201%20213.312-213.312h67.712l-97.856%2097.792%2060.352%2060.352z'%20fill%3D'%23ffffff'/%3E%3C/svg%3E";
580
592
  const INVITATION_ICON_URL = "https://storage-us.atomm.com/resource/static/atomm/icon/Vector.svg";
593
+ const NO_LOGIN_AVATAR_URL = "https://storage-us.atomm.com/resource/xart/static/agent/imgs/no-login.png";
581
594
  const WORKBENCH_SHELL_STYLES = `
582
595
  :host {
583
596
  display: block;
@@ -801,6 +814,22 @@ const WORKBENCH_SHELL_STYLES = `
801
814
  transition: background 150ms ease;
802
815
  }
803
816
 
817
+ .auth-avatar-button {
818
+ appearance: none;
819
+ border: 0;
820
+ background: transparent;
821
+ cursor: pointer;
822
+ }
823
+
824
+ .auth-avatar-button--guest {
825
+ padding: 0;
826
+ display: inline-flex;
827
+ align-items: center;
828
+ justify-content: center;
829
+ width: 40px;
830
+ height: 40px;
831
+ }
832
+
804
833
 
805
834
  .auth-avatar {
806
835
  width: 40px;
@@ -822,6 +851,10 @@ const WORKBENCH_SHELL_STYLES = `
822
851
  font-size: 14px;
823
852
  }
824
853
 
854
+ .auth-avatar--guest {
855
+ background: transparent;
856
+ }
857
+
825
858
  .auth-avatar img {
826
859
  width: 100%;
827
860
  height: 100%;
@@ -991,10 +1024,19 @@ const WORKBENCH_SHELL_STYLES = `
991
1024
  }
992
1025
 
993
1026
  .export-credits-hint-value {
994
- color: white;
995
- font-size: 13px;
996
- font-weight: 700;
997
- font-variant-numeric: tabular-nums;
1027
+ width: 100%;
1028
+ background: linear-gradient(90deg, #fce1bf, #ecb678 67.02%, #f9c991);
1029
+ border-radius: 4px;
1030
+ gap: 2px;
1031
+ height: 16px;
1032
+ padding: 2px 4px;
1033
+ font-size: 10px;
1034
+ font-weight: 600;
1035
+ line-height: 16px;
1036
+ color: #581e06;
1037
+ display: flex;
1038
+ justify-content: center;
1039
+ align-items: center;
998
1040
  }
999
1041
 
1000
1042
  .export-overlay-menu,
@@ -1155,7 +1197,7 @@ const WORKBENCH_SHELL_STYLES = `
1155
1197
  `;
1156
1198
  const WORKBENCH_VUE_TEMPLATE = `
1157
1199
  <div class="shell">
1158
- <header v-if="state.shellMode !== 'template'" class="topbar app-topbar">
1200
+ <header v-show="state.showTopbar" class="topbar app-topbar">
1159
1201
  <div class="logo-area app-topbar-main" data-role="logo-area">
1160
1202
  <img
1161
1203
  :src="state.logoSrc"
@@ -1201,12 +1243,11 @@ const WORKBENCH_VUE_TEMPLATE = `
1201
1243
  </div>
1202
1244
  <div class="app-topbar-auth">
1203
1245
  <button
1204
- v-if="state.isLogin && state.invitationEnabled"
1246
+ v-if="state.invitationEnabled"
1205
1247
  type="button"
1206
1248
  class="invite-entry"
1207
1249
  data-role="open-invitation"
1208
- :disabled="!state.invitationReady"
1209
- @click="openInvitationModal()"
1250
+ @click="callbacks.onOpenInvitation()"
1210
1251
  >
1211
1252
  <img
1212
1253
  src="${INVITATION_ICON_URL}"
@@ -1217,7 +1258,7 @@ const WORKBENCH_VUE_TEMPLATE = `
1217
1258
  <span class="invite-entry-text">{{ state.invitationButtonText }}</span>
1218
1259
  </button>
1219
1260
  <div
1220
- v-if="state.isLogin"
1261
+ v-if="state.isLogin && state.creditsEnabled"
1221
1262
  class="topbar-credits-badge"
1222
1263
  data-role="topbar-credits"
1223
1264
  title="Remaining credits"
@@ -1300,14 +1341,22 @@ const WORKBENCH_VUE_TEMPLATE = `
1300
1341
  </template>
1301
1342
  </xt-dropdown-menu>
1302
1343
 
1303
- <xt-button
1304
- size="small"
1305
- type="secondary"
1344
+ <button
1345
+ type="button"
1346
+ class="auth-avatar-button auth-avatar-button--guest"
1306
1347
  data-role="login"
1307
- :loading="state.loginLoading"
1308
1348
  v-if="!state.isLogin"
1309
1349
  @click="callbacks.onLogin()"
1310
- >Login</xt-button>
1350
+ >
1351
+ <div class="auth-avatar auth-avatar--guest">
1352
+ <img
1353
+ src="${NO_LOGIN_AVATAR_URL}"
1354
+ alt="Login"
1355
+ data-role="guest-avatar-image"
1356
+ draggable="false"
1357
+ />
1358
+ </div>
1359
+ </button>
1311
1360
  </div>
1312
1361
  </header>
1313
1362
 
@@ -1329,7 +1378,12 @@ const WORKBENCH_VUE_TEMPLATE = `
1329
1378
  <main class="canvas-host" data-role="canvas-host"></main>
1330
1379
  <aside class="panel-sidebar" data-role="panel-sidebar">
1331
1380
  <div class="panel-host" data-role="panel-host"></div>
1332
- <div id="sidebar-footer" class="sidebar-footer-export panel-actions" data-role="panel-actions">
1381
+ <div
1382
+ v-show="state.showSidebarFooter"
1383
+ id="sidebar-footer"
1384
+ class="sidebar-footer-export panel-actions"
1385
+ data-role="panel-actions"
1386
+ >
1333
1387
  <xt-dropdown-menu
1334
1388
  trigger="click"
1335
1389
  :portal-disabled="true"
@@ -1387,7 +1441,7 @@ const WORKBENCH_VUE_TEMPLATE = `
1387
1441
  </div>
1388
1442
 
1389
1443
  <div
1390
- v-if="state.shellMode === 'shell'"
1444
+ v-show="state.shellMode === 'shell' && state.showSidebarFooter"
1391
1445
  id="sidebar-footer"
1392
1446
  class="sidebar-footer-export panel-actions sidebar-footer-floating"
1393
1447
  data-role="panel-actions"
@@ -1807,13 +1861,17 @@ function createDefaultAtommProUser() {
1807
1861
  }
1808
1862
  function stateFromConfig(vue, config, invitationReady) {
1809
1863
  var _a;
1864
+ const shellChromeVisible = config.shellChromeVisible !== false;
1810
1865
  return vue.reactive({
1811
1866
  shellMode: config.mode || "full",
1867
+ showTopbar: shellChromeVisible && config.mode !== "template",
1868
+ showSidebarFooter: shellChromeVisible,
1812
1869
  logoText: config.logoText || config.title,
1813
1870
  logoSrc: config.logoUrl || "https://storage-us.atomm.com/resource/xart/static/agent/imgs/atomm-logo.svg",
1814
1871
  panelTarget: config.panelTarget || "right",
1815
1872
  avatarMenuTrigger: config.avatarMenuTrigger || "hover",
1816
1873
  templateEnabled: config.templateEnabled !== false,
1874
+ creditsEnabled: config.creditsEnabled !== false,
1817
1875
  invitationEnabled: isInvitationEnabled(config),
1818
1876
  invitationReady,
1819
1877
  invitationConfigKey: ((_a = config.invitationConfigKey) == null ? void 0 : _a.trim()) || "",
@@ -1855,6 +1913,8 @@ function createNoopCallbacks() {
1855
1913
  return {
1856
1914
  onLogin: () => {
1857
1915
  },
1916
+ onOpenInvitation: () => {
1917
+ },
1858
1918
  onLogout: () => {
1859
1919
  },
1860
1920
  onImportTemplate: () => {
@@ -2120,6 +2180,15 @@ async function renderWorkbenchShell(root, config) {
2120
2180
  const state = stateFromConfig(vue, config, invitationRuntime !== null);
2121
2181
  const callbacks = createNoopCallbacks();
2122
2182
  const invitationModalRef = vue.ref(null);
2183
+ const triggerInvitationModal = () => {
2184
+ var _a2, _b2;
2185
+ const modalInstance = invitationModalRef.value;
2186
+ const open = (modalInstance == null ? void 0 : modalInstance.open) || ((_b2 = (_a2 = modalInstance == null ? void 0 : modalInstance.$) == null ? void 0 : _a2.exposed) == null ? void 0 : _b2.open);
2187
+ open == null ? void 0 : open({
2188
+ configKey: state.invitationConfigKey || "default",
2189
+ showDetail: false
2190
+ });
2191
+ };
2123
2192
  const container = document.createElement("div");
2124
2193
  container.setAttribute("data-workbench-vue-root", "");
2125
2194
  root.appendChild(container);
@@ -2142,18 +2211,10 @@ async function renderWorkbenchShell(root, config) {
2142
2211
  }) : null;
2143
2212
  const app = vue.createApp({
2144
2213
  setup() {
2145
- const openInvitationModal = () => {
2146
- var _a2, _b2;
2147
- (_b2 = (_a2 = invitationModalRef.value) == null ? void 0 : _a2.open) == null ? void 0 : _b2.call(_a2, {
2148
- configKey: state.invitationConfigKey || "default",
2149
- showDetail: false
2150
- });
2151
- };
2152
2214
  return {
2153
2215
  state,
2154
2216
  callbacks,
2155
- invitationModalRef,
2156
- openInvitationModal
2217
+ invitationModalRef
2157
2218
  };
2158
2219
  },
2159
2220
  template: WORKBENCH_VUE_TEMPLATE
@@ -2184,6 +2245,7 @@ async function renderWorkbenchShell(root, config) {
2184
2245
  callbacks,
2185
2246
  app,
2186
2247
  refs,
2248
+ openInvitationModal: triggerInvitationModal,
2187
2249
  requestCreditsPurchase: async (options) => {
2188
2250
  if (!purchaseBridge) {
2189
2251
  await injectStyleSheetIntoDocumentHead(
@@ -2215,6 +2277,297 @@ function unmountWorkbenchShell(context) {
2215
2277
  context.disposeCreditsPurchaseBridge();
2216
2278
  context.app.unmount();
2217
2279
  }
2280
+ function createIframeBridge(args) {
2281
+ const {
2282
+ targetWindow = window,
2283
+ parentWindow = window.parent,
2284
+ targetOrigin = "*",
2285
+ validateOrigin,
2286
+ handlers
2287
+ } = args;
2288
+ let disposed = false;
2289
+ const post = (type, data) => {
2290
+ if (disposed) return;
2291
+ logBridge("outgoing", {
2292
+ type,
2293
+ data,
2294
+ targetOrigin
2295
+ });
2296
+ parentWindow.postMessage(
2297
+ {
2298
+ type,
2299
+ data
2300
+ },
2301
+ targetOrigin
2302
+ );
2303
+ };
2304
+ const postTemplateError = (action, error) => {
2305
+ logBridge("error", {
2306
+ type: "generator_toTemplateError",
2307
+ action,
2308
+ message: toErrorMessage(error)
2309
+ });
2310
+ post("generator_toTemplateError", {
2311
+ action,
2312
+ message: toErrorMessage(error)
2313
+ });
2314
+ };
2315
+ const postFileError = (action, error) => {
2316
+ logBridge("error", {
2317
+ type: "generator_toFileError",
2318
+ action,
2319
+ message: toErrorMessage(error)
2320
+ });
2321
+ post("generator_toFileError", {
2322
+ action,
2323
+ message: toErrorMessage(error)
2324
+ });
2325
+ };
2326
+ const onMessage = (event) => {
2327
+ if (disposed) return;
2328
+ if (validateOrigin && !validateOrigin(event.origin, event)) {
2329
+ return;
2330
+ }
2331
+ const message = event.data;
2332
+ if (!message || typeof message !== "object") {
2333
+ return;
2334
+ }
2335
+ const type = "type" in message && typeof message.type === "string" ? message.type : "";
2336
+ const data = "data" in message ? message.data : void 0;
2337
+ logBridge("incoming", {
2338
+ type,
2339
+ data,
2340
+ origin: event.origin
2341
+ });
2342
+ switch (type) {
2343
+ case "generator_getGeneratorData":
2344
+ void handlers.getGeneratorData().then((payload) => {
2345
+ post("generator_toGeneratorData", payload);
2346
+ }).catch((error) => {
2347
+ postTemplateError("getGeneratorData", error);
2348
+ });
2349
+ return;
2350
+ case "generator_setGeneratorData":
2351
+ console.log("generator_setGeneratorData =======> data", data);
2352
+ void Promise.resolve(handlers.setGeneratorData(data || {}));
2353
+ return;
2354
+ case "generator_loadTemplateData":
2355
+ console.log("generator_loadTemplateData =======> data", data);
2356
+ void handlers.loadTemplateData(data || {}).then((payload) => {
2357
+ post("generator_toTemplateLoaded", {
2358
+ template: (payload == null ? void 0 : payload.template) || (data && "template" in data ? data.template : data) || null
2359
+ });
2360
+ }).catch((error) => {
2361
+ postTemplateError("loadTemplateData", error);
2362
+ });
2363
+ return;
2364
+ case "generator_getTemplateData":
2365
+ void handlers.getTemplateData().then((payload) => {
2366
+ post("generator_toTemplateData", payload);
2367
+ }).catch((error) => {
2368
+ postTemplateError("getTemplateData", error);
2369
+ });
2370
+ return;
2371
+ case "generator_getFile": {
2372
+ const action = normalizeFileAction(data == null ? void 0 : data.action);
2373
+ void handlers.getFile(action).then((payload) => {
2374
+ post("generator_toFile", payload);
2375
+ }).catch((error) => {
2376
+ postFileError(action, error);
2377
+ });
2378
+ return;
2379
+ }
2380
+ case "generator_getTemplateFieldOptions":
2381
+ postTemplateError(
2382
+ "getTemplateFieldOptions",
2383
+ new Error("Template field options are not supported")
2384
+ );
2385
+ return;
2386
+ case "generator_publishTemplate":
2387
+ postTemplateError(
2388
+ "publishTemplate",
2389
+ new Error("Template publishing is not supported")
2390
+ );
2391
+ return;
2392
+ default:
2393
+ return;
2394
+ }
2395
+ };
2396
+ targetWindow.addEventListener("message", onMessage);
2397
+ post("generator_pageLoaded");
2398
+ return {
2399
+ dispose() {
2400
+ if (disposed) return;
2401
+ logBridge("lifecycle", {
2402
+ type: "dispose"
2403
+ });
2404
+ disposed = true;
2405
+ targetWindow.removeEventListener("message", onMessage);
2406
+ }
2407
+ };
2408
+ }
2409
+ async function resolveIframeBridgeFile(args) {
2410
+ var _a, _b;
2411
+ const { action, sdk, runtime, config, element } = args;
2412
+ const context = {
2413
+ sdk,
2414
+ runtime,
2415
+ element
2416
+ };
2417
+ const configuredExportData = await Promise.resolve(
2418
+ (_b = (_a = config.embedBridge) == null ? void 0 : _a.getExportData) == null ? void 0 : _b.call(_a, action, context)
2419
+ );
2420
+ const exportData = configuredExportData || await resolveSdkExportData(sdk, action);
2421
+ if (!exportData) {
2422
+ throw new Error("No export data available");
2423
+ }
2424
+ return {
2425
+ action,
2426
+ ...await exportDataToBridgeFile(exportData)
2427
+ };
2428
+ }
2429
+ async function resolveIframeBridgeOriginImageUrl(args) {
2430
+ var _a, _b;
2431
+ const { sdk, runtime, config, element } = args;
2432
+ return await Promise.resolve(
2433
+ (_b = (_a = config.embedBridge) == null ? void 0 : _a.getOriginImageUrl) == null ? void 0 : _b.call(_a, {
2434
+ sdk,
2435
+ runtime,
2436
+ element
2437
+ })
2438
+ ) || "";
2439
+ }
2440
+ async function resolveSdkExportData(sdk, action) {
2441
+ const exportModule = sdk.export;
2442
+ const provider = exportModule.provider;
2443
+ if (!provider) {
2444
+ return null;
2445
+ }
2446
+ const purpose = mapActionToExportPurpose(action);
2447
+ const preferredFormat = action === "cover" ? "png" : "svg";
2448
+ if (typeof provider.getExportData === "function") {
2449
+ const data = await Promise.resolve(
2450
+ provider.getExportData(purpose, preferredFormat)
2451
+ );
2452
+ if (data) {
2453
+ return data;
2454
+ }
2455
+ }
2456
+ if (typeof provider.getExportCanvas === "function") {
2457
+ const canvas = await Promise.resolve(provider.getExportCanvas(purpose));
2458
+ if (canvas) {
2459
+ return {
2460
+ type: "canvas",
2461
+ canvas
2462
+ };
2463
+ }
2464
+ }
2465
+ return null;
2466
+ }
2467
+ async function exportDataToBridgeFile(data) {
2468
+ switch (data.type) {
2469
+ case "canvas": {
2470
+ const base64 = data.canvas.toDataURL("image/png");
2471
+ return {
2472
+ base64,
2473
+ mimeType: "image/png",
2474
+ ext: "png"
2475
+ };
2476
+ }
2477
+ case "dataUrl": {
2478
+ const mimeType = readDataUrlMimeType(data.dataUrl);
2479
+ return {
2480
+ base64: data.dataUrl,
2481
+ mimeType,
2482
+ ext: mimeTypeToExtension(mimeType)
2483
+ };
2484
+ }
2485
+ case "svg": {
2486
+ return {
2487
+ base64: svgToDataUrl(data.svgString),
2488
+ mimeType: "image/svg+xml",
2489
+ ext: "svg"
2490
+ };
2491
+ }
2492
+ case "blob": {
2493
+ const dataUrl = await blobToDataUrl(data.blob);
2494
+ const mimeType = data.blob.type || readDataUrlMimeType(dataUrl);
2495
+ return {
2496
+ base64: dataUrl,
2497
+ mimeType,
2498
+ ext: mimeTypeToExtension(mimeType)
2499
+ };
2500
+ }
2501
+ case "url": {
2502
+ const response = await fetch(data.url);
2503
+ if (!response.ok) {
2504
+ throw new Error(`Failed to fetch export URL: ${response.status}`);
2505
+ }
2506
+ const blob = await response.blob();
2507
+ const dataUrl = await blobToDataUrl(blob);
2508
+ const mimeType = blob.type || readDataUrlMimeType(dataUrl);
2509
+ return {
2510
+ base64: dataUrl,
2511
+ mimeType,
2512
+ ext: mimeTypeToExtension(mimeType)
2513
+ };
2514
+ }
2515
+ }
2516
+ }
2517
+ function normalizeFileAction(action) {
2518
+ return action === "openInStudio" || action === "cover" ? action : "download";
2519
+ }
2520
+ function mapActionToExportPurpose(action) {
2521
+ if (action === "openInStudio") {
2522
+ return "studio";
2523
+ }
2524
+ return action;
2525
+ }
2526
+ function toErrorMessage(error) {
2527
+ return error instanceof Error ? error.message : String(error);
2528
+ }
2529
+ function readDataUrlMimeType(dataUrl) {
2530
+ var _a;
2531
+ return ((_a = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);/)) == null ? void 0 : _a[1]) || "image/png";
2532
+ }
2533
+ function mimeTypeToExtension(mimeType) {
2534
+ var _a;
2535
+ if (mimeType === "image/svg+xml") return "svg";
2536
+ if (mimeType === "image/jpeg") return "jpg";
2537
+ return ((_a = mimeType.split("/")[1]) == null ? void 0 : _a.split("+")[0]) || "png";
2538
+ }
2539
+ function svgToDataUrl(svgString) {
2540
+ const utf8 = encodeURIComponent(svgString).replace(
2541
+ /%([0-9A-F]{2})/g,
2542
+ (_, p1) => String.fromCharCode(Number.parseInt(p1, 16))
2543
+ );
2544
+ return `data:image/svg+xml;base64,${btoa(utf8)}`;
2545
+ }
2546
+ function blobToDataUrl(blob) {
2547
+ return new Promise((resolve, reject) => {
2548
+ const reader = new FileReader();
2549
+ reader.onload = () => {
2550
+ if (typeof reader.result !== "string") {
2551
+ reject(new Error("Failed to read blob as data URL"));
2552
+ return;
2553
+ }
2554
+ resolve(reader.result);
2555
+ };
2556
+ reader.onerror = () => {
2557
+ reject(reader.error || new Error("Failed to read blob as data URL"));
2558
+ };
2559
+ reader.readAsDataURL(blob);
2560
+ });
2561
+ }
2562
+ function logBridge(phase, payload) {
2563
+ if (!isBridgeDebugEnabled()) {
2564
+ return;
2565
+ }
2566
+ console.log(`[generator-workbench][iframe-bridge][${phase}]`, payload);
2567
+ }
2568
+ function isBridgeDebugEnabled() {
2569
+ return new URLSearchParams(window.location.search).get("bridgeDebug") === "1";
2570
+ }
2218
2571
  const DEFAULT_CONFIG = {
2219
2572
  title: "",
2220
2573
  mode: "full",
@@ -2229,6 +2582,9 @@ function isShellMode(config) {
2229
2582
  return config.mode === "shell";
2230
2583
  }
2231
2584
  const TOKEN_STORAGE_KEY_PREFIX = "__atomm_sdk_token__";
2585
+ function getRouteCapabilityMode() {
2586
+ return new URLSearchParams(window.location.search).get("mode") === "embed" ? "embed" : "full";
2587
+ }
2232
2588
  function normalizeWorkbenchConfig(config) {
2233
2589
  return {
2234
2590
  ...DEFAULT_CONFIG,
@@ -2257,6 +2613,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
2257
2613
  __publicField(this, "_historyController");
2258
2614
  __publicField(this, "_runtimeController");
2259
2615
  __publicField(this, "_shellContext");
2616
+ __publicField(this, "_iframeBridge");
2260
2617
  __publicField(this, "handleRuntimeCloudSaveRequest", (event) => {
2261
2618
  if (!event.detail) return;
2262
2619
  if (this._runtimeCloudSaveTimer) {
@@ -2353,6 +2710,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
2353
2710
  this._state.activePanelFilter
2354
2711
  ));
2355
2712
  }
2713
+ this.bindIframeBridge();
2356
2714
  this._mounted = true;
2357
2715
  dispatchWorkbenchEvent(this, "workbench-ready", {
2358
2716
  sdk: this._sdk,
@@ -2360,7 +2718,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
2360
2718
  });
2361
2719
  }
2362
2720
  async unmount() {
2363
- var _a, _b, _c, _d, _e, _f;
2721
+ var _a, _b, _c, _d, _e, _f, _g;
2364
2722
  (_a = this._cleanupAuth) == null ? void 0 : _a.call(this);
2365
2723
  this._cleanupAuth = void 0;
2366
2724
  (_b = this._cleanupCredits) == null ? void 0 : _b.call(this);
@@ -2376,11 +2734,13 @@ class GeneratorWorkbenchElement extends HTMLElement {
2376
2734
  this._runtimeCloudSaveTimer = null;
2377
2735
  }
2378
2736
  this.clearBillingCountdownTimer();
2737
+ (_f = this._iframeBridge) == null ? void 0 : _f.dispose();
2738
+ this._iframeBridge = void 0;
2379
2739
  this.removeEventListener(
2380
2740
  "runtime-cloud-save-request",
2381
2741
  this.handleRuntimeCloudSaveRequest
2382
2742
  );
2383
- await ((_f = this._runtimeController) == null ? void 0 : _f.unmountAll());
2743
+ await ((_g = this._runtimeController) == null ? void 0 : _g.unmountAll());
2384
2744
  if (this._shellContext) {
2385
2745
  unmountWorkbenchShell(this._shellContext);
2386
2746
  this._shellContext = void 0;
@@ -2555,7 +2915,10 @@ class GeneratorWorkbenchElement extends HTMLElement {
2555
2915
  unmountWorkbenchShell(this._shellContext);
2556
2916
  this.shadowRoot.innerHTML = "";
2557
2917
  }
2558
- this._shellContext = await renderWorkbenchShell(this.shadowRoot, this._config);
2918
+ this._shellContext = await renderWorkbenchShell(
2919
+ this.shadowRoot,
2920
+ this.getEffectiveConfig()
2921
+ );
2559
2922
  this.syncAuthUI();
2560
2923
  this.syncBillingUI();
2561
2924
  this.syncInvitationConfigKey();
@@ -2563,32 +2926,34 @@ class GeneratorWorkbenchElement extends HTMLElement {
2563
2926
  }
2564
2927
  bindControllers() {
2565
2928
  var _a;
2566
- if (!this._sdk || !this._runtime) return;
2567
- this._authController = createAuthController({ sdk: this._sdk });
2929
+ const sdk = this.getEffectiveSdk();
2930
+ const config = this.getEffectiveConfig();
2931
+ if (!sdk || !this._runtime) return;
2932
+ this._authController = createAuthController({ sdk });
2568
2933
  this._templateController = createTemplateController({
2569
- sdk: this._sdk,
2934
+ sdk,
2570
2935
  runtime: this._runtime,
2571
- config: this._config
2936
+ config
2572
2937
  });
2573
2938
  this._exportController = createExportController({
2574
- sdk: this._sdk,
2939
+ sdk,
2575
2940
  runtime: this._runtime,
2576
- config: this._config,
2941
+ config,
2577
2942
  element: this,
2578
- requestCreditsPurchase: (_a = this._shellContext) == null ? void 0 : _a.requestCreditsPurchase
2943
+ requestCreditsPurchase: config.creditsEnabled === false ? void 0 : (_a = this._shellContext) == null ? void 0 : _a.requestCreditsPurchase
2579
2944
  });
2580
- this._cloudController = createCloudController({
2581
- sdk: this._sdk,
2945
+ this._cloudController = sdk.cloud ? createCloudController({
2946
+ sdk,
2582
2947
  runtime: this._runtime,
2583
- config: this._config,
2948
+ config,
2584
2949
  element: this
2585
- });
2586
- this._historyController = createHistoryController({
2587
- sdk: this._sdk,
2950
+ }) : void 0;
2951
+ this._historyController = sdk.history ? createHistoryController({
2952
+ sdk,
2588
2953
  runtime: this._runtime,
2589
- config: this._config,
2954
+ config,
2590
2955
  element: this
2591
- });
2956
+ }) : void 0;
2592
2957
  this._runtimeController = createRuntimeController({
2593
2958
  runtime: this._runtime
2594
2959
  });
@@ -2599,6 +2964,9 @@ class GeneratorWorkbenchElement extends HTMLElement {
2599
2964
  callbacks.onLogin = () => {
2600
2965
  void this.handleLogin();
2601
2966
  };
2967
+ callbacks.onOpenInvitation = () => {
2968
+ void this.handleInvitationEntry();
2969
+ };
2602
2970
  callbacks.onLogout = () => {
2603
2971
  void this.handleLogout();
2604
2972
  };
@@ -2701,6 +3069,56 @@ class GeneratorWorkbenchElement extends HTMLElement {
2701
3069
  }
2702
3070
  this._shellContext.state.templateSelectedFieldPaths = Array.from(selected);
2703
3071
  }
3072
+ async applyTemplatePayload(template) {
3073
+ if (!this._runtime) {
3074
+ throw new Error("[generator-workbench] runtime is required for template bridge actions");
3075
+ }
3076
+ if (!this._sdk) {
3077
+ throw new Error("[generator-workbench] sdk is required for template bridge actions");
3078
+ }
3079
+ let capturedPanelFilter;
3080
+ await this._sdk.template.applyToRuntime(
3081
+ this._runtime,
3082
+ template,
3083
+ {
3084
+ onPanelFilter: (panelFilter) => {
3085
+ capturedPanelFilter = panelFilter;
3086
+ }
3087
+ }
3088
+ );
3089
+ if (capturedPanelFilter) {
3090
+ this._state.activePanelFilter = capturedPanelFilter;
3091
+ if (this._runtimeController && !isShellMode(this._config)) {
3092
+ const panelHost = this.requireRefs().panelHost;
3093
+ if (!panelHost) {
3094
+ throw new Error(
3095
+ "[generator-workbench] panel host is required for template bridge actions"
3096
+ );
3097
+ }
3098
+ await this._runtimeController.remountPanel(panelHost, capturedPanelFilter);
3099
+ }
3100
+ }
3101
+ return {
3102
+ template,
3103
+ panelFilter: capturedPanelFilter
3104
+ };
3105
+ }
3106
+ async tryResolveIframeBridgeFile(action, sdk, config) {
3107
+ try {
3108
+ if (!this._runtime) {
3109
+ return void 0;
3110
+ }
3111
+ return await resolveIframeBridgeFile({
3112
+ action,
3113
+ sdk,
3114
+ runtime: this._runtime,
3115
+ config,
3116
+ element: this
3117
+ });
3118
+ } catch {
3119
+ return void 0;
3120
+ }
3121
+ }
2704
3122
  bindAuthState() {
2705
3123
  var _a;
2706
3124
  const controller = this.requireAuthController();
@@ -2713,13 +3131,101 @@ class GeneratorWorkbenchElement extends HTMLElement {
2713
3131
  });
2714
3132
  this.bindBillingSubscriptions();
2715
3133
  }
3134
+ bindIframeBridge() {
3135
+ var _a, _b, _c;
3136
+ (_a = this._iframeBridge) == null ? void 0 : _a.dispose();
3137
+ this._iframeBridge = void 0;
3138
+ if (getRouteCapabilityMode() !== "embed" || !this._runtime) {
3139
+ return;
3140
+ }
3141
+ const sdk = this._sdk;
3142
+ if (!sdk) {
3143
+ return;
3144
+ }
3145
+ const config = this._config;
3146
+ const runtime = this._runtime;
3147
+ this._iframeBridge = createIframeBridge({
3148
+ targetOrigin: ((_b = config.embedBridge) == null ? void 0 : _b.targetOrigin) || "*",
3149
+ validateOrigin: (_c = config.embedBridge) == null ? void 0 : _c.validateOrigin,
3150
+ handlers: {
3151
+ getGeneratorData: async () => {
3152
+ const info = runtime.getState();
3153
+ const originImageUrl = await resolveIframeBridgeOriginImageUrl({
3154
+ sdk,
3155
+ runtime,
3156
+ config,
3157
+ element: this
3158
+ });
3159
+ const coverFile = await this.tryResolveIframeBridgeFile(
3160
+ "cover",
3161
+ sdk,
3162
+ config
3163
+ );
3164
+ return {
3165
+ info,
3166
+ cover: (coverFile == null ? void 0 : coverFile.base64) || "",
3167
+ originImageUrl
3168
+ };
3169
+ },
3170
+ setGeneratorData: async (data) => {
3171
+ const snapshot = data.info && typeof data.info === "object" && !Array.isArray(data.info) ? data.info : data;
3172
+ await runtime.setState(snapshot, {
3173
+ source: "iframe-bridge",
3174
+ originImageUrl: typeof data.originImageUrl === "string" ? data.originImageUrl : ""
3175
+ });
3176
+ },
3177
+ loadTemplateData: async (data) => {
3178
+ const template = data.template && typeof data.template === "object" && !Array.isArray(data.template) ? data.template : data;
3179
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
3180
+ throw new Error("Template payload is empty");
3181
+ }
3182
+ const result = await this.applyTemplatePayload(
3183
+ template
3184
+ );
3185
+ return {
3186
+ template: result.template
3187
+ };
3188
+ },
3189
+ getTemplateData: async () => {
3190
+ const template = this.requireTemplateController().buildTemplate();
3191
+ const info = runtime.getState();
3192
+ const originImageUrl = await resolveIframeBridgeOriginImageUrl({
3193
+ sdk,
3194
+ runtime,
3195
+ config,
3196
+ element: this
3197
+ });
3198
+ const coverFile = await this.tryResolveIframeBridgeFile(
3199
+ "cover",
3200
+ sdk,
3201
+ config
3202
+ );
3203
+ return {
3204
+ template,
3205
+ info,
3206
+ cover: (coverFile == null ? void 0 : coverFile.base64) || "",
3207
+ originImageUrl
3208
+ };
3209
+ },
3210
+ getFile: async (action) => resolveIframeBridgeFile({
3211
+ action,
3212
+ sdk,
3213
+ runtime,
3214
+ config,
3215
+ element: this
3216
+ })
3217
+ }
3218
+ });
3219
+ }
2716
3220
  bindAutoSave() {
2717
- var _a, _b, _c, _d;
3221
+ var _a, _b, _c;
3222
+ const sdk = this.getEffectiveSdk();
3223
+ const config = this.getEffectiveConfig();
2718
3224
  (_a = this._cleanupRuntimeSubscription) == null ? void 0 : _a.call(this);
2719
3225
  this._cleanupRuntimeSubscription = void 0;
2720
3226
  (_b = this._cloudAutoSaveScheduler) == null ? void 0 : _b.dispose();
2721
3227
  this._cloudAutoSaveScheduler = void 0;
2722
- if (!this._config.autoSaveEnabled || !((_c = this._runtime) == null ? void 0 : _c.subscribe) || !((_d = this._sdk) == null ? void 0 : _d.cloud)) {
3228
+ if (!config.autoSaveEnabled || !((_c = this._runtime) == null ? void 0 : _c.subscribe) || !(sdk == null ? void 0 : sdk.cloud)) {
2723
3229
  return;
2724
3230
  }
2725
3231
  this._cloudAutoSaveScheduler = this.requireCloudController().scheduleAutoSave(
@@ -2735,18 +3241,24 @@ class GeneratorWorkbenchElement extends HTMLElement {
2735
3241
  });
2736
3242
  }
2737
3243
  bindRuntimeCloudSaveEvent() {
3244
+ const sdk = this.getEffectiveSdk();
3245
+ const config = this.getEffectiveConfig();
2738
3246
  this.removeEventListener(
2739
3247
  "runtime-cloud-save-request",
2740
3248
  this.handleRuntimeCloudSaveRequest
2741
3249
  );
3250
+ if (!config.cloudEnabled || !(sdk == null ? void 0 : sdk.cloud)) {
3251
+ return;
3252
+ }
2742
3253
  this.addEventListener(
2743
3254
  "runtime-cloud-save-request",
2744
3255
  this.handleRuntimeCloudSaveRequest
2745
3256
  );
2746
3257
  }
2747
3258
  async initializeCloudRecordFromRoute() {
2748
- var _a;
2749
- if (!this._config.cloudEnabled || !((_a = this._sdk) == null ? void 0 : _a.cloud)) {
3259
+ const sdk = this.getEffectiveSdk();
3260
+ const config = this.getEffectiveConfig();
3261
+ if (!config.cloudEnabled || !(sdk == null ? void 0 : sdk.cloud)) {
2750
3262
  return;
2751
3263
  }
2752
3264
  const routeGid = this.getRouteCloudRecordId();
@@ -2813,19 +3325,20 @@ class GeneratorWorkbenchElement extends HTMLElement {
2813
3325
  );
2814
3326
  }
2815
3327
  bindBillingSubscriptions() {
2816
- var _a, _b, _c, _d;
3328
+ var _a, _b;
3329
+ const sdk = this.getEffectiveSdk();
2817
3330
  (_a = this._cleanupCredits) == null ? void 0 : _a.call(this);
2818
3331
  this._cleanupCredits = void 0;
2819
3332
  (_b = this._cleanupBilling) == null ? void 0 : _b.call(this);
2820
3333
  this._cleanupBilling = void 0;
2821
- const creditsModule = (_c = this._sdk) == null ? void 0 : _c.credits;
3334
+ const creditsModule = sdk == null ? void 0 : sdk.credits;
2822
3335
  if (typeof (creditsModule == null ? void 0 : creditsModule.onChange) === "function") {
2823
3336
  this._cleanupCredits = creditsModule.onChange((balance) => {
2824
3337
  this._state.creditsBalance = Number(balance) || 0;
2825
3338
  this.syncBillingUI();
2826
3339
  });
2827
3340
  }
2828
- const billingModule = (_d = this._sdk) == null ? void 0 : _d.billing;
3341
+ const billingModule = sdk == null ? void 0 : sdk.billing;
2829
3342
  if (typeof (billingModule == null ? void 0 : billingModule.onChange) === "function") {
2830
3343
  this._cleanupBilling = billingModule.onChange((usage) => {
2831
3344
  this.applyBillingUsage(usage);
@@ -2864,11 +3377,14 @@ class GeneratorWorkbenchElement extends HTMLElement {
2864
3377
  if (!this._shellContext) return;
2865
3378
  const { state } = this._shellContext;
2866
3379
  const usage = this._state.billingUsage;
3380
+ state.creditsEnabled = this.getEffectiveConfig().creditsEnabled !== false;
2867
3381
  state.creditsBalance = this._state.creditsBalance;
2868
3382
  state.showExportCreditsHint = Boolean(
2869
3383
  this._state.authStatus.isLogin && (usage == null ? void 0 : usage.isEnabled)
2870
3384
  );
2871
- state.showExportCreditsIcon = Boolean(state.showExportCreditsHint && !(usage == null ? void 0 : usage.inFreePeriod));
3385
+ state.showExportCreditsIcon = Boolean(
3386
+ state.showExportCreditsHint && !(usage == null ? void 0 : usage.inFreePeriod) && ((usage == null ? void 0 : usage.freeRemaining) ?? 0) <= 0
3387
+ );
2872
3388
  state.exportCreditsHintText = this.resolveExportCreditsHintText(usage);
2873
3389
  }
2874
3390
  syncInvitationConfigKey() {
@@ -2924,7 +3440,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
2924
3440
  return `${usage.freePeriodRemaining}s`;
2925
3441
  }
2926
3442
  if (usage.freeRemaining > 0) {
2927
- return `free ${usage.freeRemaining}`;
3443
+ return `${usage.freeRemaining}/${usage.freeTotal}`;
2928
3444
  }
2929
3445
  return String(usage.creditsPerUse);
2930
3446
  }
@@ -2974,15 +3490,20 @@ class GeneratorWorkbenchElement extends HTMLElement {
2974
3490
  }
2975
3491
  async syncBillingState(status = this._state.authStatus) {
2976
3492
  var _a, _b, _c, _d, _e, _f, _g, _h;
2977
- if (!status.isLogin || !this._sdk) {
3493
+ const sdk = this.getEffectiveSdk();
3494
+ if (!status.isLogin || !sdk) {
3495
+ this.resetBillingState();
3496
+ return;
3497
+ }
3498
+ if (!sdk.credits && !sdk.billing) {
2978
3499
  this.resetBillingState();
2979
3500
  return;
2980
3501
  }
2981
- const cachedBalance = (_b = (_a = this._sdk.credits) == null ? void 0 : _a.getCachedBalance) == null ? void 0 : _b.call(_a);
3502
+ const cachedBalance = (_b = (_a = sdk.credits) == null ? void 0 : _a.getCachedBalance) == null ? void 0 : _b.call(_a);
2982
3503
  if (typeof cachedBalance === "number") {
2983
3504
  this._state.creditsBalance = cachedBalance;
2984
3505
  }
2985
- const cachedUsage = (_d = (_c = this._sdk.billing) == null ? void 0 : _c.getCachedUsage) == null ? void 0 : _d.call(_c);
3506
+ const cachedUsage = (_d = (_c = sdk.billing) == null ? void 0 : _c.getCachedUsage) == null ? void 0 : _d.call(_c);
2986
3507
  if (cachedUsage) {
2987
3508
  this.applyBillingUsage(cachedUsage);
2988
3509
  } else {
@@ -2990,8 +3511,8 @@ class GeneratorWorkbenchElement extends HTMLElement {
2990
3511
  }
2991
3512
  try {
2992
3513
  const [creditsResult, usage] = await Promise.all([
2993
- (_f = (_e = this._sdk.credits) == null ? void 0 : _e.getBalance) == null ? void 0 : _f.call(_e),
2994
- (_h = (_g = this._sdk.billing) == null ? void 0 : _g.getUsage) == null ? void 0 : _h.call(_g)
3514
+ (_f = (_e = sdk.credits) == null ? void 0 : _e.getBalance) == null ? void 0 : _f.call(_e),
3515
+ (_h = (_g = sdk.billing) == null ? void 0 : _g.getUsage) == null ? void 0 : _h.call(_g)
2995
3516
  ]);
2996
3517
  if (typeof (creditsResult == null ? void 0 : creditsResult.quota) === "number") {
2997
3518
  this._state.creditsBalance = creditsResult.quota;
@@ -3005,6 +3526,35 @@ class GeneratorWorkbenchElement extends HTMLElement {
3005
3526
  this.handleError("auth", error);
3006
3527
  }
3007
3528
  }
3529
+ getEffectiveConfig() {
3530
+ if (getRouteCapabilityMode() !== "embed") {
3531
+ return this._config;
3532
+ }
3533
+ return {
3534
+ ...this._config,
3535
+ invitationEnabled: false,
3536
+ cloudEnabled: false,
3537
+ historyEnabled: false,
3538
+ autoSaveEnabled: false,
3539
+ creditsEnabled: false,
3540
+ shellChromeVisible: false
3541
+ };
3542
+ }
3543
+ getEffectiveSdk() {
3544
+ if (!this._sdk) {
3545
+ return null;
3546
+ }
3547
+ if (getRouteCapabilityMode() !== "embed") {
3548
+ return this._sdk;
3549
+ }
3550
+ return {
3551
+ ...this._sdk,
3552
+ credits: void 0,
3553
+ billing: void 0,
3554
+ cloud: void 0,
3555
+ history: void 0
3556
+ };
3557
+ }
3008
3558
  async handleLogin() {
3009
3559
  try {
3010
3560
  this._state.busy.login = true;
@@ -3021,6 +3571,26 @@ class GeneratorWorkbenchElement extends HTMLElement {
3021
3571
  }
3022
3572
  }
3023
3573
  }
3574
+ async handleInvitationEntry() {
3575
+ var _a;
3576
+ try {
3577
+ if (!this._state.authStatus.isLogin) {
3578
+ await this.handleLogin();
3579
+ const status = this.requireAuthController().getStatus();
3580
+ if (status.isLogin) {
3581
+ this._state.authStatus = status;
3582
+ this.syncAuthUI();
3583
+ void this.syncBillingState(status);
3584
+ }
3585
+ }
3586
+ if (!this.requireAuthController().getStatus().isLogin) {
3587
+ return;
3588
+ }
3589
+ (_a = this._shellContext) == null ? void 0 : _a.openInvitationModal();
3590
+ } catch (error) {
3591
+ this.handleError("auth", error);
3592
+ }
3593
+ }
3024
3594
  async handleLogout() {
3025
3595
  try {
3026
3596
  await this.requireAuthController().logout();