@atomm-developer/generator-workbench 0.1.4 → 0.1.5

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 CHANGED
@@ -9,7 +9,7 @@ V1 provides:
9
9
  - credits badge and export credit hint
10
10
  - local template import entry and template publish modal
11
11
  - billing-backed export actions
12
- - runtime panel / canvas mounting
12
+ - runtime mounting for either a free workspace host or split canvas / panel hosts
13
13
 
14
14
  ## Install
15
15
 
@@ -25,7 +25,7 @@ pnpm add @atomm-developer/generator-workbench
25
25
  <script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-workbench/index.umd.js"></script>
26
26
  ```
27
27
 
28
- `generator-workbench` will inject the default `atomm-ui` CDN CSS into its Shadow DOM and auto-load the default `atomm-ui` CDN script when `window.AtommUI` is absent. You only need to provide Vue 3 in environments that rely on the global CDN build.
28
+ `generator-workbench` auto-loads Vue 3 when `window.Vue` is absent, injects the default `atomm-ui` CDN CSS into its Shadow DOM, and auto-loads the default `atomm-ui` CDN script when `window.AtommUI` is absent. The host page no longer needs to add Vue or Atomm UI scripts just for the shell.
29
29
 
30
30
  ## Usage
31
31
 
@@ -41,7 +41,7 @@ workbench.sdk = sdk
41
41
  workbench.runtime = runtime
42
42
  workbench.config = {
43
43
  title: 'My Generator',
44
- mode: 'full',
44
+ mode: 'shell',
45
45
  templateEnabled: true,
46
46
  exportEnabled: true,
47
47
  studioEnabled: true,
@@ -55,6 +55,7 @@ If you need to override the default CDN addresses, pass:
55
55
  ```js
56
56
  workbench.config = {
57
57
  title: 'My Generator',
58
+ vueScriptUrl: 'https://your-cdn/vue.global.prod.js',
58
59
  atommUiCssUrl: 'https://your-cdn/atomm-ui.css',
59
60
  atommUiScriptUrl: 'https://your-cdn/atomm-ui.js',
60
61
  }
