@atomm-developer/generator-workbench 0.1.1 → 0.1.3
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 +22 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.es.js +334 -22
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ V1 provides:
|
|
|
7
7
|
- Web Component shell
|
|
8
8
|
- login / avatar / logout entry
|
|
9
9
|
- credits badge and export credit hint
|
|
10
|
-
- local template import
|
|
10
|
+
- local template import entry and template publish modal
|
|
11
11
|
- billing-backed export actions
|
|
12
12
|
- runtime panel / canvas mounting
|
|
13
13
|
|
|
@@ -39,6 +39,7 @@ workbench.sdk = sdk
|
|
|
39
39
|
workbench.runtime = runtime
|
|
40
40
|
workbench.config = {
|
|
41
41
|
title: 'My Generator',
|
|
42
|
+
mode: 'full',
|
|
42
43
|
templateEnabled: true,
|
|
43
44
|
exportEnabled: true,
|
|
44
45
|
studioEnabled: true,
|
|
@@ -47,12 +48,32 @@ workbench.config = {
|
|
|
47
48
|
await workbench.mount()
|
|
48
49
|
```
|
|
49
50
|
|
|
51
|
+
## Shell Modes
|
|
52
|
+
|
|
53
|
+
- `mode: 'full'` keeps the default shell with the top bar and the sidebar footer export area.
|
|
54
|
+
- `mode: 'template'` hides the top bar, keeps `#sidebar-footer`, and is useful when the host page already owns branding and login UI.
|
|
55
|
+
|
|
56
|
+
In `template` mode, the host shell can pass an external token into the workbench:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
await workbench.setAuthToken(token)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This writes the token to `localStorage` using the SDK app key and then syncs the login state through `sdk.auth.syncToken(token)`.
|
|
63
|
+
|
|
50
64
|
## Billing Behavior
|
|
51
65
|
|
|
52
66
|
- When `sdk.credits` and `sdk.billing` are available, the top bar shows the current credits balance after login.
|
|
53
67
|
- The export trigger shows the current `creditsPerUse` hint from `sdk.billing.getUsage()`.
|
|
54
68
|
- `exportSvg()` and `openInStudio()` now follow the standard generator flow: ensure login first, then call `sdk.billing.consume()`, then perform the export action.
|
|
55
69
|
|
|
70
|
+
## Template Publish Behavior
|
|
71
|
+
|
|
72
|
+
- Clicking the top bar template publish action no longer downloads immediately.
|
|
73
|
+
- `generator-workbench` reads `runtime.getPanelSchema()`, renders the generator's own field groups in a modal, and preselects exportable `bind.path` fields.
|
|
74
|
+
- The template JSON is downloaded only after the user confirms the selected fields.
|
|
75
|
+
- `config.getTemplateFieldPaths()` still works as the default subset for the publish modal.
|
|
76
|
+
|
|
56
77
|
## Local Preview
|
|
57
78
|
|
|
58
79
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -38,8 +38,10 @@ export declare interface GeneratorRuntimeLike {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export declare interface GeneratorSDKLike {
|
|
41
|
+
getAppKey?(): string;
|
|
41
42
|
auth: {
|
|
42
43
|
getStatus(): AuthStatus;
|
|
44
|
+
syncToken?(token: string): Promise<AuthStatus> | AuthStatus;
|
|
43
45
|
login(): Promise<UserInfo>;
|
|
44
46
|
logout(): Promise<void>;
|
|
45
47
|
onChange(callback: (status: AuthStatus) => void): () => void;
|
|
@@ -89,6 +91,7 @@ export declare interface GeneratorWorkbenchActionHookContext {
|
|
|
89
91
|
|
|
90
92
|
export declare interface GeneratorWorkbenchConfig {
|
|
91
93
|
title: string;
|
|
94
|
+
mode?: 'full' | 'template';
|
|
92
95
|
logoText?: string;
|
|
93
96
|
logoUrl?: string;
|
|
94
97
|
logoHref?: string;
|
|
@@ -136,11 +139,16 @@ export declare class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
136
139
|
refreshLayout(): void;
|
|
137
140
|
importTemplate(file: File): Promise<void>;
|
|
138
141
|
exportTemplate(): Promise<void>;
|
|
142
|
+
exportTemplateWithFields(selectedFieldPaths?: string[]): Promise<void>;
|
|
139
143
|
exportSvg(): Promise<void>;
|
|
140
144
|
openInStudio(): Promise<void>;
|
|
145
|
+
setAuthToken(token: string): Promise<void>;
|
|
141
146
|
private render;
|
|
142
147
|
private bindControllers;
|
|
143
148
|
private bindShellCallbacks;
|
|
149
|
+
private openTemplateExportDialog;
|
|
150
|
+
private closeTemplateExportDialog;
|
|
151
|
+
private toggleTemplateExportField;
|
|
144
152
|
private bindAuthState;
|
|
145
153
|
private bindBillingSubscriptions;
|
|
146
154
|
private syncAuthUI;
|
package/dist/index.es.js
CHANGED
|
@@ -106,12 +106,35 @@ function assert(condition, message) {
|
|
|
106
106
|
throw new Error(message);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
function collectPanelFieldGroups(panelSchema, allowedFieldPaths) {
|
|
110
|
+
const allowedPathSet = (allowedFieldPaths == null ? void 0 : allowedFieldPaths.length) ? new Set(allowedFieldPaths) : null;
|
|
111
|
+
return panelSchema.groups.map((group) => {
|
|
112
|
+
var _a;
|
|
113
|
+
const fields = group.fields.map((field) => {
|
|
114
|
+
var _a2, _b, _c;
|
|
115
|
+
const path = (_b = (_a2 = field.bind) == null ? void 0 : _a2.path) == null ? void 0 : _b.trim();
|
|
116
|
+
if (!path) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (allowedPathSet && !allowedPathSet.has(path)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
id: field.id,
|
|
124
|
+
label: ((_c = field.label) == null ? void 0 : _c.trim()) || field.id || path,
|
|
125
|
+
path
|
|
126
|
+
};
|
|
127
|
+
}).filter((field) => Boolean(field));
|
|
128
|
+
return {
|
|
129
|
+
id: group.id,
|
|
130
|
+
title: ((_a = group.title) == null ? void 0 : _a.trim()) || group.id,
|
|
131
|
+
fields
|
|
132
|
+
};
|
|
133
|
+
}).filter((group) => group.fields.length > 0);
|
|
134
|
+
}
|
|
135
|
+
function collectPanelFieldPaths(panelSchema, allowedFieldPaths) {
|
|
136
|
+
return collectPanelFieldGroups(panelSchema, allowedFieldPaths).flatMap(
|
|
137
|
+
(group) => group.fields.map((field) => field.path)
|
|
115
138
|
);
|
|
116
139
|
}
|
|
117
140
|
function resolveGeneratorId(panelSchema, state) {
|
|
@@ -129,6 +152,27 @@ function resolveGeneratorId(panelSchema, state) {
|
|
|
129
152
|
}
|
|
130
153
|
function createTemplateController(args) {
|
|
131
154
|
const { sdk, runtime, config } = args;
|
|
155
|
+
function prepareTemplateExport() {
|
|
156
|
+
var _a;
|
|
157
|
+
const state = runtime.getState();
|
|
158
|
+
const panelSchema = runtime.getPanelSchema();
|
|
159
|
+
const configuredFieldPaths = (_a = config.getTemplateFieldPaths) == null ? void 0 : _a.call(config, panelSchema);
|
|
160
|
+
const fieldGroups = collectPanelFieldGroups(panelSchema, configuredFieldPaths);
|
|
161
|
+
const defaultSelectedFieldPaths = fieldGroups.flatMap(
|
|
162
|
+
(group) => group.fields.map((field) => field.path)
|
|
163
|
+
);
|
|
164
|
+
assert(
|
|
165
|
+
defaultSelectedFieldPaths.length > 0,
|
|
166
|
+
"[generator-workbench] exportTemplate requires at least one panel field path"
|
|
167
|
+
);
|
|
168
|
+
return {
|
|
169
|
+
generatorId: resolveGeneratorId(panelSchema, state),
|
|
170
|
+
state,
|
|
171
|
+
panelSchema,
|
|
172
|
+
fieldGroups,
|
|
173
|
+
selectedFieldPaths: defaultSelectedFieldPaths
|
|
174
|
+
};
|
|
175
|
+
}
|
|
132
176
|
return {
|
|
133
177
|
async importTemplate(file) {
|
|
134
178
|
const text = await file.text();
|
|
@@ -144,21 +188,21 @@ function createTemplateController(args) {
|
|
|
144
188
|
panelFilter: capturedPanelFilter
|
|
145
189
|
};
|
|
146
190
|
},
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const
|
|
191
|
+
prepareTemplateExport,
|
|
192
|
+
async exportTemplate(selectedFieldPaths) {
|
|
193
|
+
var _a;
|
|
194
|
+
const prepared = prepareTemplateExport();
|
|
195
|
+
const finalSelectedFieldPaths = (selectedFieldPaths == null ? void 0 : selectedFieldPaths.length) ? collectPanelFieldPaths(prepared.panelSchema, selectedFieldPaths) : prepared.selectedFieldPaths;
|
|
152
196
|
assert(
|
|
153
|
-
|
|
197
|
+
finalSelectedFieldPaths.length > 0,
|
|
154
198
|
"[generator-workbench] exportTemplate requires at least one panel field path"
|
|
155
199
|
);
|
|
156
200
|
const template = sdk.template.build({
|
|
157
|
-
generatorId:
|
|
158
|
-
state,
|
|
159
|
-
panelSchema,
|
|
160
|
-
selectedFieldPaths,
|
|
161
|
-
templateMeta: (
|
|
201
|
+
generatorId: prepared.generatorId,
|
|
202
|
+
state: prepared.state,
|
|
203
|
+
panelSchema: prepared.panelSchema,
|
|
204
|
+
selectedFieldPaths: finalSelectedFieldPaths,
|
|
205
|
+
templateMeta: (_a = config.getTemplateMeta) == null ? void 0 : _a.call(config)
|
|
162
206
|
});
|
|
163
207
|
sdk.template.download(template);
|
|
164
208
|
return { template };
|
|
@@ -195,6 +239,26 @@ function dispatchWorkbenchEvent(target, name, detail) {
|
|
|
195
239
|
})
|
|
196
240
|
);
|
|
197
241
|
}
|
|
242
|
+
function ensureBrowserProcessEnv() {
|
|
243
|
+
const browserGlobal = globalThis;
|
|
244
|
+
if (!browserGlobal.process) {
|
|
245
|
+
browserGlobal.process = {
|
|
246
|
+
env: {
|
|
247
|
+
NODE_ENV: "production"
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!browserGlobal.process.env) {
|
|
253
|
+
browserGlobal.process.env = {
|
|
254
|
+
NODE_ENV: "production"
|
|
255
|
+
};
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (!browserGlobal.process.env.NODE_ENV) {
|
|
259
|
+
browserGlobal.process.env.NODE_ENV = "production";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
198
262
|
function queryRequired(root, selector) {
|
|
199
263
|
const node = root.querySelector(selector);
|
|
200
264
|
if (!node) {
|
|
@@ -208,7 +272,7 @@ function collectWorkbenchRefs(root) {
|
|
|
208
272
|
return {
|
|
209
273
|
root,
|
|
210
274
|
workspace: queryRequired(root, '[data-role="workspace"]'),
|
|
211
|
-
logoArea:
|
|
275
|
+
logoArea: root.querySelector('[data-role="logo-area"]'),
|
|
212
276
|
canvasHost: queryRequired(root, '[data-role="canvas-host"]'),
|
|
213
277
|
panelHost: queryRequired(root, '[data-role="panel-host"]'),
|
|
214
278
|
templateFileInput: queryRequired(root, '[data-role="template-file-input"]')
|
|
@@ -597,10 +661,63 @@ const WORKBENCH_SHELL_STYLES = `
|
|
|
597
661
|
.workspace.panel-left .panel-sidebar {
|
|
598
662
|
border-right: 1px solid var(--border-default);
|
|
599
663
|
}
|
|
664
|
+
|
|
665
|
+
.template-export-modal {
|
|
666
|
+
display: grid;
|
|
667
|
+
gap: 16px;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
.template-export-groups {
|
|
673
|
+
display: grid;
|
|
674
|
+
gap: 12px;
|
|
675
|
+
max-height: 360px;
|
|
676
|
+
overflow: auto;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.template-export-group {
|
|
680
|
+
display: grid;
|
|
681
|
+
gap: 10px;
|
|
682
|
+
padding: 14px 16px;
|
|
683
|
+
border: 1px solid var(--border-default);
|
|
684
|
+
border-radius: 12px;
|
|
685
|
+
background: #ffffff;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.template-export-group-title {
|
|
689
|
+
font-size: 13px;
|
|
690
|
+
font-weight: 700;
|
|
691
|
+
color: var(--text-primary);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.template-export-field-list {
|
|
695
|
+
display: grid;
|
|
696
|
+
gap: 10px;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.template-export-field {
|
|
700
|
+
display: grid;
|
|
701
|
+
gap: 4px;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.template-export-field-path {
|
|
705
|
+
padding-left: 28px;
|
|
706
|
+
font-size: 12px;
|
|
707
|
+
color: var(--text-muted);
|
|
708
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.template-export-footer {
|
|
712
|
+
display: flex;
|
|
713
|
+
justify-content: flex-end;
|
|
714
|
+
gap: 12px;
|
|
715
|
+
padding: 8px 24px;
|
|
716
|
+
}
|
|
600
717
|
`;
|
|
601
718
|
const WORKBENCH_VUE_TEMPLATE = `
|
|
602
719
|
<div class="shell">
|
|
603
|
-
<header class="topbar app-topbar">
|
|
720
|
+
<header v-if="state.shellMode !== 'template'" class="topbar app-topbar">
|
|
604
721
|
<div class="logo-area app-topbar-main" data-role="logo-area">
|
|
605
722
|
<img
|
|
606
723
|
:src="state.logoSrc"
|
|
@@ -793,6 +910,61 @@ const WORKBENCH_VUE_TEMPLATE = `
|
|
|
793
910
|
style="display:none"
|
|
794
911
|
@change="callbacks.onFileChange($event)"
|
|
795
912
|
/>
|
|
913
|
+
|
|
914
|
+
<xt-modal
|
|
915
|
+
:model-value="state.templateDialogOpen"
|
|
916
|
+
title="发布模板"
|
|
917
|
+
:show-footer="false"
|
|
918
|
+
:portal-disabled="true"
|
|
919
|
+
@update:model-value="callbacks.onToggleTemplateDialog($event)"
|
|
920
|
+
>
|
|
921
|
+
<div
|
|
922
|
+
v-if="state.templateDialogOpen"
|
|
923
|
+
class="template-export-modal"
|
|
924
|
+
data-role="template-export-modal"
|
|
925
|
+
>
|
|
926
|
+
|
|
927
|
+
<div class="template-export-groups">
|
|
928
|
+
<div
|
|
929
|
+
v-for="group in state.templateFieldGroups"
|
|
930
|
+
:key="group.id"
|
|
931
|
+
class="template-export-group"
|
|
932
|
+
>
|
|
933
|
+
<div class="template-export-group-title">{{ group.title }}</div>
|
|
934
|
+
<div class="template-export-field-list">
|
|
935
|
+
<div
|
|
936
|
+
v-for="field in group.fields"
|
|
937
|
+
:key="field.path"
|
|
938
|
+
class="template-export-field"
|
|
939
|
+
data-role="template-export-field"
|
|
940
|
+
>
|
|
941
|
+
<xt-checkbox
|
|
942
|
+
:model-value="state.templateSelectedFieldPaths.includes(field.path)"
|
|
943
|
+
:disabled="state.templateExportLoading"
|
|
944
|
+
@update:model-value="callbacks.onToggleTemplateField(field.path, $event)"
|
|
945
|
+
>{{ field.label }}</xt-checkbox>
|
|
946
|
+
<div class="template-export-field-path">{{ field.path }}</div>
|
|
947
|
+
</div>
|
|
948
|
+
</div>
|
|
949
|
+
</div>
|
|
950
|
+
</div>
|
|
951
|
+
|
|
952
|
+
<div class="template-export-footer">
|
|
953
|
+
<xt-button
|
|
954
|
+
type="secondary"
|
|
955
|
+
:disabled="state.templateExportLoading"
|
|
956
|
+
@click="callbacks.onCloseTemplateDialog()"
|
|
957
|
+
>取消</xt-button>
|
|
958
|
+
<xt-button
|
|
959
|
+
type="primary"
|
|
960
|
+
data-role="template-export-confirm"
|
|
961
|
+
:loading="state.templateExportLoading"
|
|
962
|
+
:disabled="state.templateSelectedFieldPaths.length === 0"
|
|
963
|
+
@click="callbacks.onConfirmTemplateExport()"
|
|
964
|
+
>发布模板</xt-button>
|
|
965
|
+
</div>
|
|
966
|
+
</div>
|
|
967
|
+
</xt-modal>
|
|
796
968
|
</div>
|
|
797
969
|
`;
|
|
798
970
|
function requireVue() {
|
|
@@ -815,6 +987,7 @@ function injectStyles(root, cdnCssUrl) {
|
|
|
815
987
|
}
|
|
816
988
|
function stateFromConfig(vue, config) {
|
|
817
989
|
return vue.reactive({
|
|
990
|
+
shellMode: config.mode || "full",
|
|
818
991
|
logoText: config.logoText || config.title,
|
|
819
992
|
logoSrc: config.logoUrl || "https://storage-us.atomm.com/resource/xart/static/agent/imgs/atomm-logo.svg",
|
|
820
993
|
panelTarget: config.panelTarget || "right",
|
|
@@ -829,7 +1002,11 @@ function stateFromConfig(vue, config) {
|
|
|
829
1002
|
authSubline: "",
|
|
830
1003
|
creditsBalance: 0,
|
|
831
1004
|
exportCreditsCost: 1,
|
|
832
|
-
loginLoading: false
|
|
1005
|
+
loginLoading: false,
|
|
1006
|
+
templateDialogOpen: false,
|
|
1007
|
+
templateExportLoading: false,
|
|
1008
|
+
templateSelectedFieldPaths: [],
|
|
1009
|
+
templateFieldGroups: []
|
|
833
1010
|
});
|
|
834
1011
|
}
|
|
835
1012
|
function createNoopCallbacks() {
|
|
@@ -842,6 +1019,14 @@ function createNoopCallbacks() {
|
|
|
842
1019
|
},
|
|
843
1020
|
onExportTemplate: () => {
|
|
844
1021
|
},
|
|
1022
|
+
onCloseTemplateDialog: () => {
|
|
1023
|
+
},
|
|
1024
|
+
onToggleTemplateDialog: () => {
|
|
1025
|
+
},
|
|
1026
|
+
onToggleTemplateField: () => {
|
|
1027
|
+
},
|
|
1028
|
+
onConfirmTemplateExport: () => {
|
|
1029
|
+
},
|
|
845
1030
|
onExportSvg: () => {
|
|
846
1031
|
},
|
|
847
1032
|
onOpenInStudio: () => {
|
|
@@ -867,9 +1052,44 @@ function registerFallbackComponents(app) {
|
|
|
867
1052
|
inheritAttrs: true,
|
|
868
1053
|
template: '<div v-bind="$attrs"><slot name="trigger" /><slot name="content" /></div>'
|
|
869
1054
|
});
|
|
1055
|
+
app.component("xt-modal", {
|
|
1056
|
+
inheritAttrs: false,
|
|
1057
|
+
props: {
|
|
1058
|
+
modelValue: { type: Boolean, default: false },
|
|
1059
|
+
title: { type: String, default: "" }
|
|
1060
|
+
},
|
|
1061
|
+
emits: ["update:modelValue"],
|
|
1062
|
+
template: `
|
|
1063
|
+
<div v-if="modelValue" v-bind="$attrs">
|
|
1064
|
+
<div>{{ title }}</div>
|
|
1065
|
+
<slot />
|
|
1066
|
+
</div>
|
|
1067
|
+
`
|
|
1068
|
+
});
|
|
1069
|
+
app.component("xt-checkbox", {
|
|
1070
|
+
inheritAttrs: false,
|
|
1071
|
+
props: {
|
|
1072
|
+
modelValue: { type: Boolean, default: false },
|
|
1073
|
+
label: { type: String, default: "" },
|
|
1074
|
+
disabled: { type: Boolean, default: false }
|
|
1075
|
+
},
|
|
1076
|
+
emits: ["update:modelValue", "change"],
|
|
1077
|
+
template: `
|
|
1078
|
+
<label v-bind="$attrs">
|
|
1079
|
+
<input
|
|
1080
|
+
type="checkbox"
|
|
1081
|
+
:checked="modelValue"
|
|
1082
|
+
:disabled="disabled"
|
|
1083
|
+
@change="$emit('update:modelValue', $event.target.checked)"
|
|
1084
|
+
/>
|
|
1085
|
+
<span><slot>{{ label }}</slot></span>
|
|
1086
|
+
</label>
|
|
1087
|
+
`
|
|
1088
|
+
});
|
|
870
1089
|
}
|
|
871
1090
|
function renderWorkbenchShell(root, config) {
|
|
872
1091
|
const vue = requireVue();
|
|
1092
|
+
ensureBrowserProcessEnv();
|
|
873
1093
|
injectStyles(root, config.atommUiCssUrl);
|
|
874
1094
|
const state = stateFromConfig(vue, config);
|
|
875
1095
|
const callbacks = createNoopCallbacks();
|
|
@@ -897,6 +1117,7 @@ function unmountWorkbenchShell(context) {
|
|
|
897
1117
|
}
|
|
898
1118
|
const DEFAULT_CONFIG = {
|
|
899
1119
|
title: "",
|
|
1120
|
+
mode: "full",
|
|
900
1121
|
templateEnabled: true,
|
|
901
1122
|
exportEnabled: true,
|
|
902
1123
|
studioEnabled: true,
|
|
@@ -904,6 +1125,7 @@ const DEFAULT_CONFIG = {
|
|
|
904
1125
|
panelTarget: "right",
|
|
905
1126
|
avatarMenuTrigger: "hover"
|
|
906
1127
|
};
|
|
1128
|
+
const TOKEN_STORAGE_KEY_PREFIX = "__atomm_sdk_token__";
|
|
907
1129
|
function normalizeWorkbenchConfig(config) {
|
|
908
1130
|
return {
|
|
909
1131
|
...DEFAULT_CONFIG,
|
|
@@ -1022,11 +1244,28 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1022
1244
|
}
|
|
1023
1245
|
}
|
|
1024
1246
|
async exportTemplate() {
|
|
1247
|
+
return this.exportTemplateWithFields();
|
|
1248
|
+
}
|
|
1249
|
+
async exportTemplateWithFields(selectedFieldPaths) {
|
|
1025
1250
|
try {
|
|
1026
|
-
|
|
1251
|
+
this._state.busy.exportTemplate = true;
|
|
1252
|
+
if (this._shellContext) {
|
|
1253
|
+
this._shellContext.state.templateExportLoading = true;
|
|
1254
|
+
}
|
|
1255
|
+
const result = await this.requireTemplateController().exportTemplate(
|
|
1256
|
+
selectedFieldPaths
|
|
1257
|
+
);
|
|
1027
1258
|
dispatchWorkbenchEvent(this, "template-exported", result);
|
|
1259
|
+
if (selectedFieldPaths) {
|
|
1260
|
+
this.closeTemplateExportDialog();
|
|
1261
|
+
}
|
|
1028
1262
|
} catch (error) {
|
|
1029
1263
|
this.handleError("template", error);
|
|
1264
|
+
} finally {
|
|
1265
|
+
this._state.busy.exportTemplate = false;
|
|
1266
|
+
if (this._shellContext) {
|
|
1267
|
+
this._shellContext.state.templateExportLoading = false;
|
|
1268
|
+
}
|
|
1030
1269
|
}
|
|
1031
1270
|
}
|
|
1032
1271
|
async exportSvg() {
|
|
@@ -1045,6 +1284,28 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1045
1284
|
this.handleError("export", error);
|
|
1046
1285
|
}
|
|
1047
1286
|
}
|
|
1287
|
+
async setAuthToken(token) {
|
|
1288
|
+
var _a, _b;
|
|
1289
|
+
const normalizedToken = token.trim();
|
|
1290
|
+
if (!normalizedToken) {
|
|
1291
|
+
throw new Error("[generator-workbench] token is required");
|
|
1292
|
+
}
|
|
1293
|
+
if (!this._sdk) {
|
|
1294
|
+
throw new Error("[generator-workbench] sdk is required before setAuthToken()");
|
|
1295
|
+
}
|
|
1296
|
+
const appKey = (_b = (_a = this._sdk).getAppKey) == null ? void 0 : _b.call(_a);
|
|
1297
|
+
if (!appKey) {
|
|
1298
|
+
throw new Error("[generator-workbench] sdk.getAppKey() is required for setAuthToken()");
|
|
1299
|
+
}
|
|
1300
|
+
if (typeof this._sdk.auth.syncToken !== "function") {
|
|
1301
|
+
throw new Error("[generator-workbench] sdk.auth.syncToken() is required for setAuthToken()");
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
localStorage.setItem(`${TOKEN_STORAGE_KEY_PREFIX}${appKey}`, normalizedToken);
|
|
1305
|
+
} catch {
|
|
1306
|
+
}
|
|
1307
|
+
await this._sdk.auth.syncToken(normalizedToken);
|
|
1308
|
+
}
|
|
1048
1309
|
render() {
|
|
1049
1310
|
if (!this.shadowRoot) return;
|
|
1050
1311
|
if (this._shellContext) {
|
|
@@ -1086,7 +1347,24 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1086
1347
|
this.requireRefs().templateFileInput.click();
|
|
1087
1348
|
};
|
|
1088
1349
|
callbacks.onExportTemplate = () => {
|
|
1089
|
-
|
|
1350
|
+
this.openTemplateExportDialog();
|
|
1351
|
+
};
|
|
1352
|
+
callbacks.onCloseTemplateDialog = () => {
|
|
1353
|
+
this.closeTemplateExportDialog();
|
|
1354
|
+
};
|
|
1355
|
+
callbacks.onToggleTemplateDialog = (open) => {
|
|
1356
|
+
if (!open) {
|
|
1357
|
+
this.closeTemplateExportDialog();
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
callbacks.onToggleTemplateField = (path, checked) => {
|
|
1361
|
+
this.toggleTemplateExportField(path, checked);
|
|
1362
|
+
};
|
|
1363
|
+
callbacks.onConfirmTemplateExport = () => {
|
|
1364
|
+
var _a;
|
|
1365
|
+
void this.exportTemplateWithFields(
|
|
1366
|
+
((_a = this._shellContext) == null ? void 0 : _a.state.templateSelectedFieldPaths) || []
|
|
1367
|
+
);
|
|
1090
1368
|
};
|
|
1091
1369
|
callbacks.onExportSvg = () => {
|
|
1092
1370
|
void this.exportSvg();
|
|
@@ -1103,6 +1381,40 @@ class GeneratorWorkbenchElement extends HTMLElement {
|
|
|
1103
1381
|
input.value = "";
|
|
1104
1382
|
};
|
|
1105
1383
|
}
|
|
1384
|
+
openTemplateExportDialog() {
|
|
1385
|
+
try {
|
|
1386
|
+
if (!this._shellContext) return;
|
|
1387
|
+
const prepared = this.requireTemplateController().prepareTemplateExport();
|
|
1388
|
+
this._shellContext.state.templateFieldGroups = prepared.fieldGroups.map(
|
|
1389
|
+
(group) => ({
|
|
1390
|
+
...group,
|
|
1391
|
+
fields: group.fields.map((field) => ({ ...field }))
|
|
1392
|
+
})
|
|
1393
|
+
);
|
|
1394
|
+
this._shellContext.state.templateSelectedFieldPaths = [
|
|
1395
|
+
...prepared.selectedFieldPaths
|
|
1396
|
+
];
|
|
1397
|
+
this._shellContext.state.templateDialogOpen = true;
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
this.handleError("template", error);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
closeTemplateExportDialog() {
|
|
1403
|
+
if (!this._shellContext) return;
|
|
1404
|
+
this._shellContext.state.templateDialogOpen = false;
|
|
1405
|
+
this._shellContext.state.templateFieldGroups = [];
|
|
1406
|
+
this._shellContext.state.templateSelectedFieldPaths = [];
|
|
1407
|
+
}
|
|
1408
|
+
toggleTemplateExportField(path, checked) {
|
|
1409
|
+
if (!this._shellContext) return;
|
|
1410
|
+
const selected = new Set(this._shellContext.state.templateSelectedFieldPaths);
|
|
1411
|
+
if (checked) {
|
|
1412
|
+
selected.add(path);
|
|
1413
|
+
} else {
|
|
1414
|
+
selected.delete(path);
|
|
1415
|
+
}
|
|
1416
|
+
this._shellContext.state.templateSelectedFieldPaths = Array.from(selected);
|
|
1417
|
+
}
|
|
1106
1418
|
bindAuthState() {
|
|
1107
1419
|
var _a;
|
|
1108
1420
|
const controller = this.requireAuthController();
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).GeneratorWorkbench={})}(this,function(t){"use strict";var n=Object.defineProperty,e=(t,e,a)=>((t,e,a)=>e in t?n(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a)(t,"symbol"!=typeof e?e+"":e,a);function a(t,n){var e;if(null==(e=t.generatorId)?void 0:e.trim())return t.generatorId.trim();const a=n.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 r(t){const{sdk:n,runtime:e,config:r}=t;return{async importTemplate(t){const a=await t.text(),r=n.template.parse(a);let i;return await n.template.applyToRuntime(e,r,{onPanelFilter(t){i=t}}),{template:r,panelFilter:i}},async exportTemplate(){var t,i;const o=e.getState(),l=e.getPanelSchema(),s=(null==(t=r.getTemplateFieldPaths)?void 0:t.call(r,l))??function(t){return t.groups.flatMap(t=>t.fields.map(t=>{var n,e;return null==(e=null==(n=t.bind)?void 0:n.path)?void 0:e.trim()}).filter(t=>Boolean(t)))}(l);!function(t,n){if(!t)throw new Error(n)}(s.length>0,"[generator-workbench] exportTemplate requires at least one panel field path");const p=n.template.build({generatorId:a(l,o),state:o,panelSchema:l,selectedFieldPaths:s,templateMeta:null==(i=r.getTemplateMeta)?void 0:i.call(r)});return n.template.download(p),{template:p}}}}function i(t,n,e){return t.dispatchEvent(new CustomEvent(n,{detail:e,bubbles:!0,composed:!0}))}function o(t,n){const e=t.querySelector(n);if(!e)throw new Error(`[generator-workbench] required DOM node not found: ${n}`);return e}const l="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",s=`\n <div class="shell">\n <header 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="${l}" 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="${l}" 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 </div>\n`;function p(t,n){const e=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(t,n){const e=document.createElement("link");e.rel="stylesheet",e.href=n||"https://minio-download.makeblock.com/resource/atomm-ui-umd/dist-browser/atomm-ui.min.css",t.appendChild(e);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',t.appendChild(a)}(t,n.atommUiCssUrl);const a=function(t,n){return t.reactive({logoText:n.logoText||n.title,logoSrc:n.logoUrl||"https://storage-us.atomm.com/resource/xart/static/agent/imgs/atomm-logo.svg",panelTarget:n.panelTarget||"right",avatarMenuTrigger:n.avatarMenuTrigger||"hover",templateEnabled:!1!==n.templateEnabled,exportEnabled:!1!==n.exportEnabled,studioEnabled:!1!==n.studioEnabled,isLogin:!1,avatarSrc:"",avatarText:"U",authDisplayName:"",authSubline:"",creditsBalance:0,exportCreditsCost:1,loginLoading:!1})}(e,n),r={onLogin:()=>{},onLogout:()=>{},onImportTemplate:()=>{},onExportTemplate:()=>{},onExportSvg:()=>{},onOpenInStudio:()=>{},onFileChange:()=>{}},i=document.createElement("div");i.setAttribute("data-workbench-vue-root",""),t.appendChild(i);const l=globalThis.AtommUI,p=e.createApp({setup:()=>({state:a,callbacks:r}),template:s});l?p.use(l):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>'})}(p),p.mount(i);const d=function(t){return{root:t,workspace:o(t,'[data-role="workspace"]'),logoArea:o(t,'[data-role="logo-area"]'),canvasHost:o(t,'[data-role="canvas-host"]'),panelHost:o(t,'[data-role="panel-host"]'),templateFileInput:o(t,'[data-role="template-file-input"]')}}(t);return{state:a,callbacks:r,app:p,refs:d}}function d(t){t.app.unmount()}const c={title:"",templateEnabled:!0,exportEnabled:!0,studioEnabled:!0,autoMount:!0,panelTarget:"right",avatarMenuTrigger:"hover"};class u extends HTMLElement{constructor(){super(),e(this,"_sdk",null),e(this,"_runtime",null),e(this,"_config",{...c}),e(this,"_mounted",!1),e(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}}),e(this,"_cleanupAuth"),e(this,"_cleanupCredits"),e(this,"_cleanupBilling"),e(this,"_authController"),e(this,"_templateController"),e(this,"_exportController"),e(this,"_runtimeController"),e(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 n;this._config=(n=t,{...c,...n}),this._mounted&&this.render()}connectedCallback(){!1!==this._config.autoMount&&!this._mounted&&this._sdk&&this._runtime&&this.mount()}disconnectedCallback(){this.unmount()}async mount(){var 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()");this.render(),this.bindControllers(),this.bindShellCallbacks(),this.bindAuthState(),await this.syncBillingState();const e=this.requireRefs();await(null==(t=this._runtimeController)?void 0:t.mountCanvas(e.canvasHost)),await(null==(n=this._runtimeController)?void 0:n.mountPanel(e.panelHost,this._state.activePanelFilter)),this._mounted=!0,i(this,"workbench-ready",{sdk:this._sdk,runtime:this._runtime})}async unmount(){var t,n,e,a;null==(t=this._cleanupAuth)||t.call(this),this._cleanupAuth=void 0,null==(n=this._cleanupCredits)||n.call(this),this._cleanupCredits=void 0,null==(e=this._cleanupBilling)||e.call(this),this._cleanupBilling=void 0,await(null==(a=this._runtimeController)?void 0:a.unmountAll()),this._shellContext&&(d(this._shellContext),this._shellContext=void 0),this._mounted=!1}refreshLayout(){}async importTemplate(t){try{const n=await this.requireTemplateController().importTemplate(t);this._state.activePanelFilter=n.panelFilter;const e=this.requireRefs();this._runtimeController&&await this._runtimeController.remountPanel(e.panelHost,n.panelFilter),i(this,"template-imported",n)}catch(n){this.handleError("template",n)}}async exportTemplate(){try{i(this,"template-exported",await this.requireTemplateController().exportTemplate())}catch(t){this.handleError("template",t)}}async exportSvg(){try{i(this,"svg-export",await this.requireExportController().exportSvg())}catch(t){this.handleError("export",t)}}async openInStudio(){try{i(this,"studio-open",await this.requireExportController().openInStudio())}catch(t){this.handleError("export",t)}}render(){this.shadowRoot&&(this._shellContext&&(d(this._shellContext),this.shadowRoot.innerHTML=""),this._shellContext=p(this.shadowRoot,this._config),this.syncAuthUI(),this.syncBillingUI())}bindControllers(){this._sdk&&this._runtime&&(this._authController=function(t){const{sdk:n}=t;return{getStatus:()=>n.auth.getStatus(),subscribe:t=>(t(n.auth.getStatus()),n.auth.onChange(t)),async login(){await n.auth.login()},async logout(){await n.auth.logout()}}}({sdk:this._sdk}),this._templateController=r({sdk:this._sdk,runtime:this._runtime,config:this._config}),this._exportController=function(t){const{sdk:n,runtime:e,config:a,element:r}=t;async function i(){if(!n.auth.getStatus().isLogin&&(await n.auth.login(),!n.auth.getStatus().isLogin))throw new Error("[generator-workbench] login is required before export")}async function o(){var t;(null==(t=n.billing)?void 0:t.consume)&&await n.billing.consume()}return{async exportSvg(){var t;return await(null==(t=a.beforeExportSvg)?void 0:t.call(a,{sdk:n,runtime:e,element:r})),await i(),await o(),n.export.download({format:"svg"})},async openInStudio(){var t;return await(null==(t=a.beforeOpenInStudio)?void 0:t.call(a,{sdk:n,runtime:e,element:r})),await i(),await o(),n.export.openInStudio({format:"svg"})}}}({sdk:this._sdk,runtime:this._runtime,config:this._config,element:this}),this._runtimeController=function(t){const{runtime:n}=t;let e=null,a=null;async function r(t,e){null==a||a.unmount(),a=await Promise.resolve(n.mount({mode:"embed",target:"panel",container:t,panelFilter:e}))}return{mountCanvas:async function(t){null==e||e.unmount(),e=await Promise.resolve(n.mount({mode:"embed",target:"canvas",container:t}))},mountPanel:r,async remountPanel(t,n){await r(t,n)},async unmountAll(){null==e||e.unmount(),null==a||a.unmount(),e=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.exportTemplate()},t.onExportSvg=()=>{this.exportSvg()},t.onOpenInStudio=()=>{this.openInStudio()},t.onFileChange=t=>{var n;const e=t.target,a=null==(n=e.files)?void 0:n[0];a&&(this.importTemplate(a),e.value="")}}bindAuthState(){var t;const n=this.requireAuthController();null==(t=this._cleanupAuth)||t.call(this),this._cleanupAuth=n.subscribe(t=>{this._state.authStatus=t,this.syncAuthUI(),this.syncBillingState(t),i(this,"auth-change",t)}),this.bindBillingSubscriptions()}bindBillingSubscriptions(){var t,n,e,a;null==(t=this._cleanupCredits)||t.call(this),this._cleanupCredits=void 0,null==(n=this._cleanupBilling)||n.call(this),this._cleanupBilling=void 0;const r=null==(e=this._sdk)?void 0:e.credits;"function"==typeof(null==r?void 0:r.onChange)&&(this._cleanupCredits=r.onChange(t=>{this._state.creditsBalance=Number(t)||0,this.syncBillingUI()}));const i=null==(a=this._sdk)?void 0:a.billing;"function"==typeof(null==i?void 0:i.onChange)&&(this._cleanupBilling=i.onChange(t=>{this.applyBillingUsage(t)}))}syncAuthUI(){var t,n,e,a;if(!this._shellContext)return;const{state:r}=this._shellContext,{isLogin:i,userInfo:o}=this._state.authStatus,l=(null==(t=null==o?void 0:o.userName)?void 0:t.trim())||(null==(n=null==o?void 0:o.email)?void 0:n.trim())||(null==(e=null==o?void 0:o.phoneNumber)?void 0:e.trim())||"",s=l?l.charAt(0).toUpperCase():"U",p=(null==(a=null==o?void 0:o.email)?void 0:a.trim())||((null==o?void 0:o.phoneNumber)?`${o.phoneZone||""}${o.phoneNumber}`:"Connected to Generator SDK");r.isLogin=i,r.avatarSrc=i&&(null==o?void 0:o.headpic)||"",r.avatarText=s,r.authDisplayName=l,r.authSubline=i?p:""}syncBillingUI(){if(!this._shellContext)return;const{state:t}=this._shellContext,n=this._state.billingUsage;t.creditsBalance=this._state.creditsBalance,t.exportCreditsCost=(null==n?void 0:n.creditsPerUse)??(n&&"unitPrice"in n&&Number(n.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 n,e,a,r,i,o,l,s;if(!t.isLogin||!this._sdk)return void this.resetBillingState();const p=null==(e=null==(n=this._sdk.credits)?void 0:n.getCachedBalance)?void 0:e.call(n);"number"==typeof p&&(this._state.creditsBalance=p);const d=null==(r=null==(a=this._sdk.billing)?void 0:a.getCachedUsage)?void 0:r.call(a);d?this.applyBillingUsage(d):this.syncBillingUI();try{const[t,n]=await Promise.all([null==(o=null==(i=this._sdk.credits)?void 0:i.getBalance)?void 0:o.call(i),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),n)return void this.applyBillingUsage(n);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,n){var e,a;const r=n instanceof Error?n:new Error(String(n));null==(a=(e=this._config).onError)||a.call(e,r,t),i(this,"workbench-error",{source:t,error:r})}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 h="generator-workbench";t.GENERATOR_WORKBENCH_TAG_NAME=h,t.GeneratorWorkbenchElement=u,t.defineGeneratorWorkbench=function(t=h){const n=customElements.get(t);return n||(customElements.define(t,u),u)},Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
|
|
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"})});
|