@atomm-developer/generator-workbench 0.1.3 → 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 +19 -3
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +376 -91
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
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
|
|
12
|
+
- runtime mounting for either a free workspace host or split canvas / panel hosts
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -25,6 +25,8 @@ 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` 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
|
+
|
|
28
30
|
## Usage
|
|
29
31
|
|
|
30
32
|
```html
|
|
@@ -39,7 +41,7 @@ workbench.sdk = sdk
|
|
|
39
41
|
workbench.runtime = runtime
|
|
40
42
|
workbench.config = {
|
|
41
43
|
title: 'My Generator',
|
|
42
|
-
mode: '
|
|
44
|
+
mode: 'shell',
|
|
43
45
|
templateEnabled: true,
|
|
44
46
|
exportEnabled: true,
|
|
45
47
|
studioEnabled: true,
|
|
@@ -48,9 +50,21 @@ workbench.config = {
|
|
|
48
50
|
await workbench.mount()
|
|
49
51
|
```
|
|
50
52
|
|
|
53
|
+
If you need to override the default CDN addresses, pass:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
workbench.config = {
|
|
57
|
+
title: 'My Generator',
|
|
58
|
+
vueScriptUrl: 'https://your-cdn/vue.global.prod.js',
|
|
59
|
+
atommUiCssUrl: 'https://your-cdn/atomm-ui.css',
|
|
60
|
+
atommUiScriptUrl: 'https://your-cdn/atomm-ui.js',
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
51
64
|
## Shell Modes
|
|
52
65
|
|
|
53
|
-
- `mode: '
|
|
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.
|
|
54
68
|
- `mode: 'template'` hides the top bar, keeps `#sidebar-footer`, and is useful when the host page already owns branding and login UI.
|
|
55
69
|
|
|
56
70
|
In `template` mode, the host shell can pass an external token into the workbench:
|
|
@@ -81,3 +95,5 @@ pnpm --dir generator-workbench dev
|
|
|
81
95
|
```
|
|
82
96
|
|
|
83
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,8 +101,12 @@ 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;
|
|
108
|
+
/** Override the default atomm-ui CDN script URL auto-loaded when AtommUI is absent */
|
|
109
|
+
atommUiScriptUrl?: string;
|
|
106
110
|
runtimePanelFilter?: PanelFilter;
|
|
107
111
|
getTemplateMeta?: () => Record<string, unknown>;
|
|
108
112
|
getTemplateFieldPaths?: (panelSchema: PanelSchema) => string[];
|
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,12 +286,17 @@ function collectWorkbenchRefs(root) {
|
|
|
273
286
|
root,
|
|
274
287
|
workspace: queryRequired(root, '[data-role="workspace"]'),
|
|
275
288
|
logoArea: root.querySelector('[data-role="logo-area"]'),
|
|
276
|
-
|
|
277
|
-
|
|
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";
|
|
299
|
+
const ATOMM_UI_CDN_SCRIPT_URL = "https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.js";
|
|
282
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";
|
|
283
301
|
const EXPORT_ICON_DATA_URI = "data:image/svg+xml,%3Csvg%20viewBox%3D'0%200%201088%201024'%20xmlns%3D'http%3A//www.w3.org/2000/svg'%3E%3Cpath%20d%3D'M416%20128v85.312h-128A42.688%2042.688%200%200%200%20245.312%20256v512c0%2023.552%2019.136%2042.688%2042.688%2042.688h512a42.688%2042.688%200%200%200%2042.688-42.688V597.312H928V768a128%20128%200%200%201-128%20128h-512a128%20128%200%200%201-128-128V256a128%20128%200%200%201%20128-128h128z'%20fill%3D'%23ffffff'/%3E%3Cpath%20d%3D'M723.52%20520.832L924.352%20320l-200.832-200.832-60.352%2060.352%2097.856%2097.792h-67.712A298.688%20298.688%200%200%200%20394.688%20576v85.312H480V576a213.312%20213.312%200%200%201%20213.312-213.312h67.712l-97.856%2097.792%2060.352%2060.352z'%20fill%3D'%23ffffff'/%3E%3C/svg%3E";
|
|
284
302
|
const WORKBENCH_SHELL_STYLES = `
|
|
@@ -350,6 +368,17 @@ const WORKBENCH_SHELL_STYLES = `
|
|
|
350
368
|
grid-template-columns: minmax(0, 1fr) 320px;
|
|
351
369
|
}
|
|
352
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
|
+
|
|
353
382
|
.workspace.panel-left {
|
|
354
383
|
grid-template-columns: 320px minmax(0, 1fr);
|
|
355
384
|
}
|
|
@@ -569,11 +598,41 @@ const WORKBENCH_SHELL_STYLES = `
|
|
|
569
598
|
.panel-actions,
|
|
570
599
|
#sidebar-footer {
|
|
571
600
|
flex-shrink: 0;
|
|
601
|
+
width: 262px;
|
|
602
|
+
display: flex;
|
|
603
|
+
align-items: center;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.panel-sidebar #sidebar-footer {
|
|
572
607
|
padding: 14px 24px;
|
|
573
|
-
|
|
574
|
-
|
|
608
|
+
flex-shrink: 0;
|
|
609
|
+
width: auto;
|
|
575
610
|
display: flex;
|
|
576
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);
|
|
577
636
|
}
|
|
578
637
|
|
|
579
638
|
.sidebar-footer-export {
|
|
@@ -640,18 +699,19 @@ const WORKBENCH_SHELL_STYLES = `
|
|
|
640
699
|
.export-overlay-item-icon {
|
|
641
700
|
width: 16px;
|
|
642
701
|
height: 16px;
|
|
643
|
-
border-radius: 4px;
|
|
644
|
-
background: #cbd5e1;
|
|
645
702
|
display: inline-block;
|
|
646
703
|
flex: 0 0 auto;
|
|
704
|
+
background-repeat: no-repeat;
|
|
705
|
+
background-position: center;
|
|
706
|
+
background-size: contain;
|
|
647
707
|
}
|
|
648
708
|
|
|
649
709
|
.export-overlay-item-icon--svg {
|
|
650
|
-
background:
|
|
710
|
+
background-image: url("${DOWNLOAD_MENU_ICON_URL}");
|
|
651
711
|
}
|
|
652
712
|
|
|
653
713
|
.export-overlay-item-icon--studio {
|
|
654
|
-
background:
|
|
714
|
+
background-image: url("${OPEN_IN_STUDIO_MENU_ICON_URL}");
|
|
655
715
|
}
|
|
656
716
|
|
|
657
717
|
.workspace.panel-right .panel-sidebar {
|
|
@@ -841,66 +901,136 @@ const WORKBENCH_VUE_TEMPLATE = `
|
|
|
841
901
|
|
|
842
902
|
<div
|
|
843
903
|
class="workspace"
|
|
844
|
-
:class="
|
|
904
|
+
:class="[
|
|
905
|
+
state.shellMode === 'shell'
|
|
906
|
+
? 'shell-mode-shell'
|
|
907
|
+
: (state.panelTarget === 'left' ? 'panel-left' : 'panel-right'),
|
|
908
|
+
]"
|
|
845
909
|
data-role="workspace"
|
|
846
910
|
>
|
|
847
|
-
<main
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
</
|
|
876
|
-
</template>
|
|
877
|
-
</xt-button>
|
|
878
|
-
</template>
|
|
879
|
-
<template #overlay>
|
|
880
|
-
<div class="export-overlay-menu fab-menu-content" data-role="fab-menu">
|
|
881
|
-
<div
|
|
882
|
-
v-show="state.exportEnabled"
|
|
883
|
-
class="export-overlay-item"
|
|
884
|
-
data-role="export-svg"
|
|
885
|
-
@click="callbacks.onExportSvg()"
|
|
886
|
-
>
|
|
887
|
-
<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>
|
|
888
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>
|
|
889
969
|
</div>
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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>
|
|
899
1007
|
</div>
|
|
900
1008
|
</template>
|
|
901
|
-
</xt-
|
|
902
|
-
</
|
|
903
|
-
|
|
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>
|
|
904
1034
|
</div>
|
|
905
1035
|
|
|
906
1036
|
<input
|
|
@@ -967,20 +1097,107 @@ const WORKBENCH_VUE_TEMPLATE = `
|
|
|
967
1097
|
</xt-modal>
|
|
968
1098
|
</div>
|
|
969
1099
|
`;
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1100
|
+
const vueRuntimePromises = /* @__PURE__ */ new Map();
|
|
1101
|
+
let atommUiLoadPromise = null;
|
|
1102
|
+
const atommUiCssTextPromises = /* @__PURE__ */ new Map();
|
|
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
|
|
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
|
|
975
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;
|
|
976
1160
|
}
|
|
977
|
-
return
|
|
1161
|
+
return loadVueRuntime(config.vueScriptUrl || VUE_CDN_SCRIPT_URL);
|
|
1162
|
+
}
|
|
1163
|
+
function getAtommUiPlugin() {
|
|
1164
|
+
return globalThis.AtommUI;
|
|
1165
|
+
}
|
|
1166
|
+
function normalizeAtommUiCssForShadowDom(cssText) {
|
|
1167
|
+
return cssText.replace(/(^|})\s*:root(?=\s*{)/g, "$1:host");
|
|
1168
|
+
}
|
|
1169
|
+
function loadAtommUiCssText(cssUrl) {
|
|
1170
|
+
const cached = atommUiCssTextPromises.get(cssUrl);
|
|
1171
|
+
if (cached) {
|
|
1172
|
+
return cached;
|
|
1173
|
+
}
|
|
1174
|
+
const request = fetch(cssUrl).then(async (response) => {
|
|
1175
|
+
if (!response.ok) {
|
|
1176
|
+
atommUiCssTextPromises.delete(cssUrl);
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
return normalizeAtommUiCssForShadowDom(await response.text());
|
|
1180
|
+
}).catch(() => {
|
|
1181
|
+
atommUiCssTextPromises.delete(cssUrl);
|
|
1182
|
+
return null;
|
|
1183
|
+
});
|
|
1184
|
+
atommUiCssTextPromises.set(cssUrl, request);
|
|
1185
|
+
return request;
|
|
978
1186
|
}
|
|
979
|
-
function injectStyles(root, cdnCssUrl) {
|
|
980
|
-
const
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1187
|
+
async function injectStyles(root, cdnCssUrl) {
|
|
1188
|
+
const cssUrl = cdnCssUrl || ATOMM_UI_CDN_CSS_URL;
|
|
1189
|
+
const atommUiCssText = await loadAtommUiCssText(cssUrl);
|
|
1190
|
+
if (atommUiCssText) {
|
|
1191
|
+
const atommUiStyle = document.createElement("style");
|
|
1192
|
+
atommUiStyle.dataset.generatorWorkbenchAtommUi = "true";
|
|
1193
|
+
atommUiStyle.textContent = atommUiCssText;
|
|
1194
|
+
root.appendChild(atommUiStyle);
|
|
1195
|
+
} else {
|
|
1196
|
+
const link = document.createElement("link");
|
|
1197
|
+
link.rel = "stylesheet";
|
|
1198
|
+
link.href = cssUrl;
|
|
1199
|
+
root.appendChild(link);
|
|
1200
|
+
}
|
|
984
1201
|
const style = document.createElement("style");
|
|
985
1202
|
style.textContent = WORKBENCH_SHELL_STYLES;
|
|
986
1203
|
root.appendChild(style);
|
|
@@ -1087,24 +1304,70 @@ function registerFallbackComponents(app) {
|
|
|
1087
1304
|
`
|
|
1088
1305
|
});
|
|
1089
1306
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1307
|
+
function loadAtommUiScript(scriptUrl) {
|
|
1308
|
+
if (atommUiLoadPromise) {
|
|
1309
|
+
return atommUiLoadPromise;
|
|
1310
|
+
}
|
|
1311
|
+
atommUiLoadPromise = new Promise((resolve) => {
|
|
1312
|
+
const existingPlugin = getAtommUiPlugin();
|
|
1313
|
+
if (existingPlugin) {
|
|
1314
|
+
resolve(existingPlugin);
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
const existingScript = document.querySelector(
|
|
1318
|
+
`script[data-generator-workbench-atomm-ui="true"][src="${scriptUrl}"]`
|
|
1319
|
+
);
|
|
1320
|
+
if (existingScript) {
|
|
1321
|
+
const finalize = () => resolve(getAtommUiPlugin());
|
|
1322
|
+
existingScript.addEventListener("load", finalize, { once: true });
|
|
1323
|
+
existingScript.addEventListener("error", () => resolve(void 0), {
|
|
1324
|
+
once: true
|
|
1325
|
+
});
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
const script = document.createElement("script");
|
|
1329
|
+
script.src = scriptUrl;
|
|
1330
|
+
script.async = true;
|
|
1331
|
+
script.dataset.generatorWorkbenchAtommUi = "true";
|
|
1332
|
+
script.addEventListener(
|
|
1333
|
+
"load",
|
|
1334
|
+
() => {
|
|
1335
|
+
resolve(getAtommUiPlugin());
|
|
1336
|
+
},
|
|
1337
|
+
{ once: true }
|
|
1338
|
+
);
|
|
1339
|
+
script.addEventListener("error", () => resolve(void 0), { once: true });
|
|
1340
|
+
(document.head || document.body || document.documentElement).appendChild(
|
|
1341
|
+
script
|
|
1342
|
+
);
|
|
1343
|
+
});
|
|
1344
|
+
return atommUiLoadPromise;
|
|
1345
|
+
}
|
|
1346
|
+
async function resolveAtommUiPlugin(config) {
|
|
1347
|
+
const existingPlugin = getAtommUiPlugin();
|
|
1348
|
+
if (existingPlugin) {
|
|
1349
|
+
return existingPlugin;
|
|
1350
|
+
}
|
|
1351
|
+
return loadAtommUiScript(config.atommUiScriptUrl || ATOMM_UI_CDN_SCRIPT_URL);
|
|
1352
|
+
}
|
|
1353
|
+
async function renderWorkbenchShell(root, config) {
|
|
1354
|
+
const vue = await resolveVueRuntime(config);
|
|
1092
1355
|
ensureBrowserProcessEnv();
|
|
1093
|
-
injectStyles(root, config.atommUiCssUrl);
|
|
1356
|
+
await injectStyles(root, config.atommUiCssUrl);
|
|
1094
1357
|
const state = stateFromConfig(vue, config);
|
|
1095
1358
|
const callbacks = createNoopCallbacks();
|
|
1096
1359
|
const container = document.createElement("div");
|
|
1097
1360
|
container.setAttribute("data-workbench-vue-root", "");
|
|
1098
1361
|
root.appendChild(container);
|
|
1099
|
-
const
|
|
1362
|
+
const atommUi = await resolveAtommUiPlugin(config);
|
|
1100
1363
|
const app = vue.createApp({
|
|
1101
1364
|
setup() {
|
|
1102
1365
|
return { state, callbacks };
|
|
1103
1366
|
},
|
|
1104
1367
|
template: WORKBENCH_VUE_TEMPLATE
|
|
1105
1368
|
});
|
|
1106
|
-
if (
|
|
1107
|
-
app.use(
|
|
1369
|
+
if (atommUi) {
|
|
1370
|
+
app.use(atommUi);
|
|
1108
1371
|
} else {
|
|
1109
1372
|
registerFallbackComponents(app);
|
|
1110
1373
|
}
|
|
@@ -1125,6 +1388,9 @@ const DEFAULT_CONFIG = {
|
|
|
1125
1388
|
panelTarget: "right",
|
|
1126
1389
|
avatarMenuTrigger: "hover"
|
|
1127
1390
|
};
|
|
1391
|
+
function isShellMode(config) {
|
|
1392
|
+
return config.mode === "shell";
|
|
1393
|
+
}
|
|
1128
1394
|
const TOKEN_STORAGE_KEY_PREFIX = "__atomm_sdk_token__";
|
|
1129
1395
|
function normalizeWorkbenchConfig(config) {
|
|
1130
1396
|
return {
|
|
@@ -1168,7 +1434,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1168
1434
|
set config(value) {
|
|
1169
1435
|
this._config = normalizeWorkbenchConfig(value);
|
|
1170
1436
|
if (this._mounted) {
|
|
1171
|
-
this.render();
|
|
1437
|
+
void this.render();
|
|
1172
1438
|
}
|
|
1173
1439
|
}
|
|
1174
1440
|
connectedCallback() {
|
|
@@ -1180,7 +1446,7 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1180
1446
|
void this.unmount();
|
|
1181
1447
|
}
|
|
1182
1448
|
async mount() {
|
|
1183
|
-
var _a, _b;
|
|
1449
|
+
var _a, _b, _c;
|
|
1184
1450
|
if (this._mounted) {
|
|
1185
1451
|
return;
|
|
1186
1452
|
}
|
|
@@ -1193,17 +1459,32 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1193
1459
|
if (!this._runtime) {
|
|
1194
1460
|
throw new Error("[generator-workbench] runtime is required before mount()");
|
|
1195
1461
|
}
|
|
1196
|
-
this.render();
|
|
1462
|
+
await this.render();
|
|
1197
1463
|
this.bindControllers();
|
|
1198
1464
|
this.bindShellCallbacks();
|
|
1199
1465
|
this.bindAuthState();
|
|
1200
1466
|
await this.syncBillingState();
|
|
1201
1467
|
const refs = this.requireRefs();
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
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
|
+
}
|
|
1207
1488
|
this._mounted = true;
|
|
1208
1489
|
dispatchWorkbenchEvent(this, "workbench-ready", {
|
|
1209
1490
|
sdk: this._sdk,
|
|
@@ -1232,9 +1513,13 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1232
1513
|
const result = await this.requireTemplateController().importTemplate(file);
|
|
1233
1514
|
this._state.activePanelFilter = result.panelFilter;
|
|
1234
1515
|
const refs = this.requireRefs();
|
|
1235
|
-
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
|
+
}
|
|
1236
1521
|
await this._runtimeController.remountPanel(
|
|
1237
|
-
|
|
1522
|
+
panelHost,
|
|
1238
1523
|
result.panelFilter
|
|
1239
1524
|
);
|
|
1240
1525
|
}
|
|
@@ -1306,13 +1591,13 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1306
1591
|
}
|
|
1307
1592
|
await this._sdk.auth.syncToken(normalizedToken);
|
|
1308
1593
|
}
|
|
1309
|
-
render() {
|
|
1594
|
+
async render() {
|
|
1310
1595
|
if (!this.shadowRoot) return;
|
|
1311
1596
|
if (this._shellContext) {
|
|
1312
1597
|
unmountWorkbenchShell(this._shellContext);
|
|
1313
1598
|
this.shadowRoot.innerHTML = "";
|
|
1314
1599
|
}
|
|
1315
|
-
this._shellContext = renderWorkbenchShell(this.shadowRoot, this._config);
|
|
1600
|
+
this._shellContext = await renderWorkbenchShell(this.shadowRoot, this._config);
|
|
1316
1601
|
this.syncAuthUI();
|
|
1317
1602
|
this.syncBillingUI();
|
|
1318
1603
|
}
|
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`;function c(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"}}}(),function(t,e){const n=document.createElement("link");n.rel="stylesheet",n.href=e||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.min.css",t.appendChild(n);const a=document.createElement("style");a.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(a)}(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=globalThis.AtommUI,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 h(t){t.app.unmount()}const u={title:"",mode:"full",templateEnabled:!0,exportEnabled:!0,studioEnabled:!0,autoMount:!0,panelTarget:"right",avatarMenuTrigger:"hover"};class g extends HTMLElement{constructor(){super(),n(this,"_sdk",null),n(this,"_runtime",null),n(this,"_config",{...u}),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,{...u,...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()");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&&(h(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)}render(){this.shadowRoot&&(this._shellContext&&(h(this._shellContext),this.shadowRoot.innerHTML=""),this._shellContext=c(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 m="generator-workbench";t.GENERATOR_WORKBENCH_TAG_NAME=m,t.GeneratorWorkbenchElement=g,t.defineGeneratorWorkbench=function(t=m){const e=customElements.get(t);return e||(customElements.define(t,g),g)},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"})});
|