@@ -62,7 +63,8 @@ workbench.config = {
62
63
 
63
64
  ## Shell Modes
64
65
 
65
- - `mode: 'full'` keeps the default shell with the top bar and the sidebar footer export area.
66
+ - `mode: 'shell'` keeps the top bar, moves `#sidebar-footer` to a fixed bottom-right floating export entry, and mounts the runtime into a free workspace host so the generator owns the full internal layout.
67
+ - `mode: 'full'` keeps the classic shell with the top bar plus the built-in right sidebar layout for separate canvas / panel mounting.
66
68
  - `mode: 'template'` hides the top bar, keeps `#sidebar-footer`, and is useful when the host page already owns branding and login UI.
67
69
 
68
70
  In `template` mode, the host shell can pass an external token into the workbench:
@@ -93,3 +95,5 @@ pnpm --dir generator-workbench dev
93
95
  ```
94
96
 
95
97
  Then open `http://127.0.0.1:5173/examples/basic/`.
98
+
99
+ For the new shell-mode example, open `http://127.0.0.1:5173/examples/shell/`.
package/dist/index.d.ts CHANGED
@@ -91,7 +91,7 @@ export declare interface GeneratorWorkbenchActionHookContext {
91
91
 
92
92
  export declare interface GeneratorWorkbenchConfig {
93
93
  title: string;
94
- mode?: 'full' | 'template';
94
+ mode?: 'full' | 'template' | 'shell';
95
95
  logoText?: string;
96
96
  logoUrl?: string;
97
97
  logoHref?: string;
@@ -101,6 +101,8 @@ export declare interface GeneratorWorkbenchConfig {
101
101
  autoMount?: boolean;
102
102
  panelTarget?: 'left' | 'right';
103
103
  avatarMenuTrigger?: 'hover' | 'click';
104
+ /** Override the default Vue 3 CDN script URL auto-loaded when window.Vue is absent */
105
+ vueScriptUrl?: string;
104
106
  /** Override the default atomm-ui CDN CSS URL injected into Shadow DOM */
105
107
  atommUiCssUrl?: string;
106
108
  /** Override the default atomm-ui CDN script URL auto-loaded when AtommUI is absent */
package/dist/index.es.js CHANGED
@@ -76,6 +76,18 @@ function createRuntimeController(args) {
76
76
  })
77
77
  );
78
78
  }
79
+ async function mountWorkspace(container) {
80
+ canvasInstance == null ? void 0 : canvasInstance.unmount();
81
+ panelInstance == null ? void 0 : panelInstance.unmount();
82
+ canvasInstance = await Promise.resolve(
83
+ runtime.mount({
84
+ mode: "full",
85
+ target: "full",
86
+ container
87
+ })
88
+ );
89
+ panelInstance = null;
90
+ }
79
91
  async function mountPanel(container, panelFilter) {
80
92
  panelInstance == null ? void 0 : panelInstance.unmount();
81
93
  panelInstance = await Promise.resolve(
@@ -88,6 +100,7 @@ function createRuntimeController(args) {
88
100
  );
89
101
  }
90
102
  return {
103
+ mountWorkspace,
91
104
  mountCanvas,
92
105
  mountPanel,
93
106
  async remountPanel(container, panelFilter) {
@@ -273,11 +286,15 @@ function collectWorkbenchRefs(root) {
273
286
  root,
274
287
  workspace: queryRequired(root, '[data-role="workspace"]'),
275
288
  logoArea: root.querySelector('[data-role="logo-area"]'),
276
- canvasHost: queryRequired(root, '[data-role="canvas-host"]'),
277
- panelHost: queryRequired(root, '[data-role="panel-host"]'),
289
+ workspaceHost: root.querySelector('[data-role="workspace-host"]'),
290
+ canvasHost: root.querySelector('[data-role="canvas-host"]'),
291
+ panelHost: root.querySelector('[data-role="panel-host"]'),
278
292
  templateFileInput: queryRequired(root, '[data-role="template-file-input"]')
279
293
  };
280
294
  }
295
+ const DOWNLOAD_MENU_ICON_URL = "data:image/svg+xml,%3c?xml%20version='1.0'%20standalone='no'?%3e%3c!DOCTYPE%20svg%20PUBLIC%20'-//W3C//DTD%20SVG%201.1//EN'%20'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3e%3csvg%20t='1775118703519'%20class='icon'%20viewBox='0%200%201024%201024'%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20p-id='12828'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20width='200'%20height='200'%3e%3cpath%20d='M469.333333%20596.053333V128h85.333334v468.053333l208.64-160.554666%2052.053333%2067.669333-277.333333%20213.333333a42.666667%2042.666667%200%200%201-52.053334%200l-277.333333-213.333333%2052.053333-67.669333L469.333333%20596.053333zM896%20896H128v-85.333333h768v85.333333z'%20p-id='12829'%3e%3c/path%3e%3c/svg%3e";
296
+ const OPEN_IN_STUDIO_MENU_ICON_URL = "data:image/svg+xml,%3c?xml%20version='1.0'%20standalone='no'?%3e%3c!DOCTYPE%20svg%20PUBLIC%20'-//W3C//DTD%20SVG%201.1//EN'%20'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3e%3csvg%20t='1775118714051'%20class='icon'%20viewBox='0%200%201024%201024'%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20p-id='12988'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20width='200'%20height='200'%3e%3cpath%20d='M197.553231%20309.169231h128.945231l85.858461%2085.858461-64.512%2064.827077-150.291692-150.685538z%20m472.694154%2021.267692L509.006769%20492.307692l182.429539%20183.059693H562.491077L444.494769%20557.056%20326.498462%20675.288615H197.553231l364.937846-366.119384h128.945231l-21.188923%2021.267692z'%20p-id='12989'%3e%3c/path%3e%3cpath%20d='M157.538462%200a157.538462%20157.538462%200%200%200-157.538462%20157.538462v682.692923a157.538462%20157.538462%200%200%200%20157.538462%20157.538461h236.307692v-105.078154H157.538462a52.539077%2052.539077%200%200%201-52.539077-52.460307V157.538462c0-28.987077%2023.552-52.539077%2052.539077-52.539077h577.614769c29.065846%200%2052.539077%2023.552%2052.539077%2052.539077v393.846153h104.999384V157.538462a157.538462%20157.538462%200%200%200-157.538461-157.538462H157.538462z'%20p-id='12990'%3e%3c/path%3e%3cpath%20d='M789.267692%20681.038769l159.113846%20159.113846-159.113846%20159.113847-55.689846-55.611077%2064.039385-64.039385H551.384615v-78.769231h246.232616l-64.039385-64.039384%2055.689846-55.768616z'%20p-id='12991'%3e%3c/path%3e%3c/svg%3e";
297
+ const VUE_CDN_SCRIPT_URL = "https://unpkg.com/vue@3/dist/vue.global.prod.js";
281
298
  const ATOMM_UI_CDN_CSS_URL = "https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.min.css";
282
299
  const ATOMM_UI_CDN_SCRIPT_URL = "https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.js";
283
300
  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";
@@ -351,6 +368,17 @@ const WORKBENCH_SHELL_STYLES = `
351
368
  grid-template-columns: minmax(0, 1fr) 320px;
352
369
  }
353
370
 
371
+ .workspace.shell-mode-shell {
372
+ display: block;
373
+ position: relative;
374
+ }
375
+
376
+ .workspace-host {
377
+ height: 100%;
378
+ min-height: 0;
379
+ position: relative;
380
+ }
381
+
354
382
  .workspace.panel-left {
355
383
  grid-template-columns: 320px minmax(0, 1fr);
356
384
  }
@@ -570,11 +598,41 @@ const WORKBENCH_SHELL_STYLES = `
570
598
  .panel-actions,
571
599
  #sidebar-footer {
572
600
  flex-shrink: 0;
601
+ width: 262px;
602
+ display: flex;
603
+ align-items: center;
604
+ }
605
+
606
+ .panel-sidebar #sidebar-footer {
573
607
  padding: 14px 24px;
574
- border-top: 1px solid var(--border-default);
575
- background: #ffffff;
608
+ flex-shrink: 0;
609
+ width: auto;
576
610
  display: flex;
577
611
  align-items: center;
612
+
613
+ }
614
+
615
+ .sidebar-footer-floating {
616
+ position: fixed;
617
+ right: 24px;
618
+ bottom: 24px;
619
+ z-index: 30;
620
+ width: auto;
621
+ padding: 0;
622
+ border-top: 0;
623
+ background: transparent;
624
+ box-shadow: none;
625
+ }
626
+
627
+ .sidebar-footer-floating .sidebar-export-trigger-btn,
628
+ .sidebar-footer-floating .sidebar-export-trigger-btn-wrap,
629
+ .sidebar-footer-floating .sidebar-export-trigger-btn-wrap {
630
+ width: auto;
631
+ }
632
+
633
+ .sidebar-footer-floating .sidebar-export-trigger-btn {
634
+ min-width: 180px;
635
+ box-shadow: 0 12px 28px rgba(17, 24, 39, 0.18);
578
636
  }
579
637
 
580
638
  .sidebar-footer-export {
@@ -641,18 +699,19 @@ const WORKBENCH_SHELL_STYLES = `
641
699
  .export-overlay-item-icon {
642
700
  width: 16px;
643
701
  height: 16px;
644
- border-radius: 4px;
645
- background: #cbd5e1;
646
702
  display: inline-block;
647
703
  flex: 0 0 auto;
704
+ background-repeat: no-repeat;
705
+ background-position: center;
706
+ background-size: contain;
648
707
  }
649
708
 
650
709
  .export-overlay-item-icon--svg {
651
- background: linear-gradient(180deg, #94a3b8, #64748b);
710
+ background-image: url("${DOWNLOAD_MENU_ICON_URL}");
652
711
  }
653
712
 
654
713
  .export-overlay-item-icon--studio {
655
- background: linear-gradient(180deg, #cbd5e1, #94a3b8);
714
+ background-image: url("${OPEN_IN_STUDIO_MENU_ICON_URL}");
656
715
  }
657
716
 
658
717
  .workspace.panel-right .panel-sidebar {
@@ -842,66 +901,136 @@ const WORKBENCH_VUE_TEMPLATE = `
842
901
 
843
902
  <div
844
903
  class="workspace"
845
- :class="state.panelTarget === 'left' ? 'panel-left' : 'panel-right'"
904
+ :class="[
905
+ state.shellMode === 'shell'
906
+ ? 'shell-mode-shell'
907
+ : (state.panelTarget === 'left' ? 'panel-left' : 'panel-right'),
908
+ ]"
846
909
  data-role="workspace"
847
910
  >
848
- <main class="canvas-host" data-role="canvas-host"></main>
849
- <aside class="panel-sidebar" data-role="panel-sidebar">
850
- <div class="panel-host" data-role="panel-host"></div>
851
- <div id="sidebar-footer" class="sidebar-footer-export panel-actions" data-role="panel-actions">
852
- <xt-dropdown-menu
853
- trigger="click"
854
- :portal-disabled="true"
855
- placement="topLeft"
856
- domTriggerClass="sidebar-export-trigger-btn-wrap"
857
- :show-trigger-icon="false"
858
- domContentClass="sidebar-export-dropdown-menu"
859
- >
860
- <template #trigger>
861
- <xt-button class="sidebar-export-trigger-btn" block data-role="fab-trigger">
862
- <template #icon>
863
- <img
864
- src="${EXPORT_ICON_DATA_URI}"
865
- alt=""
866
- width="16"
867
- height="16"
868
- draggable="false"
869
- />
870
- </template>
871
- Export SVG
872
- <template #append-icon>
873
- <div v-if="state.isLogin" class="export-credits-hint">
874
- <img src="${CREDIT_ICON_DATA_URI}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />
875
- <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>
876
- </div>
877
- </template>
878
- </xt-button>
879
- </template>
880
- <template #overlay>
881
- <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">
882
- <div
883
- v-show="state.exportEnabled"
884
- class="export-overlay-item"
885
- data-role="export-svg"
886
- @click="callbacks.onExportSvg()"
887
- >
888
- <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>
911
+ <main
912
+ v-if="state.shellMode === 'shell'"
913
+ class="workspace-host"
914
+ data-role="workspace-host"
915
+ ></main>
916
+ <template v-else>
917
+ <main class="canvas-host" data-role="canvas-host"></main>
918
+ <aside class="panel-sidebar" data-role="panel-sidebar">
919
+ <div class="panel-host" data-role="panel-host"></div>
920
+ <div id="sidebar-footer" class="sidebar-footer-export panel-actions" data-role="panel-actions">
921
+ <xt-dropdown-menu
922
+ trigger="click"
923
+ :portal-disabled="true"
924
+ placement="topLeft"
925
+ domTriggerClass="sidebar-export-trigger-btn-wrap"
926
+ :show-trigger-icon="false"
927
+ domContentClass="sidebar-export-dropdown-menu"
928
+ >
929
+ <template #trigger>
930
+ <xt-button class="sidebar-export-trigger-btn" block data-role="fab-trigger">
931
+ <template #icon>
932
+ <img
933
+ src="${EXPORT_ICON_DATA_URI}"
934
+ alt=""
935
+ width="16"
936
+ height="16"
937
+ draggable="false"
938
+ />
939
+ </template>
889
940
  Export SVG
941
+ <template #append-icon>
942
+ <div v-if="state.isLogin" class="export-credits-hint">
943
+ <img src="${CREDIT_ICON_DATA_URI}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />
944
+ <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>
945
+ </div>
946
+ </template>
947
+ </xt-button>
948
+ </template>
949
+ <template #overlay>
950
+ <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">
951
+ <div
952
+ v-show="state.exportEnabled"
953
+ class="export-overlay-item"
954
+ data-role="export-svg"
955
+ @click="callbacks.onExportSvg()"
956
+ >
957
+ <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>
958
+ Download
959
+ </div>
960
+ <div
961
+ v-show="state.studioEnabled"
962
+ class="export-overlay-item"
963
+ data-role="open-in-studio"
964
+ @click="callbacks.onOpenInStudio()"
965
+ >
966
+ <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>
967
+ Open in Studio
968
+ </div>
890
969
  </div>
891
- <div
892
- v-show="state.studioEnabled"
893
- class="export-overlay-item"
894
- data-role="open-in-studio"
895
- @click="callbacks.onOpenInStudio()"
896
- >
897
- <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>
898
- Open in Studio
899
- </div>
970
+ </template>
971
+ </xt-dropdown-menu>
972
+ </div>
973
+ </aside>
974
+ </template>
975
+ </div>
976
+
977
+ <div
978
+ v-if="state.shellMode === 'shell'"
979
+ id="sidebar-footer"
980
+ class="sidebar-footer-export panel-actions sidebar-footer-floating"
981
+ data-role="panel-actions"
982
+ >
983
+ <xt-dropdown-menu
984
+ trigger="click"
985
+ :portal-disabled="true"
986
+ placement="topLeft"
987
+ domTriggerClass="sidebar-export-trigger-btn-wrap"
988
+ :show-trigger-icon="false"
989
+ domContentClass="sidebar-export-dropdown-menu"
990
+ >
991
+ <template #trigger>
992
+ <xt-button class="sidebar-export-trigger-btn" data-role="fab-trigger">
993
+ <template #icon>
994
+ <img
995
+ src="${EXPORT_ICON_DATA_URI}"
996
+ alt=""
997
+ width="16"
998
+ height="16"
999
+ draggable="false"
1000
+ />
1001
+ </template>
1002
+ Export SVG
1003
+ <template #append-icon>
1004
+ <div v-if="state.isLogin" class="export-credits-hint">
1005
+ <img src="${CREDIT_ICON_DATA_URI}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />
1006
+ <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>
900
1007
  </div>
901
1008
  </template>
902
- </xt-dropdown-menu>
903
- </div>
904
- </aside>
1009
+ </xt-button>
1010
+ </template>
1011
+ <template #overlay>
1012
+ <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">
1013
+ <div
1014
+ v-show="state.exportEnabled"
1015
+ class="export-overlay-item"
1016
+ data-role="export-svg"
1017
+ @click="callbacks.onExportSvg()"
1018
+ >
1019
+ <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>
1020
+ Download
1021
+ </div>
1022
+ <div
1023
+ v-show="state.studioEnabled"
1024
+ class="export-overlay-item"
1025
+ data-role="open-in-studio"
1026
+ @click="callbacks.onOpenInStudio()"
1027
+ >
1028
+ <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>
1029
+ Open in Studio
1030
+ </div>
1031
+ </div>
1032
+ </template>
1033
+ </xt-dropdown-menu>
905
1034
  </div>
906
1035
 
907
1036
  <input
@@ -968,16 +1097,68 @@ const WORKBENCH_VUE_TEMPLATE = `
968
1097
  </xt-modal>
969
1098
  </div>
970
1099
  `;
1100
+ const vueRuntimePromises = /* @__PURE__ */ new Map();
971
1101
  let atommUiLoadPromise = null;
972
1102
  const atommUiCssTextPromises = /* @__PURE__ */ new Map();
973
- function requireVue() {
974
- const Vue = globalThis.Vue;
975
- if (!Vue || typeof Vue.createApp !== "function") {
976
- throw new Error(
977
- '[generator-workbench] Vue 3 is required. Load it via CDN: <script src="https://unpkg.com/vue@3/dist/vue.global.js"><\/script>'
1103
+ function getVueRuntime() {
1104
+ return globalThis.Vue;
1105
+ }
1106
+ function createVueLoadError(scriptUrl) {
1107
+ return new Error(
1108
+ `[generator-workbench] failed to auto-load Vue 3 runtime from ${scriptUrl}`
1109
+ );
1110
+ }
1111
+ function loadVueRuntime(scriptUrl) {
1112
+ const cached = vueRuntimePromises.get(scriptUrl);
1113
+ if (cached) {
1114
+ return cached;
1115
+ }
1116
+ const request = new Promise((resolve, reject) => {
1117
+ const existingVue = getVueRuntime();
1118
+ if (existingVue && typeof existingVue.createApp === "function") {
1119
+ resolve(existingVue);
1120
+ return;
1121
+ }
1122
+ const finalize = () => {
1123
+ const vue = getVueRuntime();
1124
+ if (vue && typeof vue.createApp === "function") {
1125
+ resolve(vue);
1126
+ return;
1127
+ }
1128
+ vueRuntimePromises.delete(scriptUrl);
1129
+ reject(createVueLoadError(scriptUrl));
1130
+ };
1131
+ const fail = () => {
1132
+ vueRuntimePromises.delete(scriptUrl);
1133
+ reject(createVueLoadError(scriptUrl));
1134
+ };
1135
+ const existingScript = Array.from(document.querySelectorAll("script")).find(
1136
+ (script2) => script2.src === scriptUrl
978
1137
  );
1138
+ if (existingScript) {
1139
+ existingScript.addEventListener("load", finalize, { once: true });
1140
+ existingScript.addEventListener("error", fail, { once: true });
1141
+ return;
1142
+ }
1143
+ const script = document.createElement("script");
1144
+ script.src = scriptUrl;
1145
+ script.async = true;
1146
+ script.dataset.generatorWorkbenchVue = "true";
1147
+ script.addEventListener("load", finalize, { once: true });
1148
+ script.addEventListener("error", fail, { once: true });
1149
+ (document.head || document.body || document.documentElement).appendChild(
1150
+ script
1151
+ );
1152
+ });
1153
+ vueRuntimePromises.set(scriptUrl, request);
1154
+ return request;
1155
+ }
1156
+ async function resolveVueRuntime(config) {
1157
+ const existingVue = getVueRuntime();
1158
+ if (existingVue && typeof existingVue.createApp === "function") {
1159
+ return existingVue;
979
1160
  }
980
- return Vue;
1161
+ return loadVueRuntime(config.vueScriptUrl || VUE_CDN_SCRIPT_URL);
981
1162
  }
982
1163
  function getAtommUiPlugin() {
983
1164
  return globalThis.AtommUI;
@@ -1170,7 +1351,7 @@ async function resolveAtommUiPlugin(config) {
1170
1351
  return loadAtommUiScript(config.atommUiScriptUrl || ATOMM_UI_CDN_SCRIPT_URL);
1171
1352
  }
1172
1353
  async function renderWorkbenchShell(root, config) {
1173
- const vue = requireVue();
1354
+ const vue = await resolveVueRuntime(config);
1174
1355
  ensureBrowserProcessEnv();
1175
1356
  await injectStyles(root, config.atommUiCssUrl);
1176
1357
  const state = stateFromConfig(vue, config);
@@ -1207,6 +1388,9 @@ const DEFAULT_CONFIG = {
1207
1388
  panelTarget: "right",
1208
1389
  avatarMenuTrigger: "hover"
1209
1390
  };
1391
+ function isShellMode(config) {
1392
+ return config.mode === "shell";
1393
+ }
1210
1394
  const TOKEN_STORAGE_KEY_PREFIX = "__atomm_sdk_token__";
1211
1395
  function normalizeWorkbenchConfig(config) {
1212
1396
  return {
@@ -1262,7 +1446,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
1262
1446
  void this.unmount();
1263
1447
  }
1264
1448
  async mount() {
1265
- var _a, _b;
1449
+ var _a, _b, _c;
1266
1450
  if (this._mounted) {
1267
1451
  return;
1268
1452
  }
@@ -1281,11 +1465,26 @@ class GeneratorWorkbenchElement extends HTMLElement {
1281
1465
  this.bindAuthState();
1282
1466
  await this.syncBillingState();
1283
1467
  const refs = this.requireRefs();
1284
- await ((_a = this._runtimeController) == null ? void 0 : _a.mountCanvas(refs.canvasHost));
1285
- await ((_b = this._runtimeController) == null ? void 0 : _b.mountPanel(
1286
- refs.panelHost,
1287
- this._state.activePanelFilter
1288
- ));
1468
+ if (isShellMode(this._config)) {
1469
+ const workspaceHost = refs.workspaceHost;
1470
+ if (!workspaceHost) {
1471
+ throw new Error("[generator-workbench] workspace host is required in shell mode");
1472
+ }
1473
+ await ((_a = this._runtimeController) == null ? void 0 : _a.mountWorkspace(workspaceHost));
1474
+ } else {
1475
+ const canvasHost = refs.canvasHost;
1476
+ const panelHost = refs.panelHost;
1477
+ if (!canvasHost || !panelHost) {
1478
+ throw new Error(
1479
+ "[generator-workbench] canvas host and panel host are required in full/template mode"
1480
+ );
1481
+ }
1482
+ await ((_b = this._runtimeController) == null ? void 0 : _b.mountCanvas(canvasHost));
1483
+ await ((_c = this._runtimeController) == null ? void 0 : _c.mountPanel(
1484
+ panelHost,
1485
+ this._state.activePanelFilter
1486
+ ));
1487
+ }
1289
1488
  this._mounted = true;
1290
1489
  dispatchWorkbenchEvent(this, "workbench-ready", {
1291
1490
  sdk: this._sdk,
@@ -1314,9 +1513,13 @@ class GeneratorWorkbenchElement extends HTMLElement {
1314
1513
  const result = await this.requireTemplateController().importTemplate(file);
1315
1514
  this._state.activePanelFilter = result.panelFilter;
1316
1515
  const refs = this.requireRefs();
1317
- if (this._runtimeController) {
1516
+ if (this._runtimeController && !isShellMode(this._config)) {
1517
+ const panelHost = refs.panelHost;
1518
+ if (!panelHost) {
1519
+ throw new Error("[generator-workbench] panel host is required for template import");
1520
+ }
1318
1521
  await this._runtimeController.remountPanel(
1319
- refs.panelHost,
1522
+ panelHost,
1320
1523
  result.panelFilter
1321
1524
  );
1322
1525
  }
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).GeneratorWorkbench={})}(this,function(t){"use strict";var e=Object.defineProperty,n=(t,n,a)=>((t,n,a)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[n]=a)(t,"symbol"!=typeof n?n+"":n,a);function a(t,e){if(!t)throw new Error(e)}function o(t,e){const n=(null==e?void 0:e.length)?new Set(e):null;return t.groups.map(t=>{var e;const a=t.fields.map(t=>{var e,a,o;const r=null==(a=null==(e=t.bind)?void 0:e.path)?void 0:a.trim();return r?n&&!n.has(r)?null:{id:t.id,label:(null==(o=t.label)?void 0:o.trim())||t.id||r,path:r}:null}).filter(t=>Boolean(t));return{id:t.id,title:(null==(e=t.title)?void 0:e.trim())||t.id,fields:a}}).filter(t=>t.fields.length>0)}function r(t,e){var n;if(null==(n=t.generatorId)?void 0:n.trim())return t.generatorId.trim();const a=e.meta;if(a&&"object"==typeof a&&"generatorId"in a&&"string"==typeof a.generatorId&&a.generatorId.trim())return a.generatorId.trim();throw new Error("[generator-workbench] generatorId is required in panelSchema.generatorId or state.meta.generatorId")}function i(t){const{sdk:e,runtime:n,config:i}=t;function l(){var t;const e=n.getState(),l=n.getPanelSchema(),s=o(l,null==(t=i.getTemplateFieldPaths)?void 0:t.call(i,l)),p=s.flatMap(t=>t.fields.map(t=>t.path));return a(p.length>0,"[generator-workbench] exportTemplate requires at least one panel field path"),{generatorId:r(l,e),state:e,panelSchema:l,fieldGroups:s,selectedFieldPaths:p}}return{async importTemplate(t){const a=await t.text(),o=e.template.parse(a);let r;return await e.template.applyToRuntime(n,o,{onPanelFilter(t){r=t}}),{template:o,panelFilter:r}},prepareTemplateExport:l,async exportTemplate(t){var n;const r=l(),s=(null==t?void 0:t.length)?o(r.panelSchema,t).flatMap(t=>t.fields.map(t=>t.path)):r.selectedFieldPaths;a(s.length>0,"[generator-workbench] exportTemplate requires at least one panel field path");const p=e.template.build({generatorId:r.generatorId,state:r.state,panelSchema:r.panelSchema,selectedFieldPaths:s,templateMeta:null==(n=i.getTemplateMeta)?void 0:n.call(i)});return e.template.download(p),{template:p}}}}function l(t,e,n){return t.dispatchEvent(new CustomEvent(e,{detail:n,bubbles:!0,composed:!0}))}function s(t,e){const n=t.querySelector(e);if(!n)throw new Error(`[generator-workbench] required DOM node not found: ${e}`);return n}const p="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",d=`\n <div class="shell">\n <header v-if="state.shellMode !== 'template'" class="topbar app-topbar">\n <div class="logo-area app-topbar-main" data-role="logo-area">\n <img\n :src="state.logoSrc"\n :alt="state.logoText || 'Atomm'"\n class="brand-logo"\n data-role="brand-logo"\n draggable="false"\n />\n <div class="app-topbar-nav">\n <xt-button\n v-show="state.templateEnabled"\n type="secondary"\n size="small"\n data-role="import-template"\n @click="callbacks.onImportTemplate()"\n >导入模板</xt-button>\n <xt-button\n v-show="state.templateEnabled"\n type="secondary"\n size="small"\n data-role="export-template"\n @click="callbacks.onExportTemplate()"\n >生成模板</xt-button>\n </div>\n </div>\n <div class="app-topbar-auth">\n <div\n v-if="state.isLogin"\n class="topbar-credits-badge"\n data-role="topbar-credits"\n title="Remaining credits"\n >\n <img src="${p}" alt="" class="credits-token-icon" draggable="false" />\n <span class="topbar-credits-value" data-role="topbar-credits-value">{{ state.creditsBalance }}</span>\n </div>\n\n <div\n v-if="state.isLogin && state.avatarMenuTrigger === 'hover'"\n class="auth-hover-card"\n >\n <div class="auth-avatar-trigger" data-role="avatar-button" tabindex="0">\n <div class="auth-avatar">\n <img\n v-if="state.avatarSrc"\n :src="state.avatarSrc"\n alt="Avatar"\n data-role="avatar-image"\n />\n <span v-else data-role="avatar-image">{{ state.avatarText }}</span>\n </div>\n </div>\n <div class="auth-hover-panel">\n <div class="auth-popover-body" data-role="avatar-menu">\n <div class="auth-popover-header">\n <div class="auth-avatar auth-avatar--lg">\n <img v-if="state.avatarSrc" :src="state.avatarSrc" alt="Avatar" />\n <span v-else>{{ state.avatarText }}</span>\n </div>\n <div class="auth-popover-user-text">\n <span class="auth-popover-name">{{ state.authDisplayName }}</span>\n <span class="auth-popover-sub">{{ state.authSubline }}</span>\n </div>\n </div>\n <div class="auth-popover-divider"></div>\n <div class="auth-popover-action" data-role="logout" @click="callbacks.onLogout()">\n Logout\n </div>\n </div>\n </div>\n </div>\n\n <xt-dropdown-menu\n v-else-if="state.isLogin"\n trigger="click"\n :portal-disabled="true"\n placement="bottomRight"\n >\n <template #trigger>\n <div class="auth-avatar-trigger" data-role="avatar-button">\n <div class="auth-avatar">\n <img\n v-if="state.avatarSrc"\n :src="state.avatarSrc"\n alt="Avatar"\n data-role="avatar-image"\n />\n <span v-else data-role="avatar-image">{{ state.avatarText }}</span>\n </div>\n </div>\n </template>\n <template #overlay>\n <div class="auth-popover-body" data-role="avatar-menu">\n <div class="auth-popover-header">\n <div class="auth-avatar auth-avatar--lg">\n <img v-if="state.avatarSrc" :src="state.avatarSrc" alt="Avatar" />\n <span v-else>{{ state.avatarText }}</span>\n </div>\n <div class="auth-popover-user-text">\n <span class="auth-popover-name">{{ state.authDisplayName }}</span>\n <span class="auth-popover-sub">{{ state.authSubline }}</span>\n </div>\n </div>\n <div class="auth-popover-divider"></div>\n <div class="auth-popover-action" data-role="logout" @click="callbacks.onLogout()">\n Logout\n </div>\n </div>\n </template>\n </xt-dropdown-menu>\n\n <xt-button\n size="small"\n type="secondary"\n data-role="login"\n :loading="state.loginLoading"\n v-if="!state.isLogin"\n @click="callbacks.onLogin()"\n >Login</xt-button>\n </div>\n </header>\n\n <div\n class="workspace"\n :class="state.panelTarget === 'left' ? 'panel-left' : 'panel-right'"\n data-role="workspace"\n >\n <main class="canvas-host" data-role="canvas-host"></main>\n <aside class="panel-sidebar" data-role="panel-sidebar">\n <div class="panel-host" data-role="panel-host"></div>\n <div id="sidebar-footer" class="sidebar-footer-export panel-actions" data-role="panel-actions">\n <xt-dropdown-menu\n trigger="click"\n :portal-disabled="true"\n placement="topLeft"\n domTriggerClass="sidebar-export-trigger-btn-wrap"\n :show-trigger-icon="false"\n domContentClass="sidebar-export-dropdown-menu"\n >\n <template #trigger>\n <xt-button class="sidebar-export-trigger-btn" block data-role="fab-trigger">\n <template #icon>\n <img\n src="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"\n alt=""\n width="16"\n height="16"\n draggable="false"\n />\n </template>\n Export SVG\n <template #append-icon>\n <div v-if="state.isLogin" class="export-credits-hint">\n <img src="${p}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />\n <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>\n </div>\n </template>\n </xt-button>\n </template>\n <template #overlay>\n <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">\n <div\n v-show="state.exportEnabled"\n class="export-overlay-item"\n data-role="export-svg"\n @click="callbacks.onExportSvg()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>\n Export SVG\n </div>\n <div\n v-show="state.studioEnabled"\n class="export-overlay-item"\n data-role="open-in-studio"\n @click="callbacks.onOpenInStudio()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>\n Open in Studio\n </div>\n </div>\n </template>\n </xt-dropdown-menu>\n </div>\n </aside>\n </div>\n\n <input\n data-role="template-file-input"\n type="file"\n accept="application/json"\n style="display:none"\n @change="callbacks.onFileChange($event)"\n />\n\n <xt-modal\n :model-value="state.templateDialogOpen"\n title="发布模板"\n :show-footer="false"\n :portal-disabled="true"\n @update:model-value="callbacks.onToggleTemplateDialog($event)"\n >\n <div\n v-if="state.templateDialogOpen"\n class="template-export-modal"\n data-role="template-export-modal"\n >\n \n <div class="template-export-groups">\n <div\n v-for="group in state.templateFieldGroups"\n :key="group.id"\n class="template-export-group"\n >\n <div class="template-export-group-title">{{ group.title }}</div>\n <div class="template-export-field-list">\n <div\n v-for="field in group.fields"\n :key="field.path"\n class="template-export-field"\n data-role="template-export-field"\n >\n <xt-checkbox\n :model-value="state.templateSelectedFieldPaths.includes(field.path)"\n :disabled="state.templateExportLoading"\n @update:model-value="callbacks.onToggleTemplateField(field.path, $event)"\n >{{ field.label }}</xt-checkbox>\n <div class="template-export-field-path">{{ field.path }}</div>\n </div>\n </div>\n </div>\n </div>\n\n <div class="template-export-footer">\n <xt-button\n type="secondary"\n :disabled="state.templateExportLoading"\n @click="callbacks.onCloseTemplateDialog()"\n >取消</xt-button>\n <xt-button\n type="primary"\n data-role="template-export-confirm"\n :loading="state.templateExportLoading"\n :disabled="state.templateSelectedFieldPaths.length === 0"\n @click="callbacks.onConfirmTemplateExport()"\n >发布模板</xt-button>\n </div>\n </div>\n </xt-modal>\n </div>\n`;let c=null;const u=new Map;function h(){return globalThis.AtommUI}async function m(t,e){const n=e||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.min.css",a=await function(t){const e=u.get(t);if(e)return e;const n=fetch(t).then(async e=>e.ok?(await e.text()).replace(/(^|})\s*:root(?=\s*{)/g,"$1:host"):(u.delete(t),null)).catch(()=>(u.delete(t),null));return u.set(t,n),n}(n);if(a){const e=document.createElement("style");e.dataset.generatorWorkbenchAtommUi="true",e.textContent=a,t.appendChild(e)}else{const e=document.createElement("link");e.rel="stylesheet",e.href=n,t.appendChild(e)}const o=document.createElement("style");o.textContent='\n :host {\n display: block;\n height: 100%;\n color: #111827;\n font-family: Inter, "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;\n --bg-soft: #f3f4f6;\n --border-default: #e5e7eb;\n --text-primary: #111827;\n --text-secondary: #4b5563;\n --text-muted: #9ca3af;\n }\n\n * {\n box-sizing: border-box;\n }\n\n [data-workbench-vue-root] {\n height: 100%;\n }\n\n .shell {\n height: 100%;\n display: flex;\n flex-direction: column;\n background:\n radial-gradient(rgba(17, 24, 39, 0.06) 1px, transparent 1px),\n linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(249, 250, 251, 0.96));\n background-size: 20px 20px, auto;\n }\n\n .topbar,\n .app-topbar {\n height: 64px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 0 24px;\n background: rgba(255, 255, 255, 0.92);\n border-bottom: 1px solid var(--border-default);\n backdrop-filter: blur(12px);\n position: relative;\n z-index: 20;\n }\n\n .logo-area,\n .app-topbar-main {\n display: flex;\n align-items: center;\n gap: 18px;\n min-width: 0;\n flex: 1;\n }\n\n .app-topbar-nav {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n }\n\n .workspace {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 320px;\n }\n\n .workspace.panel-left {\n grid-template-columns: 320px minmax(0, 1fr);\n }\n\n .workspace.panel-right .canvas-host {\n grid-column: 1;\n }\n\n .workspace.panel-right .panel-sidebar {\n grid-column: 2;\n }\n\n .workspace.panel-left .panel-sidebar {\n grid-column: 1;\n }\n\n .workspace.panel-left .canvas-host {\n grid-column: 2;\n }\n\n .panel-sidebar {\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: #ffffff;\n }\n\n .panel-host {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .canvas-host {\n position: relative;\n min-height: 480px;\n display: grid;\n place-items: center;\n }\n\n .brand-logo {\n height: 32px;\n width: 92px;\n display: block;\n cursor: pointer;\n flex: 0 0 auto;\n }\n\n .app-topbar-auth {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 12px;\n flex: 0 0 auto;\n }\n\n .topbar-credits-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0;\n color: #2d3541;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n cursor: default;\n }\n\n .topbar-credits-value {\n font-variant-numeric: tabular-nums;\n }\n\n .credits-token-icon {\n width: 20px;\n height: 20px;\n flex: 0 0 16px;\n user-select: none;\n -webkit-user-drag: none;\n }\n\n .credits-token-icon--sm {\n width: 16px;\n height: 16px;\n flex-basis: 16px;\n }\n\n .auth-avatar-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .auth-avatar-trigger:hover {\n background: var(--bg-soft);\n }\n\n .auth-avatar {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n overflow: hidden;\n flex: 0 0 auto;\n background: #f3f4f6;\n color: var(--text-secondary);\n font-size: 14px;\n font-weight: 600;\n }\n\n .auth-avatar--lg {\n width: 40px;\n height: 40px;\n font-size: 14px;\n }\n\n .auth-avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n }\n\n .auth-popover-body {\n min-width: 200px;\n padding: 4px 0;\n }\n\n .auth-popover-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n }\n\n .auth-popover-user-text {\n min-width: 0;\n flex: 1;\n }\n\n .auth-popover-name {\n display: block;\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .auth-popover-sub {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .auth-popover-divider {\n height: 1px;\n background: var(--border-default);\n margin: 4px 0;\n }\n\n .auth-popover-action {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: var(--text-secondary);\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .auth-popover-action:hover {\n background: var(--bg-soft);\n color: var(--text-primary);\n }\n\n .auth-hover-card {\n position: relative;\n display: inline-flex;\n align-items: center;\n }\n\n .auth-hover-panel {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 40;\n min-width: 200px;\n background: #fff;\n border: 1px solid var(--border-default);\n border-radius: 12px;\n box-shadow: 0 8px 28px rgba(17, 24, 39, 0.12);\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 150ms ease, transform 150ms ease;\n }\n\n .auth-hover-card:hover .auth-hover-panel,\n .auth-hover-card:focus-within .auth-hover-panel {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n\n .panel-actions,\n #sidebar-footer {\n flex-shrink: 0;\n padding: 14px 24px;\n border-top: 1px solid var(--border-default);\n background: #ffffff;\n display: flex;\n align-items: center;\n }\n\n .sidebar-footer-export {\n width: 100%;\n }\n\n .sidebar-footer-export .sidebar-export-trigger-btn,\n .sidebar-footer-export .sidebar-export-trigger-btn-wrap,\n .sidebar-export-trigger-btn-wrap {\n width: 100%;\n }\n\n .sidebar-export-dropdown-menu {\n width: 268px;\n }\n\n .sidebar-export-trigger-btn img {\n margin-right: 4px;\n }\n\n .export-credits-hint {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 0;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n color: #ff7c23;\n margin-left: 10px;\n white-space: nowrap;\n }\n\n .export-credits-hint-value {\n color: white;\n font-size: 13px;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .export-overlay-menu,\n .fab-menu-content {\n padding: 4px;\n }\n\n .export-overlay-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: 14px;\n color: var(--text-primary);\n cursor: pointer;\n transition: background 120ms ease;\n user-select: none;\n }\n\n .export-overlay-item:hover {\n background: var(--bg-soft);\n }\n\n .export-overlay-item-icon {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n background: #cbd5e1;\n display: inline-block;\n flex: 0 0 auto;\n }\n\n .export-overlay-item-icon--svg {\n background: linear-gradient(180deg, #94a3b8, #64748b);\n }\n\n .export-overlay-item-icon--studio {\n background: linear-gradient(180deg, #cbd5e1, #94a3b8);\n }\n\n .workspace.panel-right .panel-sidebar {\n border-left: 1px solid var(--border-default);\n }\n\n .workspace.panel-left .panel-sidebar {\n border-right: 1px solid var(--border-default);\n }\n\n .template-export-modal {\n display: grid;\n gap: 16px;\n }\n\n \n\n .template-export-groups {\n display: grid;\n gap: 12px;\n max-height: 360px;\n overflow: auto;\n }\n\n .template-export-group {\n display: grid;\n gap: 10px;\n padding: 14px 16px;\n border: 1px solid var(--border-default);\n border-radius: 12px;\n background: #ffffff;\n }\n\n .template-export-group-title {\n font-size: 13px;\n font-weight: 700;\n color: var(--text-primary);\n }\n\n .template-export-field-list {\n display: grid;\n gap: 10px;\n }\n\n .template-export-field {\n display: grid;\n gap: 4px;\n }\n\n .template-export-field-path {\n padding-left: 28px;\n font-size: 12px;\n color: var(--text-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;\n }\n\n .template-export-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 8px 24px;\n }\n',t.appendChild(o)}async function g(t){const e=h();return e||(n=t.atommUiScriptUrl||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.js",c||(c=new Promise(t=>{const e=h();if(e)return void t(e);const a=document.querySelector(`script[data-generator-workbench-atomm-ui="true"][src="${n}"]`);if(a){const e=()=>t(h());return a.addEventListener("load",e,{once:!0}),void a.addEventListener("error",()=>t(void 0),{once:!0})}const o=document.createElement("script");o.src=n,o.async=!0,o.dataset.generatorWorkbenchAtommUi="true",o.addEventListener("load",()=>{t(h())},{once:!0}),o.addEventListener("error",()=>t(void 0),{once:!0}),(document.head||document.body||document.documentElement).appendChild(o)}),c));var n}async function v(t,e){const n=function(){const t=globalThis.Vue;if(!t||"function"!=typeof t.createApp)throw new Error('[generator-workbench] Vue 3 is required. Load it via CDN: <script src="https://unpkg.com/vue@3/dist/vue.global.js"><\/script>');return t}();!function(){const t=globalThis;t.process?t.process.env?t.process.env.NODE_ENV||(t.process.env.NODE_ENV="production"):t.process.env={NODE_ENV:"production"}:t.process={env:{NODE_ENV:"production"}}}(),await m(t,e.atommUiCssUrl);const a=function(t,e){return t.reactive({shellMode:e.mode||"full",logoText:e.logoText||e.title,logoSrc:e.logoUrl||"https://storage-us.atomm.com/resource/xart/static/agent/imgs/atomm-logo.svg",panelTarget:e.panelTarget||"right",avatarMenuTrigger:e.avatarMenuTrigger||"hover",templateEnabled:!1!==e.templateEnabled,exportEnabled:!1!==e.exportEnabled,studioEnabled:!1!==e.studioEnabled,isLogin:!1,avatarSrc:"",avatarText:"U",authDisplayName:"",authSubline:"",creditsBalance:0,exportCreditsCost:1,loginLoading:!1,templateDialogOpen:!1,templateExportLoading:!1,templateSelectedFieldPaths:[],templateFieldGroups:[]})}(n,e),o={onLogin:()=>{},onLogout:()=>{},onImportTemplate:()=>{},onExportTemplate:()=>{},onCloseTemplateDialog:()=>{},onToggleTemplateDialog:()=>{},onToggleTemplateField:()=>{},onConfirmTemplateExport:()=>{},onExportSvg:()=>{},onOpenInStudio:()=>{},onFileChange:()=>{}},r=document.createElement("div");r.setAttribute("data-workbench-vue-root",""),t.appendChild(r);const i=await g(e),l=n.createApp({setup:()=>({state:a,callbacks:o}),template:d});i?l.use(i):function(t){t.component("xt-button",{inheritAttrs:!0,template:'<button v-bind="$attrs"><slot name="icon" /><slot /><slot name="append-icon" /></button>'}),t.component("xt-avatar",{inheritAttrs:!0,template:'<span v-bind="$attrs"><slot /></span>'}),t.component("xt-dropdown-menu",{inheritAttrs:!0,template:'<div v-bind="$attrs"><slot name="trigger" /><slot name="overlay" /><slot /></div>'}),t.component("xt-hover-card",{inheritAttrs:!0,template:'<div v-bind="$attrs"><slot name="trigger" /><slot name="content" /></div>'}),t.component("xt-modal",{inheritAttrs:!1,props:{modelValue:{type:Boolean,default:!1},title:{type:String,default:""}},emits:["update:modelValue"],template:'\n <div v-if="modelValue" v-bind="$attrs">\n <div>{{ title }}</div>\n <slot />\n </div>\n '}),t.component("xt-checkbox",{inheritAttrs:!1,props:{modelValue:{type:Boolean,default:!1},label:{type:String,default:""},disabled:{type:Boolean,default:!1}},emits:["update:modelValue","change"],template:'\n <label v-bind="$attrs">\n <input\n type="checkbox"\n :checked="modelValue"\n :disabled="disabled"\n @change="$emit(\'update:modelValue\', $event.target.checked)"\n />\n <span><slot>{{ label }}</slot></span>\n </label>\n '})}(l),l.mount(r);const p=function(t){return{root:t,workspace:s(t,'[data-role="workspace"]'),logoArea:t.querySelector('[data-role="logo-area"]'),canvasHost:s(t,'[data-role="canvas-host"]'),panelHost:s(t,'[data-role="panel-host"]'),templateFileInput:s(t,'[data-role="template-file-input"]')}}(t);return{state:a,callbacks:o,app:l,refs:p}}function f(t){t.app.unmount()}const x={title:"",mode:"full",templateEnabled:!0,exportEnabled:!0,studioEnabled:!0,autoMount:!0,panelTarget:"right",avatarMenuTrigger:"hover"};class b extends HTMLElement{constructor(){super(),n(this,"_sdk",null),n(this,"_runtime",null),n(this,"_config",{...x}),n(this,"_mounted",!1),n(this,"_state",{authStatus:{isLogin:!1,userInfo:null},creditsBalance:0,billingUsage:null,busy:{login:!1,importTemplate:!1,exportTemplate:!1,exportSvg:!1,openInStudio:!1},menu:{avatarOpen:!1,fabOpen:!1}}),n(this,"_cleanupAuth"),n(this,"_cleanupCredits"),n(this,"_cleanupBilling"),n(this,"_authController"),n(this,"_templateController"),n(this,"_exportController"),n(this,"_runtimeController"),n(this,"_shellContext"),this.attachShadow({mode:"open"})}get sdk(){return this._sdk}set sdk(t){this._sdk=t}get runtime(){return this._runtime}set runtime(t){this._runtime=t}get config(){return this._config}set config(t){var e;this._config=(e=t,{...x,...e}),this._mounted&&this.render()}connectedCallback(){!1!==this._config.autoMount&&!this._mounted&&this._sdk&&this._runtime&&this.mount()}disconnectedCallback(){this.unmount()}async mount(){var t,e;if(this._mounted)return;if(!this.shadowRoot)throw new Error("[generator-workbench] shadowRoot is required");if(!this._sdk)throw new Error("[generator-workbench] sdk is required before mount()");if(!this._runtime)throw new Error("[generator-workbench] runtime is required before mount()");await this.render(),this.bindControllers(),this.bindShellCallbacks(),this.bindAuthState(),await this.syncBillingState();const n=this.requireRefs();await(null==(t=this._runtimeController)?void 0:t.mountCanvas(n.canvasHost)),await(null==(e=this._runtimeController)?void 0:e.mountPanel(n.panelHost,this._state.activePanelFilter)),this._mounted=!0,l(this,"workbench-ready",{sdk:this._sdk,runtime:this._runtime})}async unmount(){var t,e,n,a;null==(t=this._cleanupAuth)||t.call(this),this._cleanupAuth=void 0,null==(e=this._cleanupCredits)||e.call(this),this._cleanupCredits=void 0,null==(n=this._cleanupBilling)||n.call(this),this._cleanupBilling=void 0,await(null==(a=this._runtimeController)?void 0:a.unmountAll()),this._shellContext&&(f(this._shellContext),this._shellContext=void 0),this._mounted=!1}refreshLayout(){}async importTemplate(t){try{const e=await this.requireTemplateController().importTemplate(t);this._state.activePanelFilter=e.panelFilter;const n=this.requireRefs();this._runtimeController&&await this._runtimeController.remountPanel(n.panelHost,e.panelFilter),l(this,"template-imported",e)}catch(e){this.handleError("template",e)}}async exportTemplate(){return this.exportTemplateWithFields()}async exportTemplateWithFields(t){try{this._state.busy.exportTemplate=!0,this._shellContext&&(this._shellContext.state.templateExportLoading=!0);l(this,"template-exported",await this.requireTemplateController().exportTemplate(t)),t&&this.closeTemplateExportDialog()}catch(e){this.handleError("template",e)}finally{this._state.busy.exportTemplate=!1,this._shellContext&&(this._shellContext.state.templateExportLoading=!1)}}async exportSvg(){try{l(this,"svg-export",await this.requireExportController().exportSvg())}catch(t){this.handleError("export",t)}}async openInStudio(){try{l(this,"studio-open",await this.requireExportController().openInStudio())}catch(t){this.handleError("export",t)}}async setAuthToken(t){var e,n;const a=t.trim();if(!a)throw new Error("[generator-workbench] token is required");if(!this._sdk)throw new Error("[generator-workbench] sdk is required before setAuthToken()");const o=null==(n=(e=this._sdk).getAppKey)?void 0:n.call(e);if(!o)throw new Error("[generator-workbench] sdk.getAppKey() is required for setAuthToken()");if("function"!=typeof this._sdk.auth.syncToken)throw new Error("[generator-workbench] sdk.auth.syncToken() is required for setAuthToken()");try{localStorage.setItem(`__atomm_sdk_token__${o}`,a)}catch{}await this._sdk.auth.syncToken(a)}async render(){this.shadowRoot&&(this._shellContext&&(f(this._shellContext),this.shadowRoot.innerHTML=""),this._shellContext=await v(this.shadowRoot,this._config),this.syncAuthUI(),this.syncBillingUI())}bindControllers(){this._sdk&&this._runtime&&(this._authController=function(t){const{sdk:e}=t;return{getStatus:()=>e.auth.getStatus(),subscribe:t=>(t(e.auth.getStatus()),e.auth.onChange(t)),async login(){await e.auth.login()},async logout(){await e.auth.logout()}}}({sdk:this._sdk}),this._templateController=i({sdk:this._sdk,runtime:this._runtime,config:this._config}),this._exportController=function(t){const{sdk:e,runtime:n,config:a,element:o}=t;async function r(){if(!e.auth.getStatus().isLogin&&(await e.auth.login(),!e.auth.getStatus().isLogin))throw new Error("[generator-workbench] login is required before export")}async function i(){var t;(null==(t=e.billing)?void 0:t.consume)&&await e.billing.consume()}return{async exportSvg(){var t;return await(null==(t=a.beforeExportSvg)?void 0:t.call(a,{sdk:e,runtime:n,element:o})),await r(),await i(),e.export.download({format:"svg"})},async openInStudio(){var t;return await(null==(t=a.beforeOpenInStudio)?void 0:t.call(a,{sdk:e,runtime:n,element:o})),await r(),await i(),e.export.openInStudio({format:"svg"})}}}({sdk:this._sdk,runtime:this._runtime,config:this._config,element:this}),this._runtimeController=function(t){const{runtime:e}=t;let n=null,a=null;async function o(t,n){null==a||a.unmount(),a=await Promise.resolve(e.mount({mode:"embed",target:"panel",container:t,panelFilter:n}))}return{mountCanvas:async function(t){null==n||n.unmount(),n=await Promise.resolve(e.mount({mode:"embed",target:"canvas",container:t}))},mountPanel:o,async remountPanel(t,e){await o(t,e)},async unmountAll(){null==n||n.unmount(),null==a||a.unmount(),n=null,a=null}}}({runtime:this._runtime}))}bindShellCallbacks(){if(!this._shellContext)return;const{callbacks:t}=this._shellContext;t.onLogin=()=>{this.handleLogin()},t.onLogout=()=>{this.handleLogout()},t.onImportTemplate=()=>{this.requireRefs().templateFileInput.click()},t.onExportTemplate=()=>{this.openTemplateExportDialog()},t.onCloseTemplateDialog=()=>{this.closeTemplateExportDialog()},t.onToggleTemplateDialog=t=>{t||this.closeTemplateExportDialog()},t.onToggleTemplateField=(t,e)=>{this.toggleTemplateExportField(t,e)},t.onConfirmTemplateExport=()=>{var t;this.exportTemplateWithFields((null==(t=this._shellContext)?void 0:t.state.templateSelectedFieldPaths)||[])},t.onExportSvg=()=>{this.exportSvg()},t.onOpenInStudio=()=>{this.openInStudio()},t.onFileChange=t=>{var e;const n=t.target,a=null==(e=n.files)?void 0:e[0];a&&(this.importTemplate(a),n.value="")}}openTemplateExportDialog(){try{if(!this._shellContext)return;const t=this.requireTemplateController().prepareTemplateExport();this._shellContext.state.templateFieldGroups=t.fieldGroups.map(t=>({...t,fields:t.fields.map(t=>({...t}))})),this._shellContext.state.templateSelectedFieldPaths=[...t.selectedFieldPaths],this._shellContext.state.templateDialogOpen=!0}catch(t){this.handleError("template",t)}}closeTemplateExportDialog(){this._shellContext&&(this._shellContext.state.templateDialogOpen=!1,this._shellContext.state.templateFieldGroups=[],this._shellContext.state.templateSelectedFieldPaths=[])}toggleTemplateExportField(t,e){if(!this._shellContext)return;const n=new Set(this._shellContext.state.templateSelectedFieldPaths);e?n.add(t):n.delete(t),this._shellContext.state.templateSelectedFieldPaths=Array.from(n)}bindAuthState(){var t;const e=this.requireAuthController();null==(t=this._cleanupAuth)||t.call(this),this._cleanupAuth=e.subscribe(t=>{this._state.authStatus=t,this.syncAuthUI(),this.syncBillingState(t),l(this,"auth-change",t)}),this.bindBillingSubscriptions()}bindBillingSubscriptions(){var t,e,n,a;null==(t=this._cleanupCredits)||t.call(this),this._cleanupCredits=void 0,null==(e=this._cleanupBilling)||e.call(this),this._cleanupBilling=void 0;const o=null==(n=this._sdk)?void 0:n.credits;"function"==typeof(null==o?void 0:o.onChange)&&(this._cleanupCredits=o.onChange(t=>{this._state.creditsBalance=Number(t)||0,this.syncBillingUI()}));const r=null==(a=this._sdk)?void 0:a.billing;"function"==typeof(null==r?void 0:r.onChange)&&(this._cleanupBilling=r.onChange(t=>{this.applyBillingUsage(t)}))}syncAuthUI(){var t,e,n,a;if(!this._shellContext)return;const{state:o}=this._shellContext,{isLogin:r,userInfo:i}=this._state.authStatus,l=(null==(t=null==i?void 0:i.userName)?void 0:t.trim())||(null==(e=null==i?void 0:i.email)?void 0:e.trim())||(null==(n=null==i?void 0:i.phoneNumber)?void 0:n.trim())||"",s=l?l.charAt(0).toUpperCase():"U",p=(null==(a=null==i?void 0:i.email)?void 0:a.trim())||((null==i?void 0:i.phoneNumber)?`${i.phoneZone||""}${i.phoneNumber}`:"Connected to Generator SDK");o.isLogin=r,o.avatarSrc=r&&(null==i?void 0:i.headpic)||"",o.avatarText=s,o.authDisplayName=l,o.authSubline=r?p:""}syncBillingUI(){if(!this._shellContext)return;const{state:t}=this._shellContext,e=this._state.billingUsage;t.creditsBalance=this._state.creditsBalance,t.exportCreditsCost=(null==e?void 0:e.creditsPerUse)??(e&&"unitPrice"in e&&Number(e.unitPrice)||1)}applyBillingUsage(t){this._state.billingUsage=t||null,t&&"number"==typeof t.creditsBalance&&(this._state.creditsBalance=t.creditsBalance),this.syncBillingUI()}resetBillingState(){this._state.creditsBalance=0,this._state.billingUsage=null,this.syncBillingUI()}async syncBillingState(t=this._state.authStatus){var e,n,a,o,r,i,l,s;if(!t.isLogin||!this._sdk)return void this.resetBillingState();const p=null==(n=null==(e=this._sdk.credits)?void 0:e.getCachedBalance)?void 0:n.call(e);"number"==typeof p&&(this._state.creditsBalance=p);const d=null==(o=null==(a=this._sdk.billing)?void 0:a.getCachedUsage)?void 0:o.call(a);d?this.applyBillingUsage(d):this.syncBillingUI();try{const[t,e]=await Promise.all([null==(i=null==(r=this._sdk.credits)?void 0:r.getBalance)?void 0:i.call(r),null==(s=null==(l=this._sdk.billing)?void 0:l.getUsage)?void 0:s.call(l)]);if("number"==typeof(null==t?void 0:t.quota)&&(this._state.creditsBalance=t.quota),e)return void this.applyBillingUsage(e);this.syncBillingUI()}catch(c){this.handleError("auth",c)}}async handleLogin(){try{this._state.busy.login=!0,this._shellContext&&(this._shellContext.state.loginLoading=!0),await this.requireAuthController().login()}catch(t){this.handleError("auth",t)}finally{this._state.busy.login=!1,this._shellContext&&(this._shellContext.state.loginLoading=!1)}}async handleLogout(){try{await this.requireAuthController().logout()}catch(t){this.handleError("auth",t)}}handleError(t,e){var n,a;const o=e instanceof Error?e:new Error(String(e));null==(a=(n=this._config).onError)||a.call(n,o,t),l(this,"workbench-error",{source:t,error:o})}requireRefs(){if(!this._shellContext)throw new Error("[generator-workbench] shell context is not ready");return this._shellContext.refs}requireAuthController(){if(!this._authController)throw new Error("[generator-workbench] auth controller is not ready");return this._authController}requireTemplateController(){if(!this._templateController)throw new Error("[generator-workbench] template controller is not ready");return this._templateController}requireExportController(){if(!this._exportController)throw new Error("[generator-workbench] export controller is not ready");return this._exportController}}const y="generator-workbench";t.GENERATOR_WORKBENCH_TAG_NAME=y,t.GeneratorWorkbenchElement=b,t.defineGeneratorWorkbench=function(t=y){const e=customElements.get(t);return e||(customElements.define(t,b),b)},Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).GeneratorWorkbench={})}(this,function(e){"use strict";var t=Object.defineProperty,n=(e,n,a)=>((e,n,a)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[n]=a)(e,"symbol"!=typeof n?n+"":n,a);function a(e,t){if(!e)throw new Error(t)}function o(e,t){const n=(null==t?void 0:t.length)?new Set(t):null;return e.groups.map(e=>{var t;const a=e.fields.map(e=>{var t,a,o;const r=null==(a=null==(t=e.bind)?void 0:t.path)?void 0:a.trim();return r?n&&!n.has(r)?null:{id:e.id,label:(null==(o=e.label)?void 0:o.trim())||e.id||r,path:r}:null}).filter(e=>Boolean(e));return{id:e.id,title:(null==(t=e.title)?void 0:t.trim())||e.id,fields:a}}).filter(e=>e.fields.length>0)}function r(e,t){var n;if(null==(n=e.generatorId)?void 0:n.trim())return e.generatorId.trim();const a=t.meta;if(a&&"object"==typeof a&&"generatorId"in a&&"string"==typeof a.generatorId&&a.generatorId.trim())return a.generatorId.trim();throw new Error("[generator-workbench] generatorId is required in panelSchema.generatorId or state.meta.generatorId")}function i(e){const{sdk:t,runtime:n,config:i}=e;function l(){var e;const t=n.getState(),l=n.getPanelSchema(),s=o(l,null==(e=i.getTemplateFieldPaths)?void 0:e.call(i,l)),p=s.flatMap(e=>e.fields.map(e=>e.path));return a(p.length>0,"[generator-workbench] exportTemplate requires at least one panel field path"),{generatorId:r(l,t),state:t,panelSchema:l,fieldGroups:s,selectedFieldPaths:p}}return{async importTemplate(e){const a=await e.text(),o=t.template.parse(a);let r;return await t.template.applyToRuntime(n,o,{onPanelFilter(e){r=e}}),{template:o,panelFilter:r}},prepareTemplateExport:l,async exportTemplate(e){var n;const r=l(),s=(null==e?void 0:e.length)?o(r.panelSchema,e).flatMap(e=>e.fields.map(e=>e.path)):r.selectedFieldPaths;a(s.length>0,"[generator-workbench] exportTemplate requires at least one panel field path");const p=t.template.build({generatorId:r.generatorId,state:r.state,panelSchema:r.panelSchema,selectedFieldPaths:s,templateMeta:null==(n=i.getTemplateMeta)?void 0:n.call(i)});return t.template.download(p),{template:p}}}}function l(e,t,n){return e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0}))}function s(e,t){const n=e.querySelector(t);if(!n)throw new Error(`[generator-workbench] required DOM node not found: ${t}`);return n}const p="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",d="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",c=`\n <div class="shell">\n <header v-if="state.shellMode !== 'template'" class="topbar app-topbar">\n <div class="logo-area app-topbar-main" data-role="logo-area">\n <img\n :src="state.logoSrc"\n :alt="state.logoText || 'Atomm'"\n class="brand-logo"\n data-role="brand-logo"\n draggable="false"\n />\n <div class="app-topbar-nav">\n <xt-button\n v-show="state.templateEnabled"\n type="secondary"\n size="small"\n data-role="import-template"\n @click="callbacks.onImportTemplate()"\n >导入模板</xt-button>\n <xt-button\n v-show="state.templateEnabled"\n type="secondary"\n size="small"\n data-role="export-template"\n @click="callbacks.onExportTemplate()"\n >生成模板</xt-button>\n </div>\n </div>\n <div class="app-topbar-auth">\n <div\n v-if="state.isLogin"\n class="topbar-credits-badge"\n data-role="topbar-credits"\n title="Remaining credits"\n >\n <img src="${p}" alt="" class="credits-token-icon" draggable="false" />\n <span class="topbar-credits-value" data-role="topbar-credits-value">{{ state.creditsBalance }}</span>\n </div>\n\n <div\n v-if="state.isLogin && state.avatarMenuTrigger === 'hover'"\n class="auth-hover-card"\n >\n <div class="auth-avatar-trigger" data-role="avatar-button" tabindex="0">\n <div class="auth-avatar">\n <img\n v-if="state.avatarSrc"\n :src="state.avatarSrc"\n alt="Avatar"\n data-role="avatar-image"\n />\n <span v-else data-role="avatar-image">{{ state.avatarText }}</span>\n </div>\n </div>\n <div class="auth-hover-panel">\n <div class="auth-popover-body" data-role="avatar-menu">\n <div class="auth-popover-header">\n <div class="auth-avatar auth-avatar--lg">\n <img v-if="state.avatarSrc" :src="state.avatarSrc" alt="Avatar" />\n <span v-else>{{ state.avatarText }}</span>\n </div>\n <div class="auth-popover-user-text">\n <span class="auth-popover-name">{{ state.authDisplayName }}</span>\n <span class="auth-popover-sub">{{ state.authSubline }}</span>\n </div>\n </div>\n <div class="auth-popover-divider"></div>\n <div class="auth-popover-action" data-role="logout" @click="callbacks.onLogout()">\n Logout\n </div>\n </div>\n </div>\n </div>\n\n <xt-dropdown-menu\n v-else-if="state.isLogin"\n trigger="click"\n :portal-disabled="true"\n placement="bottomRight"\n >\n <template #trigger>\n <div class="auth-avatar-trigger" data-role="avatar-button">\n <div class="auth-avatar">\n <img\n v-if="state.avatarSrc"\n :src="state.avatarSrc"\n alt="Avatar"\n data-role="avatar-image"\n />\n <span v-else data-role="avatar-image">{{ state.avatarText }}</span>\n </div>\n </div>\n </template>\n <template #overlay>\n <div class="auth-popover-body" data-role="avatar-menu">\n <div class="auth-popover-header">\n <div class="auth-avatar auth-avatar--lg">\n <img v-if="state.avatarSrc" :src="state.avatarSrc" alt="Avatar" />\n <span v-else>{{ state.avatarText }}</span>\n </div>\n <div class="auth-popover-user-text">\n <span class="auth-popover-name">{{ state.authDisplayName }}</span>\n <span class="auth-popover-sub">{{ state.authSubline }}</span>\n </div>\n </div>\n <div class="auth-popover-divider"></div>\n <div class="auth-popover-action" data-role="logout" @click="callbacks.onLogout()">\n Logout\n </div>\n </div>\n </template>\n </xt-dropdown-menu>\n\n <xt-button\n size="small"\n type="secondary"\n data-role="login"\n :loading="state.loginLoading"\n v-if="!state.isLogin"\n @click="callbacks.onLogin()"\n >Login</xt-button>\n </div>\n </header>\n\n <div\n class="workspace"\n :class="[\n state.shellMode === 'shell'\n ? 'shell-mode-shell'\n : (state.panelTarget === 'left' ? 'panel-left' : 'panel-right'),\n ]"\n data-role="workspace"\n >\n <main\n v-if="state.shellMode === 'shell'"\n class="workspace-host"\n data-role="workspace-host"\n ></main>\n <template v-else>\n <main class="canvas-host" data-role="canvas-host"></main>\n <aside class="panel-sidebar" data-role="panel-sidebar">\n <div class="panel-host" data-role="panel-host"></div>\n <div id="sidebar-footer" class="sidebar-footer-export panel-actions" data-role="panel-actions">\n <xt-dropdown-menu\n trigger="click"\n :portal-disabled="true"\n placement="topLeft"\n domTriggerClass="sidebar-export-trigger-btn-wrap"\n :show-trigger-icon="false"\n domContentClass="sidebar-export-dropdown-menu"\n >\n <template #trigger>\n <xt-button class="sidebar-export-trigger-btn" block data-role="fab-trigger">\n <template #icon>\n <img\n src="${d}"\n alt=""\n width="16"\n height="16"\n draggable="false"\n />\n </template>\n Export SVG\n <template #append-icon>\n <div v-if="state.isLogin" class="export-credits-hint">\n <img src="${p}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />\n <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>\n </div>\n </template>\n </xt-button>\n </template>\n <template #overlay>\n <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">\n <div\n v-show="state.exportEnabled"\n class="export-overlay-item"\n data-role="export-svg"\n @click="callbacks.onExportSvg()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>\n Download\n </div>\n <div\n v-show="state.studioEnabled"\n class="export-overlay-item"\n data-role="open-in-studio"\n @click="callbacks.onOpenInStudio()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>\n Open in Studio\n </div>\n </div>\n </template>\n </xt-dropdown-menu>\n </div>\n </aside>\n </template>\n </div>\n\n <div\n v-if="state.shellMode === 'shell'"\n id="sidebar-footer"\n class="sidebar-footer-export panel-actions sidebar-footer-floating"\n data-role="panel-actions"\n >\n <xt-dropdown-menu\n trigger="click"\n :portal-disabled="true"\n placement="topLeft"\n domTriggerClass="sidebar-export-trigger-btn-wrap"\n :show-trigger-icon="false"\n domContentClass="sidebar-export-dropdown-menu"\n >\n <template #trigger>\n <xt-button class="sidebar-export-trigger-btn" data-role="fab-trigger">\n <template #icon>\n <img\n src="${d}"\n alt=""\n width="16"\n height="16"\n draggable="false"\n />\n </template>\n Export SVG\n <template #append-icon>\n <div v-if="state.isLogin" class="export-credits-hint">\n <img src="${p}" alt="" class="credits-token-icon credits-token-icon--sm" draggable="false" />\n <span class="export-credits-hint-value" data-role="export-credits-value">{{ state.exportCreditsCost }}</span>\n </div>\n </template>\n </xt-button>\n </template>\n <template #overlay>\n <div class="export-overlay-menu fab-menu-content" data-role="fab-menu">\n <div\n v-show="state.exportEnabled"\n class="export-overlay-item"\n data-role="export-svg"\n @click="callbacks.onExportSvg()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--svg"></span>\n Download\n </div>\n <div\n v-show="state.studioEnabled"\n class="export-overlay-item"\n data-role="open-in-studio"\n @click="callbacks.onOpenInStudio()"\n >\n <span class="export-overlay-item-icon export-overlay-item-icon--studio"></span>\n Open in Studio\n </div>\n </div>\n </template>\n </xt-dropdown-menu>\n </div>\n\n <input\n data-role="template-file-input"\n type="file"\n accept="application/json"\n style="display:none"\n @change="callbacks.onFileChange($event)"\n />\n\n <xt-modal\n :model-value="state.templateDialogOpen"\n title="发布模板"\n :show-footer="false"\n :portal-disabled="true"\n @update:model-value="callbacks.onToggleTemplateDialog($event)"\n >\n <div\n v-if="state.templateDialogOpen"\n class="template-export-modal"\n data-role="template-export-modal"\n >\n \n <div class="template-export-groups">\n <div\n v-for="group in state.templateFieldGroups"\n :key="group.id"\n class="template-export-group"\n >\n <div class="template-export-group-title">{{ group.title }}</div>\n <div class="template-export-field-list">\n <div\n v-for="field in group.fields"\n :key="field.path"\n class="template-export-field"\n data-role="template-export-field"\n >\n <xt-checkbox\n :model-value="state.templateSelectedFieldPaths.includes(field.path)"\n :disabled="state.templateExportLoading"\n @update:model-value="callbacks.onToggleTemplateField(field.path, $event)"\n >{{ field.label }}</xt-checkbox>\n <div class="template-export-field-path">{{ field.path }}</div>\n </div>\n </div>\n </div>\n </div>\n\n <div class="template-export-footer">\n <xt-button\n type="secondary"\n :disabled="state.templateExportLoading"\n @click="callbacks.onCloseTemplateDialog()"\n >取消</xt-button>\n <xt-button\n type="primary"\n data-role="template-export-confirm"\n :loading="state.templateExportLoading"\n :disabled="state.templateSelectedFieldPaths.length === 0"\n @click="callbacks.onConfirmTemplateExport()"\n >发布模板</xt-button>\n </div>\n </div>\n </xt-modal>\n </div>\n`,h=new Map;let u=null;const m=new Map;function g(){return globalThis.Vue}function v(e){return new Error(`[generator-workbench] failed to auto-load Vue 3 runtime from ${e}`)}async function f(e){const t=g();return t&&"function"==typeof t.createApp?t:function(e){const t=h.get(e);if(t)return t;const n=new Promise((t,n)=>{const a=g();if(a&&"function"==typeof a.createApp)return void t(a);const o=()=>{const a=g();a&&"function"==typeof a.createApp?t(a):(h.delete(e),n(v(e)))},r=()=>{h.delete(e),n(v(e))},i=Array.from(document.querySelectorAll("script")).find(t=>t.src===e);if(i)return i.addEventListener("load",o,{once:!0}),void i.addEventListener("error",r,{once:!0});const l=document.createElement("script");l.src=e,l.async=!0,l.dataset.generatorWorkbenchVue="true",l.addEventListener("load",o,{once:!0}),l.addEventListener("error",r,{once:!0}),(document.head||document.body||document.documentElement).appendChild(l)});return h.set(e,n),n}(e.vueScriptUrl||"https://unpkg.com/vue@3/dist/vue.global.prod.js")}function x(){return globalThis.AtommUI}async function b(e,t){const n=t||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.min.css",a=await function(e){const t=m.get(e);if(t)return t;const n=fetch(e).then(async t=>t.ok?(await t.text()).replace(/(^|})\s*:root(?=\s*{)/g,"$1:host"):(m.delete(e),null)).catch(()=>(m.delete(e),null));return m.set(e,n),n}(n);if(a){const t=document.createElement("style");t.dataset.generatorWorkbenchAtommUi="true",t.textContent=a,e.appendChild(t)}else{const t=document.createElement("link");t.rel="stylesheet",t.href=n,e.appendChild(t)}const o=document.createElement("style");o.textContent="\n :host {\n display: block;\n height: 100%;\n color: #111827;\n font-family: Inter, \"Montserrat\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --bg-soft: #f3f4f6;\n --border-default: #e5e7eb;\n --text-primary: #111827;\n --text-secondary: #4b5563;\n --text-muted: #9ca3af;\n }\n\n * {\n box-sizing: border-box;\n }\n\n [data-workbench-vue-root] {\n height: 100%;\n }\n\n .shell {\n height: 100%;\n display: flex;\n flex-direction: column;\n background:\n radial-gradient(rgba(17, 24, 39, 0.06) 1px, transparent 1px),\n linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(249, 250, 251, 0.96));\n background-size: 20px 20px, auto;\n }\n\n .topbar,\n .app-topbar {\n height: 64px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 0 24px;\n background: rgba(255, 255, 255, 0.92);\n border-bottom: 1px solid var(--border-default);\n backdrop-filter: blur(12px);\n position: relative;\n z-index: 20;\n }\n\n .logo-area,\n .app-topbar-main {\n display: flex;\n align-items: center;\n gap: 18px;\n min-width: 0;\n flex: 1;\n }\n\n .app-topbar-nav {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n }\n\n .workspace {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 320px;\n }\n\n .workspace.shell-mode-shell {\n display: block;\n position: relative;\n }\n\n .workspace-host {\n height: 100%;\n min-height: 0;\n position: relative;\n }\n\n .workspace.panel-left {\n grid-template-columns: 320px minmax(0, 1fr);\n }\n\n .workspace.panel-right .canvas-host {\n grid-column: 1;\n }\n\n .workspace.panel-right .panel-sidebar {\n grid-column: 2;\n }\n\n .workspace.panel-left .panel-sidebar {\n grid-column: 1;\n }\n\n .workspace.panel-left .canvas-host {\n grid-column: 2;\n }\n\n .panel-sidebar {\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: #ffffff;\n }\n\n .panel-host {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .canvas-host {\n position: relative;\n min-height: 480px;\n display: grid;\n place-items: center;\n }\n\n .brand-logo {\n height: 32px;\n width: 92px;\n display: block;\n cursor: pointer;\n flex: 0 0 auto;\n }\n\n .app-topbar-auth {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 12px;\n flex: 0 0 auto;\n }\n\n .topbar-credits-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0;\n color: #2d3541;\n font-size: 14px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n cursor: default;\n }\n\n .topbar-credits-value {\n font-variant-numeric: tabular-nums;\n }\n\n .credits-token-icon {\n width: 20px;\n height: 20px;\n flex: 0 0 16px;\n user-select: none;\n -webkit-user-drag: none;\n }\n\n .credits-token-icon--sm {\n width: 16px;\n height: 16px;\n flex-basis: 16px;\n }\n\n .auth-avatar-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .auth-avatar-trigger:hover {\n background: var(--bg-soft);\n }\n\n .auth-avatar {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n overflow: hidden;\n flex: 0 0 auto;\n background: #f3f4f6;\n color: var(--text-secondary);\n font-size: 14px;\n font-weight: 600;\n }\n\n .auth-avatar--lg {\n width: 40px;\n height: 40px;\n font-size: 14px;\n }\n\n .auth-avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n }\n\n .auth-popover-body {\n min-width: 200px;\n padding: 4px 0;\n }\n\n .auth-popover-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n }\n\n .auth-popover-user-text {\n min-width: 0;\n flex: 1;\n }\n\n .auth-popover-name {\n display: block;\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .auth-popover-sub {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .auth-popover-divider {\n height: 1px;\n background: var(--border-default);\n margin: 4px 0;\n }\n\n .auth-popover-action {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: var(--text-secondary);\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .auth-popover-action:hover {\n background: var(--bg-soft);\n color: var(--text-primary);\n }\n\n .auth-hover-card {\n position: relative;\n display: inline-flex;\n align-items: center;\n }\n\n .auth-hover-panel {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 40;\n min-width: 200px;\n background: #fff;\n border: 1px solid var(--border-default);\n border-radius: 12px;\n box-shadow: 0 8px 28px rgba(17, 24, 39, 0.12);\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 150ms ease, transform 150ms ease;\n }\n\n .auth-hover-card:hover .auth-hover-panel,\n .auth-hover-card:focus-within .auth-hover-panel {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n\n .panel-actions,\n #sidebar-footer {\n flex-shrink: 0;\n width: 262px;\n display: flex;\n align-items: center;\n }\n\n .panel-sidebar #sidebar-footer {\n padding: 14px 24px;\n flex-shrink: 0;\n width: auto;\n display: flex;\n align-items: center;\n \n }\n\n .sidebar-footer-floating {\n position: fixed;\n right: 24px;\n bottom: 24px;\n z-index: 30;\n width: auto;\n padding: 0;\n border-top: 0;\n background: transparent;\n box-shadow: none;\n }\n\n .sidebar-footer-floating .sidebar-export-trigger-btn,\n .sidebar-footer-floating .sidebar-export-trigger-btn-wrap,\n .sidebar-footer-floating .sidebar-export-trigger-btn-wrap {\n width: auto;\n }\n\n .sidebar-footer-floating .sidebar-export-trigger-btn {\n min-width: 180px;\n box-shadow: 0 12px 28px rgba(17, 24, 39, 0.18);\n }\n\n .sidebar-footer-export {\n width: 100%;\n }\n\n .sidebar-footer-export .sidebar-export-trigger-btn,\n .sidebar-footer-export .sidebar-export-trigger-btn-wrap,\n .sidebar-export-trigger-btn-wrap {\n width: 100%;\n }\n\n .sidebar-export-dropdown-menu {\n width: 268px;\n }\n\n .sidebar-export-trigger-btn img {\n margin-right: 4px;\n }\n\n .export-credits-hint {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 0;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n color: #ff7c23;\n margin-left: 10px;\n white-space: nowrap;\n }\n\n .export-credits-hint-value {\n color: white;\n font-size: 13px;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .export-overlay-menu,\n .fab-menu-content {\n padding: 4px;\n }\n\n .export-overlay-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: 14px;\n color: var(--text-primary);\n cursor: pointer;\n transition: background 120ms ease;\n user-select: none;\n }\n\n .export-overlay-item:hover {\n background: var(--bg-soft);\n }\n\n .export-overlay-item-icon {\n width: 16px;\n height: 16px;\n display: inline-block;\n flex: 0 0 auto;\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n }\n\n .export-overlay-item-icon--svg {\n background-image: url(\"data:image/svg+xml,%3c?xml%20version='1.0'%20standalone='no'?%3e%3c!DOCTYPE%20svg%20PUBLIC%20'-//W3C//DTD%20SVG%201.1//EN'%20'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3e%3csvg%20t='1775118703519'%20class='icon'%20viewBox='0%200%201024%201024'%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20p-id='12828'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20width='200'%20height='200'%3e%3cpath%20d='M469.333333%20596.053333V128h85.333334v468.053333l208.64-160.554666%2052.053333%2067.669333-277.333333%20213.333333a42.666667%2042.666667%200%200%201-52.053334%200l-277.333333-213.333333%2052.053333-67.669333L469.333333%20596.053333zM896%20896H128v-85.333333h768v85.333333z'%20p-id='12829'%3e%3c/path%3e%3c/svg%3e\");\n }\n\n .export-overlay-item-icon--studio {\n background-image: url(\"data:image/svg+xml,%3c?xml%20version='1.0'%20standalone='no'?%3e%3c!DOCTYPE%20svg%20PUBLIC%20'-//W3C//DTD%20SVG%201.1//EN'%20'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3e%3csvg%20t='1775118714051'%20class='icon'%20viewBox='0%200%201024%201024'%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20p-id='12988'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20width='200'%20height='200'%3e%3cpath%20d='M197.553231%20309.169231h128.945231l85.858461%2085.858461-64.512%2064.827077-150.291692-150.685538z%20m472.694154%2021.267692L509.006769%20492.307692l182.429539%20183.059693H562.491077L444.494769%20557.056%20326.498462%20675.288615H197.553231l364.937846-366.119384h128.945231l-21.188923%2021.267692z'%20p-id='12989'%3e%3c/path%3e%3cpath%20d='M157.538462%200a157.538462%20157.538462%200%200%200-157.538462%20157.538462v682.692923a157.538462%20157.538462%200%200%200%20157.538462%20157.538461h236.307692v-105.078154H157.538462a52.539077%2052.539077%200%200%201-52.539077-52.460307V157.538462c0-28.987077%2023.552-52.539077%2052.539077-52.539077h577.614769c29.065846%200%2052.539077%2023.552%2052.539077%2052.539077v393.846153h104.999384V157.538462a157.538462%20157.538462%200%200%200-157.538461-157.538462H157.538462z'%20p-id='12990'%3e%3c/path%3e%3cpath%20d='M789.267692%20681.038769l159.113846%20159.113846-159.113846%20159.113847-55.689846-55.611077%2064.039385-64.039385H551.384615v-78.769231h246.232616l-64.039385-64.039384%2055.689846-55.768616z'%20p-id='12991'%3e%3c/path%3e%3c/svg%3e\");\n }\n\n .workspace.panel-right .panel-sidebar {\n border-left: 1px solid var(--border-default);\n }\n\n .workspace.panel-left .panel-sidebar {\n border-right: 1px solid var(--border-default);\n }\n\n .template-export-modal {\n display: grid;\n gap: 16px;\n }\n\n \n\n .template-export-groups {\n display: grid;\n gap: 12px;\n max-height: 360px;\n overflow: auto;\n }\n\n .template-export-group {\n display: grid;\n gap: 10px;\n padding: 14px 16px;\n border: 1px solid var(--border-default);\n border-radius: 12px;\n background: #ffffff;\n }\n\n .template-export-group-title {\n font-size: 13px;\n font-weight: 700;\n color: var(--text-primary);\n }\n\n .template-export-field-list {\n display: grid;\n gap: 10px;\n }\n\n .template-export-field {\n display: grid;\n gap: 4px;\n }\n\n .template-export-field-path {\n padding-left: 28px;\n font-size: 12px;\n color: var(--text-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n }\n\n .template-export-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 8px 24px;\n }\n",e.appendChild(o)}async function w(e){const t=x();return t||(n=e.atommUiScriptUrl||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.js",u||(u=new Promise(e=>{const t=x();if(t)return void e(t);const a=document.querySelector(`script[data-generator-workbench-atomm-ui="true"][src="${n}"]`);if(a){const t=()=>e(x());return a.addEventListener("load",t,{once:!0}),void a.addEventListener("error",()=>e(void 0),{once:!0})}const o=document.createElement("script");o.src=n,o.async=!0,o.dataset.generatorWorkbenchAtommUi="true",o.addEventListener("load",()=>{e(x())},{once:!0}),o.addEventListener("error",()=>e(void 0),{once:!0}),(document.head||document.body||document.documentElement).appendChild(o)}),u));var n}async function y(e,t){const n=await f(t);!function(){const e=globalThis;e.process?e.process.env?e.process.env.NODE_ENV||(e.process.env.NODE_ENV="production"):e.process.env={NODE_ENV:"production"}:e.process={env:{NODE_ENV:"production"}}}(),await b(e,t.atommUiCssUrl);const a=function(e,t){return e.reactive({shellMode:t.mode||"full",logoText:t.logoText||t.title,logoSrc:t.logoUrl||"https://storage-us.atomm.com/resource/xart/static/agent/imgs/atomm-logo.svg",panelTarget:t.panelTarget||"right",avatarMenuTrigger:t.avatarMenuTrigger||"hover",templateEnabled:!1!==t.templateEnabled,exportEnabled:!1!==t.exportEnabled,studioEnabled:!1!==t.studioEnabled,isLogin:!1,avatarSrc:"",avatarText:"U",authDisplayName:"",authSubline:"",creditsBalance:0,exportCreditsCost:1,loginLoading:!1,templateDialogOpen:!1,templateExportLoading:!1,templateSelectedFieldPaths:[],templateFieldGroups:[]})}(n,t),o={onLogin:()=>{},onLogout:()=>{},onImportTemplate:()=>{},onExportTemplate:()=>{},onCloseTemplateDialog:()=>{},onToggleTemplateDialog:()=>{},onToggleTemplateField:()=>{},onConfirmTemplateExport:()=>{},onExportSvg:()=>{},onOpenInStudio:()=>{},onFileChange:()=>{}},r=document.createElement("div");r.setAttribute("data-workbench-vue-root",""),e.appendChild(r);const i=await w(t),l=n.createApp({setup:()=>({state:a,callbacks:o}),template:c});i?l.use(i):function(e){e.component("xt-button",{inheritAttrs:!0,template:'<button v-bind="$attrs"><slot name="icon" /><slot /><slot name="append-icon" /></button>'}),e.component("xt-avatar",{inheritAttrs:!0,template:'<span v-bind="$attrs"><slot /></span>'}),e.component("xt-dropdown-menu",{inheritAttrs:!0,template:'<div v-bind="$attrs"><slot name="trigger" /><slot name="overlay" /><slot /></div>'}),e.component("xt-hover-card",{inheritAttrs:!0,template:'<div v-bind="$attrs"><slot name="trigger" /><slot name="content" /></div>'}),e.component("xt-modal",{inheritAttrs:!1,props:{modelValue:{type:Boolean,default:!1},title:{type:String,default:""}},emits:["update:modelValue"],template:'\n <div v-if="modelValue" v-bind="$attrs">\n <div>{{ title }}</div>\n <slot />\n </div>\n '}),e.component("xt-checkbox",{inheritAttrs:!1,props:{modelValue:{type:Boolean,default:!1},label:{type:String,default:""},disabled:{type:Boolean,default:!1}},emits:["update:modelValue","change"],template:'\n <label v-bind="$attrs">\n <input\n type="checkbox"\n :checked="modelValue"\n :disabled="disabled"\n @change="$emit(\'update:modelValue\', $event.target.checked)"\n />\n <span><slot>{{ label }}</slot></span>\n </label>\n '})}(l),l.mount(r);const p=function(e){return{root:e,workspace:s(e,'[data-role="workspace"]'),logoArea:e.querySelector('[data-role="logo-area"]'),workspaceHost:e.querySelector('[data-role="workspace-host"]'),canvasHost:e.querySelector('[data-role="canvas-host"]'),panelHost:e.querySelector('[data-role="panel-host"]'),templateFileInput:s(e,'[data-role="template-file-input"]')}}(e);return{state:a,callbacks:o,app:l,refs:p}}function k(e){e.app.unmount()}const _={title:"",mode:"full",templateEnabled:!0,exportEnabled:!0,studioEnabled:!0,autoMount:!0,panelTarget:"right",avatarMenuTrigger:"hover"};function C(e){return"shell"===e.mode}class E extends HTMLElement{constructor(){super(),n(this,"_sdk",null),n(this,"_runtime",null),n(this,"_config",{..._}),n(this,"_mounted",!1),n(this,"_state",{authStatus:{isLogin:!1,userInfo:null},creditsBalance:0,billingUsage:null,busy:{login:!1,importTemplate:!1,exportTemplate:!1,exportSvg:!1,openInStudio:!1},menu:{avatarOpen:!1,fabOpen:!1}}),n(this,"_cleanupAuth"),n(this,"_cleanupCredits"),n(this,"_cleanupBilling"),n(this,"_authController"),n(this,"_templateController"),n(this,"_exportController"),n(this,"_runtimeController"),n(this,"_shellContext"),this.attachShadow({mode:"open"})}get sdk(){return this._sdk}set sdk(e){this._sdk=e}get runtime(){return this._runtime}set runtime(e){this._runtime=e}get config(){return this._config}set config(e){var t;this._config=(t=e,{..._,...t}),this._mounted&&this.render()}connectedCallback(){!1!==this._config.autoMount&&!this._mounted&&this._sdk&&this._runtime&&this.mount()}disconnectedCallback(){this.unmount()}async mount(){var e,t,n;if(this._mounted)return;if(!this.shadowRoot)throw new Error("[generator-workbench] shadowRoot is required");if(!this._sdk)throw new Error("[generator-workbench] sdk is required before mount()");if(!this._runtime)throw new Error("[generator-workbench] runtime is required before mount()");await this.render(),this.bindControllers(),this.bindShellCallbacks(),this.bindAuthState(),await this.syncBillingState();const a=this.requireRefs();if(C(this._config)){const t=a.workspaceHost;if(!t)throw new Error("[generator-workbench] workspace host is required in shell mode");await(null==(e=this._runtimeController)?void 0:e.mountWorkspace(t))}else{const e=a.canvasHost,o=a.panelHost;if(!e||!o)throw new Error("[generator-workbench] canvas host and panel host are required in full/template mode");await(null==(t=this._runtimeController)?void 0:t.mountCanvas(e)),await(null==(n=this._runtimeController)?void 0:n.mountPanel(o,this._state.activePanelFilter))}this._mounted=!0,l(this,"workbench-ready",{sdk:this._sdk,runtime:this._runtime})}async unmount(){var e,t,n,a;null==(e=this._cleanupAuth)||e.call(this),this._cleanupAuth=void 0,null==(t=this._cleanupCredits)||t.call(this),this._cleanupCredits=void 0,null==(n=this._cleanupBilling)||n.call(this),this._cleanupBilling=void 0,await(null==(a=this._runtimeController)?void 0:a.unmountAll()),this._shellContext&&(k(this._shellContext),this._shellContext=void 0),this._mounted=!1}refreshLayout(){}async importTemplate(e){try{const t=await this.requireTemplateController().importTemplate(e);this._state.activePanelFilter=t.panelFilter;const n=this.requireRefs();if(this._runtimeController&&!C(this._config)){const e=n.panelHost;if(!e)throw new Error("[generator-workbench] panel host is required for template import");await this._runtimeController.remountPanel(e,t.panelFilter)}l(this,"template-imported",t)}catch(t){this.handleError("template",t)}}async exportTemplate(){return this.exportTemplateWithFields()}async exportTemplateWithFields(e){try{this._state.busy.exportTemplate=!0,this._shellContext&&(this._shellContext.state.templateExportLoading=!0);l(this,"template-exported",await this.requireTemplateController().exportTemplate(e)),e&&this.closeTemplateExportDialog()}catch(t){this.handleError("template",t)}finally{this._state.busy.exportTemplate=!1,this._shellContext&&(this._shellContext.state.templateExportLoading=!1)}}async exportSvg(){try{l(this,"svg-export",await this.requireExportController().exportSvg())}catch(e){this.handleError("export",e)}}async openInStudio(){try{l(this,"studio-open",await this.requireExportController().openInStudio())}catch(e){this.handleError("export",e)}}async setAuthToken(e){var t,n;const a=e.trim();if(!a)throw new Error("[generator-workbench] token is required");if(!this._sdk)throw new Error("[generator-workbench] sdk is required before setAuthToken()");const o=null==(n=(t=this._sdk).getAppKey)?void 0:n.call(t);if(!o)throw new Error("[generator-workbench] sdk.getAppKey() is required for setAuthToken()");if("function"!=typeof this._sdk.auth.syncToken)throw new Error("[generator-workbench] sdk.auth.syncToken() is required for setAuthToken()");try{localStorage.setItem(`__atomm_sdk_token__${o}`,a)}catch{}await this._sdk.auth.syncToken(a)}async render(){this.shadowRoot&&(this._shellContext&&(k(this._shellContext),this.shadowRoot.innerHTML=""),this._shellContext=await y(this.shadowRoot,this._config),this.syncAuthUI(),this.syncBillingUI())}bindControllers(){this._sdk&&this._runtime&&(this._authController=function(e){const{sdk:t}=e;return{getStatus:()=>t.auth.getStatus(),subscribe:e=>(e(t.auth.getStatus()),t.auth.onChange(e)),async login(){await t.auth.login()},async logout(){await t.auth.logout()}}}({sdk:this._sdk}),this._templateController=i({sdk:this._sdk,runtime:this._runtime,config:this._config}),this._exportController=function(e){const{sdk:t,runtime:n,config:a,element:o}=e;async function r(){if(!t.auth.getStatus().isLogin&&(await t.auth.login(),!t.auth.getStatus().isLogin))throw new Error("[generator-workbench] login is required before export")}async function i(){var e;(null==(e=t.billing)?void 0:e.consume)&&await t.billing.consume()}return{async exportSvg(){var e;return await(null==(e=a.beforeExportSvg)?void 0:e.call(a,{sdk:t,runtime:n,element:o})),await r(),await i(),t.export.download({format:"svg"})},async openInStudio(){var e;return await(null==(e=a.beforeOpenInStudio)?void 0:e.call(a,{sdk:t,runtime:n,element:o})),await r(),await i(),t.export.openInStudio({format:"svg"})}}}({sdk:this._sdk,runtime:this._runtime,config:this._config,element:this}),this._runtimeController=function(e){const{runtime:t}=e;let n=null,a=null;async function o(e,n){null==a||a.unmount(),a=await Promise.resolve(t.mount({mode:"embed",target:"panel",container:e,panelFilter:n}))}return{mountWorkspace:async function(e){null==n||n.unmount(),null==a||a.unmount(),n=await Promise.resolve(t.mount({mode:"full",target:"full",container:e})),a=null},mountCanvas:async function(e){null==n||n.unmount(),n=await Promise.resolve(t.mount({mode:"embed",target:"canvas",container:e}))},mountPanel:o,async remountPanel(e,t){await o(e,t)},async unmountAll(){null==n||n.unmount(),null==a||a.unmount(),n=null,a=null}}}({runtime:this._runtime}))}bindShellCallbacks(){if(!this._shellContext)return;const{callbacks:e}=this._shellContext;e.onLogin=()=>{this.handleLogin()},e.onLogout=()=>{this.handleLogout()},e.onImportTemplate=()=>{this.requireRefs().templateFileInput.click()},e.onExportTemplate=()=>{this.openTemplateExportDialog()},e.onCloseTemplateDialog=()=>{this.closeTemplateExportDialog()},e.onToggleTemplateDialog=e=>{e||this.closeTemplateExportDialog()},e.onToggleTemplateField=(e,t)=>{this.toggleTemplateExportField(e,t)},e.onConfirmTemplateExport=()=>{var e;this.exportTemplateWithFields((null==(e=this._shellContext)?void 0:e.state.templateSelectedFieldPaths)||[])},e.onExportSvg=()=>{this.exportSvg()},e.onOpenInStudio=()=>{this.openInStudio()},e.onFileChange=e=>{var t;const n=e.target,a=null==(t=n.files)?void 0:t[0];a&&(this.importTemplate(a),n.value="")}}openTemplateExportDialog(){try{if(!this._shellContext)return;const e=this.requireTemplateController().prepareTemplateExport();this._shellContext.state.templateFieldGroups=e.fieldGroups.map(e=>({...e,fields:e.fields.map(e=>({...e}))})),this._shellContext.state.templateSelectedFieldPaths=[...e.selectedFieldPaths],this._shellContext.state.templateDialogOpen=!0}catch(e){this.handleError("template",e)}}closeTemplateExportDialog(){this._shellContext&&(this._shellContext.state.templateDialogOpen=!1,this._shellContext.state.templateFieldGroups=[],this._shellContext.state.templateSelectedFieldPaths=[])}toggleTemplateExportField(e,t){if(!this._shellContext)return;const n=new Set(this._shellContext.state.templateSelectedFieldPaths);t?n.add(e):n.delete(e),this._shellContext.state.templateSelectedFieldPaths=Array.from(n)}bindAuthState(){var e;const t=this.requireAuthController();null==(e=this._cleanupAuth)||e.call(this),this._cleanupAuth=t.subscribe(e=>{this._state.authStatus=e,this.syncAuthUI(),this.syncBillingState(e),l(this,"auth-change",e)}),this.bindBillingSubscriptions()}bindBillingSubscriptions(){var e,t,n,a;null==(e=this._cleanupCredits)||e.call(this),this._cleanupCredits=void 0,null==(t=this._cleanupBilling)||t.call(this),this._cleanupBilling=void 0;const o=null==(n=this._sdk)?void 0:n.credits;"function"==typeof(null==o?void 0:o.onChange)&&(this._cleanupCredits=o.onChange(e=>{this._state.creditsBalance=Number(e)||0,this.syncBillingUI()}));const r=null==(a=this._sdk)?void 0:a.billing;"function"==typeof(null==r?void 0:r.onChange)&&(this._cleanupBilling=r.onChange(e=>{this.applyBillingUsage(e)}))}syncAuthUI(){var e,t,n,a;if(!this._shellContext)return;const{state:o}=this._shellContext,{isLogin:r,userInfo:i}=this._state.authStatus,l=(null==(e=null==i?void 0:i.userName)?void 0:e.trim())||(null==(t=null==i?void 0:i.email)?void 0:t.trim())||(null==(n=null==i?void 0:i.phoneNumber)?void 0:n.trim())||"",s=l?l.charAt(0).toUpperCase():"U",p=(null==(a=null==i?void 0:i.email)?void 0:a.trim())||((null==i?void 0:i.phoneNumber)?`${i.phoneZone||""}${i.phoneNumber}`:"Connected to Generator SDK");o.isLogin=r,o.avatarSrc=r&&(null==i?void 0:i.headpic)||"",o.avatarText=s,o.authDisplayName=l,o.authSubline=r?p:""}syncBillingUI(){if(!this._shellContext)return;const{state:e}=this._shellContext,t=this._state.billingUsage;e.creditsBalance=this._state.creditsBalance,e.exportCreditsCost=(null==t?void 0:t.creditsPerUse)??(t&&"unitPrice"in t&&Number(t.unitPrice)||1)}applyBillingUsage(e){this._state.billingUsage=e||null,e&&"number"==typeof e.creditsBalance&&(this._state.creditsBalance=e.creditsBalance),this.syncBillingUI()}resetBillingState(){this._state.creditsBalance=0,this._state.billingUsage=null,this.syncBillingUI()}async syncBillingState(e=this._state.authStatus){var t,n,a,o,r,i,l,s;if(!e.isLogin||!this._sdk)return void this.resetBillingState();const p=null==(n=null==(t=this._sdk.credits)?void 0:t.getCachedBalance)?void 0:n.call(t);"number"==typeof p&&(this._state.creditsBalance=p);const d=null==(o=null==(a=this._sdk.billing)?void 0:a.getCachedUsage)?void 0:o.call(a);d?this.applyBillingUsage(d):this.syncBillingUI();try{const[e,t]=await Promise.all([null==(i=null==(r=this._sdk.credits)?void 0:r.getBalance)?void 0:i.call(r),null==(s=null==(l=this._sdk.billing)?void 0:l.getUsage)?void 0:s.call(l)]);if("number"==typeof(null==e?void 0:e.quota)&&(this._state.creditsBalance=e.quota),t)return void this.applyBillingUsage(t);this.syncBillingUI()}catch(c){this.handleError("auth",c)}}async handleLogin(){try{this._state.busy.login=!0,this._shellContext&&(this._shellContext.state.loginLoading=!0),await this.requireAuthController().login()}catch(e){this.handleError("auth",e)}finally{this._state.busy.login=!1,this._shellContext&&(this._shellContext.state.loginLoading=!1)}}async handleLogout(){try{await this.requireAuthController().logout()}catch(e){this.handleError("auth",e)}}handleError(e,t){var n,a;const o=t instanceof Error?t:new Error(String(t));null==(a=(n=this._config).onError)||a.call(n,o,e),l(this,"workbench-error",{source:e,error:o})}requireRefs(){if(!this._shellContext)throw new Error("[generator-workbench] shell context is not ready");return this._shellContext.refs}requireAuthController(){if(!this._authController)throw new Error("[generator-workbench] auth controller is not ready");return this._authController}requireTemplateController(){if(!this._templateController)throw new Error("[generator-workbench] template controller is not ready");return this._templateController}requireExportController(){if(!this._exportController)throw new Error("[generator-workbench] export controller is not ready");return this._exportController}}const S="generator-workbench";e.GENERATOR_WORKBENCH_TAG_NAME=S,e.GeneratorWorkbenchElement=E,e.defineGeneratorWorkbench=function(e=S){const t=customElements.get(e);return t||(customElements.define(e,E),E)},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomm-developer/generator-workbench",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Unified generator shell based on Web Components",
5
5
  "keywords": [
6
6
  "atomm",