@chengxianglibra/dsh-data-analysis 0.1.1 → 0.1.2

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.
Files changed (195) hide show
  1. package/README.md +83 -34
  2. package/lib/bin/environment.js +1 -1
  3. package/lib/bin/presentation-lint.js +21 -0
  4. package/lib/bridges.js +2 -2
  5. package/lib/client/credentials/create-datasource.js +67 -0
  6. package/lib/client/credentials/install.js +191 -0
  7. package/lib/client/credentials/model.js +494 -0
  8. package/lib/client/credentials/styles.js +17 -0
  9. package/lib/client/workspace-header-action.js +11 -0
  10. package/lib/client.js +1133 -736
  11. package/lib/compatibility.js +1 -1
  12. package/lib/datasource/authoring.js +70 -0
  13. package/lib/datasource/bridge-programs.js +30 -18
  14. package/lib/datasource/bridge.js +100 -8
  15. package/lib/datasource/index.js +3 -2
  16. package/lib/datasource/python.js +115 -0
  17. package/lib/datasource/resolver-program.js +112 -0
  18. package/lib/datasource/rpc.js +88 -0
  19. package/lib/datasource/service.js +687 -0
  20. package/lib/datasource/shell-env.js +2 -333
  21. package/lib/datasource/test.js +24 -51
  22. package/lib/environment/binding.js +54 -9
  23. package/lib/environment/runtime.js +89 -78
  24. package/lib/environment/subprocess.js +6 -1
  25. package/lib/environment/summary.js +3 -3
  26. package/lib/environment/workspace.js +5 -0
  27. package/lib/index.js +1 -1
  28. package/lib/plugin.js +168 -51
  29. package/lib/presentation/assets/portable.js +606 -0
  30. package/lib/presentation/assets/static.js +945 -0
  31. package/lib/presentation/build/index.js +71 -0
  32. package/lib/presentation/commit.js +156 -0
  33. package/lib/presentation/contracts/charts.js +476 -0
  34. package/lib/presentation/contracts/editing.js +58 -0
  35. package/lib/presentation/contracts/index.js +651 -0
  36. package/lib/presentation/contracts/interaction.js +62 -0
  37. package/lib/presentation/contracts/types.js +15 -0
  38. package/lib/presentation/contracts/values.js +28 -0
  39. package/lib/presentation/delivery.js +56 -0
  40. package/lib/presentation/files.js +83 -0
  41. package/lib/presentation/index.js +6 -0
  42. package/lib/presentation/lint.js +46 -0
  43. package/lib/presentation/projection/files.js +85 -0
  44. package/lib/presentation/projection/index.js +195 -0
  45. package/lib/presentation/projection/program.js +144 -0
  46. package/lib/presentation/projection/sql-code.js +79 -0
  47. package/lib/presentation/receipt.js +34 -0
  48. package/lib/presentation/reports.js +122 -0
  49. package/lib/presentation/rpc.js +148 -0
  50. package/lib/presentation/tool.js +122 -0
  51. package/lib/python-execution-contracts.js +1 -0
  52. package/lib/python-execution.js +232 -0
  53. package/lib/semantic-browser/contracts.js +80 -0
  54. package/lib/semantic-browser/definition.js +64 -0
  55. package/lib/semantic-browser/program.js +119 -0
  56. package/lib/semantic-browser/service.js +72 -0
  57. package/lib/semantic-reference/bridge.js +118 -0
  58. package/lib/semantic-reference/contracts.js +113 -0
  59. package/lib/semantic-reference/index.js +3 -0
  60. package/lib/semantic-reference/rpc.js +122 -0
  61. package/lib/semantic-reference/search.js +94 -0
  62. package/lib/semantic-reference/usage.js +143 -0
  63. package/lib/types/bridges.d.ts +2 -2
  64. package/lib/types/client/credentials/create-datasource.d.ts +5 -0
  65. package/lib/types/client/credentials/install.d.ts +1 -0
  66. package/lib/types/client/credentials/model.d.ts +59 -0
  67. package/lib/types/client/credentials/styles.d.ts +1 -0
  68. package/lib/types/client/presentation/ask-dsh.d.ts +9 -0
  69. package/lib/types/client/presentation/chart-explorer.d.ts +12 -0
  70. package/lib/types/client/presentation/chart-geometry.d.ts +30 -0
  71. package/lib/types/client/presentation/chart-renderer.d.ts +10 -0
  72. package/lib/types/client/presentation/chart-specials.d.ts +10 -0
  73. package/lib/types/client/presentation/chart-styles.d.ts +1 -0
  74. package/lib/types/client/presentation/chart-tooltip.d.ts +13 -0
  75. package/lib/types/client/presentation/chart-view.d.ts +23 -0
  76. package/lib/types/client/presentation/copy-context.d.ts +5 -0
  77. package/lib/types/client/presentation/delivery-model.d.ts +54 -0
  78. package/lib/types/client/presentation/delivery.d.ts +77 -0
  79. package/lib/types/client/presentation/editor-controls.d.ts +12 -0
  80. package/lib/types/client/presentation/global-filters.d.ts +6 -0
  81. package/lib/types/client/presentation/host-entry.d.ts +12 -0
  82. package/lib/types/client/presentation/icons.d.ts +4 -0
  83. package/lib/types/client/presentation/install.d.ts +15 -0
  84. package/lib/types/client/presentation/markdown.d.ts +5 -0
  85. package/lib/types/client/presentation/model.d.ts +62 -0
  86. package/lib/types/client/presentation/reader.d.ts +11 -0
  87. package/lib/types/client/presentation/source-code-model.d.ts +16 -0
  88. package/lib/types/client/presentation/source-code.d.ts +11 -0
  89. package/lib/types/client/presentation/source-dialog.d.ts +13 -0
  90. package/lib/types/client/presentation/source-facts.d.ts +20 -0
  91. package/lib/types/client/presentation/source-format.d.ts +5 -0
  92. package/lib/types/client/presentation/source-highlight.d.ts +7 -0
  93. package/lib/types/client/presentation/sources.d.ts +13 -0
  94. package/lib/types/client/presentation/styles.d.ts +1 -0
  95. package/lib/types/client/presentation/table.d.ts +12 -0
  96. package/lib/types/client/presentation/use-element-width.d.ts +5 -0
  97. package/lib/types/client/workspace-header-action.d.ts +7 -0
  98. package/lib/types/client.d.ts +4 -122
  99. package/lib/types/compatibility.d.ts +2 -2
  100. package/lib/types/datasource/authoring.d.ts +25 -0
  101. package/lib/types/datasource/bridge.d.ts +21 -3
  102. package/lib/types/datasource/index.d.ts +3 -5
  103. package/lib/types/datasource/python.d.ts +4 -0
  104. package/lib/types/datasource/resolver-program.d.ts +6 -0
  105. package/lib/types/datasource/rpc.d.ts +4 -0
  106. package/lib/types/datasource/service.d.ts +112 -0
  107. package/lib/types/datasource/shell-env.d.ts +0 -29
  108. package/lib/types/datasource/test.d.ts +6 -29
  109. package/lib/types/environment/binding.d.ts +5 -1
  110. package/lib/types/environment/errors.d.ts +1 -1
  111. package/lib/types/environment/runtime.d.ts +5 -5
  112. package/lib/types/environment/types.d.ts +13 -2
  113. package/lib/types/index.d.ts +1 -1
  114. package/lib/types/plugin.d.ts +11 -5
  115. package/lib/types/presentation/build/index.d.ts +11 -0
  116. package/lib/types/presentation/commit.d.ts +4 -0
  117. package/lib/types/presentation/contracts/charts.d.ts +13 -0
  118. package/lib/types/presentation/contracts/editing.d.ts +8 -0
  119. package/lib/types/presentation/contracts/index.d.ts +11 -0
  120. package/lib/types/presentation/contracts/interaction.d.ts +8 -0
  121. package/lib/types/presentation/contracts/types.d.ts +252 -0
  122. package/lib/types/presentation/contracts/values.d.ts +9 -0
  123. package/lib/types/presentation/delivery.d.ts +3 -0
  124. package/lib/types/presentation/files.d.ts +7 -0
  125. package/lib/types/presentation/index.d.ts +6 -0
  126. package/lib/types/presentation/lint.d.ts +13 -0
  127. package/lib/types/presentation/projection/files.d.ts +2 -0
  128. package/lib/types/presentation/projection/index.d.ts +20 -0
  129. package/lib/types/presentation/projection/program.d.ts +2 -0
  130. package/lib/types/presentation/projection/sql-code.d.ts +2 -0
  131. package/lib/types/presentation/receipt.d.ts +14 -0
  132. package/lib/types/presentation/reports.d.ts +5 -0
  133. package/lib/types/presentation/rpc.d.ts +24 -0
  134. package/lib/types/presentation/tool.d.ts +12 -0
  135. package/lib/types/python-execution-contracts.d.ts +10 -0
  136. package/lib/types/python-execution.d.ts +10 -0
  137. package/lib/types/semantic-browser/contracts.d.ts +40 -0
  138. package/lib/types/semantic-browser/definition.d.ts +20 -0
  139. package/lib/types/semantic-browser/program.d.ts +2 -0
  140. package/lib/types/semantic-browser/service.d.ts +20 -0
  141. package/lib/types/semantic-reference/bridge.d.ts +11 -0
  142. package/lib/types/semantic-reference/contracts.d.ts +51 -0
  143. package/lib/types/semantic-reference/index.d.ts +3 -0
  144. package/lib/types/semantic-reference/rpc.d.ts +15 -0
  145. package/lib/types/semantic-reference/search.d.ts +10 -0
  146. package/lib/types/semantic-reference/usage.d.ts +43 -0
  147. package/package.json +43 -19
  148. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  149. package/skills/dsh-data-analysis-presentation/SKILL.md +24 -0
  150. package/skills/dsh-data-analysis-presentation/references/chart-examples.md +18 -0
  151. package/skills/dsh-data-analysis-presentation/references/charts.md +55 -0
  152. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +27 -0
  153. package/skills/dsh-data-analysis-presentation/references/examples/charts-bins.dataset.json +33 -0
  154. package/skills/dsh-data-analysis-presentation/references/examples/charts-boxes.dataset.json +45 -0
  155. package/skills/dsh-data-analysis-presentation/references/examples/charts-composition.dataset.json +33 -0
  156. package/skills/dsh-data-analysis-presentation/references/examples/charts-funnel.dataset.json +27 -0
  157. package/skills/dsh-data-analysis-presentation/references/examples/charts-precision.dataset.json +21 -0
  158. package/skills/dsh-data-analysis-presentation/references/examples/charts-series.dataset.json +58 -0
  159. package/skills/dsh-data-analysis-presentation/references/examples/charts-shares.dataset.json +33 -0
  160. package/skills/dsh-data-analysis-presentation/references/examples/charts-waterfall.dataset.json +44 -0
  161. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +392 -0
  162. package/skills/dsh-data-analysis-presentation/references/examples/comparison-selected.dataset.json +24 -0
  163. package/skills/dsh-data-analysis-presentation/references/examples/comparison-summary.dataset.json +22 -0
  164. package/skills/dsh-data-analysis-presentation/references/examples/comparison.dataset.json +18 -0
  165. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +79 -0
  166. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +35 -0
  167. package/skills/dsh-data-analysis-presentation/references/examples/computed.dataset.json +12 -0
  168. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +57 -0
  169. package/skills/dsh-data-analysis-presentation/references/examples/interaction-detail.dataset.json +30 -0
  170. package/skills/dsh-data-analysis-presentation/references/examples/interaction-summary.dataset.json +22 -0
  171. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +278 -0
  172. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +19 -0
  173. package/skills/dsh-data-analysis-presentation/references/examples/write-charts.py +73 -0
  174. package/skills/dsh-data-analysis-presentation/references/examples/write-comparison.py +42 -0
  175. package/skills/dsh-data-analysis-presentation/references/examples/write-computed.py +21 -0
  176. package/skills/dsh-data-analysis-presentation/references/examples/write-interaction.py +33 -0
  177. package/skills/dsh-data-analysis-presentation/references/examples.md +42 -0
  178. package/skills/dsh-data-analysis-presentation/references/interaction.md +56 -0
  179. package/skills/dsh-data-analysis-presentation/references/narrative.md +37 -0
  180. package/skills/dsh-data-analysis-presentation/references/schema.md +138 -0
  181. package/lib/datasource/access.js +0 -60
  182. package/lib/datasource/credentials.js +0 -24
  183. package/lib/evidence/bridge-program.js +0 -153
  184. package/lib/evidence/bridge.js +0 -166
  185. package/lib/evidence/index.js +0 -2
  186. package/lib/evidence/sources.js +0 -263
  187. package/lib/types/datasource/access.d.ts +0 -27
  188. package/lib/types/evidence/bridge.d.ts +0 -40
  189. package/lib/types/evidence/index.d.ts +0 -4
  190. package/lib/types/evidence/sources.d.ts +0 -57
  191. package/python/report-kit/dist/dsh_data_analysis_report_kit-3.0.0-py3-none-any.whl +0 -0
  192. package/skills/dsh-data-analysis-report/SKILL.md +0 -57
  193. package/skills/dsh-data-analysis-report/assets/marivo-artifact.js +0 -113
  194. package/skills/dsh-data-analysis-report/assets/marivo-session-dag.js +0 -1201
  195. package/skills/dsh-data-analysis-report/assets/report-data.js +0 -449
@@ -0,0 +1,494 @@
1
+ const CHANNEL = '/dsh-data-analysis-credentials';
2
+ const STORAGE_KEY = 'marivo-credential-operation';
3
+ const messages = {
4
+ 'context-changed': 'Workspace 或数据源定义已变化,请重新读取后操作。',
5
+ 'credentials-changed': '凭证配置已变化,请重新读取后验证。',
6
+ 'call-ended': '原调用已结束,已保存的值仍保留;请重新发起任务。',
7
+ 'credential-missing': '凭证尚未配齐,请补填后验证。',
8
+ 'credential-save-failed': '部分凭证保存失败;已成功保存的项目保留。',
9
+ 'operation-busy': '该操作正在进行,请等待结果。',
10
+ 'credential-state-unavailable': '暂时无法读取凭证状态,请稍后重试。',
11
+ 'capacity-exceeded': '当前操作数量达到上限,请稍后重试。',
12
+ 'datasource-already-exists': '该数据源已存在,请使用其他名称。',
13
+ 'datasource-definition-invalid': '数据源定义无效,请检查名称、字段类型和凭证引用。',
14
+ 'datasource-credential-ref-invalid': '凭证引用名称无效。*_env 字段填写引用名(如 MY_DB_PASSWORD),仅可使用字母、数字和下划线,且不能以数字开头;不能使用 MARIVO_、DSH_DATA_ANALYSIS_ 前缀或 Host 保留名称。实际用户名和密码请在创建后的“新增凭证”中填写。',
15
+ 'datasource-authoring-unavailable': '当前 Runtime 不支持新增数据源。',
16
+ };
17
+ export function credentialMessage(code) {
18
+ return messages[code] ?? '凭证操作失败,请检查配置后重试。';
19
+ }
20
+ class CredentialResponseError extends Error {
21
+ code;
22
+ constructor(code) {
23
+ super(credentialMessage(code));
24
+ this.code = code;
25
+ }
26
+ }
27
+ function delay(ms, signal) {
28
+ return new Promise((resolve) => {
29
+ const done = () => {
30
+ clearTimeout(timer);
31
+ signal.removeEventListener('abort', done);
32
+ resolve();
33
+ };
34
+ const timer = setTimeout(done, ms);
35
+ signal.addEventListener('abort', done, { once: true });
36
+ if (signal.aborted)
37
+ done();
38
+ });
39
+ }
40
+ export class CredentialClientModel {
41
+ #state = {
42
+ open: false,
43
+ workspaceId: '',
44
+ sessionId: '',
45
+ generation: '',
46
+ datasources: [],
47
+ requests: [],
48
+ requestId: '',
49
+ selected: '',
50
+ loading: false,
51
+ error: '',
52
+ operations: [],
53
+ outcomes: {},
54
+ };
55
+ #rpc;
56
+ #storage;
57
+ #listeners = new Set();
58
+ #lifetime = new AbortController();
59
+ #watch;
60
+ #read;
61
+ #refresh;
62
+ #polls = new Map();
63
+ #operations = new Map();
64
+ #opened = new Set();
65
+ constructor(rpc, storage) {
66
+ this.#rpc = rpc;
67
+ this.#storage = storage;
68
+ }
69
+ getSnapshot = () => this.#state;
70
+ subscribe = (listener) => {
71
+ this.#listeners.add(listener);
72
+ return () => {
73
+ this.#listeners.delete(listener);
74
+ };
75
+ };
76
+ #patch(change) {
77
+ if (this.#lifetime.signal.aborted)
78
+ return;
79
+ this.#state = { ...this.#state, ...change };
80
+ for (const listener of this.#listeners)
81
+ listener();
82
+ }
83
+ async #call(endpoint, payload, signal = this.#lifetime.signal) {
84
+ const result = (await this.#rpc.call(CHANNEL, endpoint, payload, signal));
85
+ if (!result.ok)
86
+ throw new CredentialResponseError(result.error?.message ?? '');
87
+ return result.value;
88
+ }
89
+ show(workspaceId) {
90
+ this.#patch({ open: true, requestId: '' });
91
+ void this.selectWorkspace(workspaceId);
92
+ }
93
+ async authoring(workspaceId, signal) {
94
+ return (await this.#call('authoring', { workspaceId }, signal));
95
+ }
96
+ async createDatasource(workspaceId, schema, input) {
97
+ let result;
98
+ try {
99
+ result = (await this.#call('create-datasource', {
100
+ workspaceId,
101
+ generation: schema.generation,
102
+ fingerprint: schema.fingerprint,
103
+ ...input,
104
+ }));
105
+ }
106
+ catch (error) {
107
+ if (error instanceof CredentialResponseError &&
108
+ [
109
+ 'datasource-credential-ref-invalid',
110
+ 'datasource-already-exists',
111
+ 'context-changed',
112
+ ].includes(error.code))
113
+ throw error;
114
+ throw new Error(`${error instanceof Error ? error.message : '新增数据源失败。'} 如提交结果未确认,请刷新列表核对后再操作。`);
115
+ }
116
+ if (!this.#state.open || this.#state.workspaceId !== workspaceId)
117
+ return;
118
+ await this.selectWorkspace(workspaceId);
119
+ if (this.#state.workspaceId !== workspaceId)
120
+ return;
121
+ const created = this.#state.datasources.find((item) => item.name === result.name);
122
+ if (created)
123
+ this.select(created.token);
124
+ }
125
+ close() {
126
+ this.#read?.abort();
127
+ this.#refresh?.abort();
128
+ this.#patch({ open: false, loading: false });
129
+ }
130
+ openRequest(id) {
131
+ this.#read?.abort();
132
+ this.#refresh?.abort();
133
+ this.#patch({ open: true, requestId: id, loading: false });
134
+ }
135
+ select(token) {
136
+ this.#patch({ selected: token, requestId: '', error: '' });
137
+ }
138
+ async selectWorkspace(workspaceId) {
139
+ this.#read?.abort();
140
+ this.#refresh?.abort();
141
+ const flight = new AbortController();
142
+ this.#read = flight;
143
+ const selected = this.#state.workspaceId === workspaceId ? this.#state.selected : '';
144
+ const activity = new Map([...Object.values(this.#state.outcomes), ...this.#operations.values()].map((entry) => [
145
+ entry.handle.scope,
146
+ entry.handle.id,
147
+ ]));
148
+ this.#patch({
149
+ workspaceId,
150
+ datasources: [],
151
+ selected: '',
152
+ requestId: '',
153
+ loading: !!workspaceId,
154
+ error: '',
155
+ });
156
+ if (!workspaceId)
157
+ return;
158
+ try {
159
+ const value = (await this.#call('overview', { workspaceId }, flight.signal));
160
+ if (flight.signal.aborted)
161
+ return;
162
+ this.#patch({
163
+ generation: value.generation,
164
+ datasources: value.datasources,
165
+ outcomes: this.#currentOutcomes(workspaceId, value.generation, value.datasources, activity),
166
+ selected: value.datasources.some((item) => item.token === selected)
167
+ ? selected
168
+ : (value.datasources[0]?.token ?? ''),
169
+ loading: false,
170
+ });
171
+ }
172
+ catch (error) {
173
+ if (!flight.signal.aborted)
174
+ this.#patch({
175
+ loading: false,
176
+ error: error instanceof Error ? error.message : '无法读取凭证状态。',
177
+ });
178
+ }
179
+ }
180
+ session(sessionId) {
181
+ if (sessionId === this.#state.sessionId && this.#watch)
182
+ return;
183
+ this.#watch?.abort();
184
+ this.#read?.abort();
185
+ this.#refresh?.abort();
186
+ const controller = new AbortController();
187
+ this.#watch = controller;
188
+ this.#patch({ sessionId, requests: [], requestId: '', open: false });
189
+ if (sessionId)
190
+ void this.#watchSession(sessionId, controller.signal);
191
+ }
192
+ async #watchSession(sessionId, signal) {
193
+ let cursor, attempt = 0;
194
+ while (!signal.aborted && !this.#lifetime.signal.aborted) {
195
+ try {
196
+ const value = (await this.#call('watch', { sessionId, ...(cursor ? { cursor } : {}) }, signal));
197
+ if (signal.aborted)
198
+ return;
199
+ cursor = value.cursor;
200
+ attempt = 0;
201
+ this.#patch({ generation: value.generation, requests: value.requests });
202
+ const request = value.requests.find((r) => r.endedAt === undefined && !this.#opened.has(r.id));
203
+ if (request) {
204
+ this.#opened.add(request.id);
205
+ this.openRequest(request.id);
206
+ }
207
+ }
208
+ catch {
209
+ if (!signal.aborted) {
210
+ this.#patch({ error: '凭证待办连接中断,正在重连;Host 中的等待仍受原调用期限限制。' });
211
+ await delay([1000, 2000, 5000][Math.min(attempt++, 2)], signal);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ #persist() {
217
+ try {
218
+ const pending = [...this.#operations.values()]
219
+ .filter((entry) => !entry.error && (!entry.operation || entry.operation.status === 'running'))
220
+ .map(({ handle, name, workspaceId }) => ({ handle, name, workspaceId }));
221
+ if (pending.length)
222
+ this.#storage?.setItem(STORAGE_KEY, JSON.stringify(pending));
223
+ else
224
+ this.#storage?.removeItem(STORAGE_KEY);
225
+ }
226
+ catch {
227
+ /* in-memory queries still work */
228
+ }
229
+ }
230
+ #visible(scope) {
231
+ const selected = this.#state.requestId
232
+ ? this.#state.requests.find((request) => request.id === this.#state.requestId)?.context.token
233
+ : this.#state.selected;
234
+ return selected === scope;
235
+ }
236
+ #currentOutcomes(workspaceId, generation, datasources, activity) {
237
+ const scopes = new Set(datasources.map((context) => context.token));
238
+ return Object.fromEntries(Object.entries(this.#state.outcomes)
239
+ .filter(([scope, entry]) => entry.handle.generation === generation &&
240
+ (entry.workspaceId !== workspaceId || scopes.has(scope)))
241
+ .map(([scope, entry]) => [
242
+ scope,
243
+ scopes.has(scope) && activity.get(scope) === entry.handle.id
244
+ ? { ...entry, overviewUpdated: true }
245
+ : entry,
246
+ ]));
247
+ }
248
+ #activity(scope) {
249
+ return ([...this.#operations.values()].find((entry) => entry.handle.scope === scope)?.handle.id ??
250
+ this.#state.outcomes[scope]?.handle.id);
251
+ }
252
+ async #refreshOverview(handle) {
253
+ const { workspaceId, generation, datasources, open, requestId } = this.#state;
254
+ if (!open ||
255
+ requestId ||
256
+ handle.generation !== generation ||
257
+ !datasources.some((context) => context.token === handle.scope))
258
+ return;
259
+ this.#refresh?.abort();
260
+ const flight = new AbortController(), read = this.#read, activity = new Map(datasources.map((context) => [context.token, this.#activity(context.token)]));
261
+ this.#refresh = flight;
262
+ try {
263
+ const value = (await this.#call('overview', { workspaceId }, flight.signal));
264
+ if (flight.signal.aborted ||
265
+ read !== this.#read ||
266
+ workspaceId !== this.#state.workspaceId ||
267
+ generation !== this.#state.generation ||
268
+ value.generation !== generation)
269
+ return;
270
+ const current = new Map(this.#state.datasources.map((context) => [context.token, context]));
271
+ const updated = value.datasources.map((context) => current.has(context.token) && activity.get(context.token) !== this.#activity(context.token)
272
+ ? current.get(context.token)
273
+ : context);
274
+ this.#patch({
275
+ datasources: updated,
276
+ outcomes: this.#currentOutcomes(workspaceId, generation, updated, activity),
277
+ selected: updated.some((context) => context.token === this.#state.selected)
278
+ ? this.#state.selected
279
+ : (updated[0]?.token ?? ''),
280
+ });
281
+ }
282
+ catch {
283
+ // Keep the last authoritative overview and scoped outcome if a passive refresh fails.
284
+ }
285
+ }
286
+ #publish(handle, operation) {
287
+ const entry = this.#operations.get(handle.id);
288
+ if (!entry)
289
+ return;
290
+ if (operation.status !== 'running') {
291
+ this.#settle({ ...entry, operation });
292
+ return;
293
+ }
294
+ this.#operations.set(handle.id, { ...entry, operation });
295
+ this.#patch({
296
+ operations: [...this.#operations.values()],
297
+ ...(this.#state.handle?.id === handle.id
298
+ ? { operation, ...(this.#visible(handle.scope) ? { error: '' } : {}) }
299
+ : {}),
300
+ });
301
+ this.#persist();
302
+ }
303
+ #settle(entry) {
304
+ this.#operations.delete(entry.handle.id);
305
+ this.#patch({
306
+ operations: [...this.#operations.values()],
307
+ outcomes: { ...this.#state.outcomes, [entry.handle.scope]: entry },
308
+ ...(this.#state.handle?.id === entry.handle.id
309
+ ? {
310
+ handle: undefined,
311
+ operation: undefined,
312
+ ...(this.#visible(entry.handle.scope) ? { error: entry.error ?? '' } : {}),
313
+ }
314
+ : {}),
315
+ });
316
+ this.#persist();
317
+ }
318
+ selectOperation(id) {
319
+ const entry = this.#operations.get(id);
320
+ if (entry)
321
+ this.#patch({ handle: entry.handle, operation: entry.operation, error: entry.error ?? '' });
322
+ }
323
+ async start(context, action, changes = {}, reference) {
324
+ if ([...this.#operations.values()].some((entry) => entry.handle.scope === context.token &&
325
+ !entry.error &&
326
+ (!entry.operation || entry.operation.status === 'running'))) {
327
+ for (const ref of Object.keys(changes))
328
+ delete changes[ref];
329
+ this.#patch({ error: credentialMessage('operation-busy') });
330
+ return;
331
+ }
332
+ const outcomes = { ...this.#state.outcomes };
333
+ delete outcomes[context.token];
334
+ const handle = {
335
+ generation: this.#state.generation,
336
+ id: crypto.randomUUID(),
337
+ scope: context.token,
338
+ };
339
+ const requestId = action === 'submit' || action === 'diagnose' ? this.#state.requestId : undefined;
340
+ const operation = {
341
+ id: handle.id,
342
+ scope: handle.scope,
343
+ action,
344
+ status: 'running',
345
+ phase: 'saving',
346
+ saved: [],
347
+ errors: [],
348
+ };
349
+ this.#operations.set(handle.id, {
350
+ handle,
351
+ name: context.name,
352
+ workspaceId: context.workspaceId,
353
+ operation,
354
+ });
355
+ this.#patch({
356
+ handle,
357
+ operation,
358
+ operations: [...this.#operations.values()],
359
+ outcomes,
360
+ error: '',
361
+ });
362
+ this.#persist();
363
+ try {
364
+ // Only operation queries publish authoritative progress. A late submit reply cannot
365
+ // overwrite another operation or an already newer query result for this ID.
366
+ await this.#call('start', {
367
+ ...handle,
368
+ action,
369
+ version: context.version,
370
+ ...(requestId ? { requestId } : {}),
371
+ changes,
372
+ ...(reference ? { reference } : {}),
373
+ });
374
+ }
375
+ catch {
376
+ if (this.#state.handle?.id === handle.id && this.#visible(handle.scope))
377
+ this.#patch({ error: '提交响应未确认,正在查询操作状态;不会重新发送秘密值。' });
378
+ }
379
+ finally {
380
+ for (const ref of Object.keys(changes))
381
+ delete changes[ref];
382
+ }
383
+ await this.#query(handle);
384
+ }
385
+ async #query(handle) {
386
+ const entry = this.#operations.get(handle.id);
387
+ if (!entry ||
388
+ entry.error ||
389
+ this.#polls.has(handle.id) ||
390
+ (entry.operation && entry.operation.status !== 'running'))
391
+ return;
392
+ const controller = new AbortController();
393
+ this.#polls.set(handle.id, controller);
394
+ const signal = AbortSignal.any([controller.signal, this.#lifetime.signal]);
395
+ try {
396
+ while (!signal.aborted) {
397
+ try {
398
+ const operation = (await this.#call('operation', handle, signal));
399
+ if (signal.aborted)
400
+ return;
401
+ if (!operation) {
402
+ const error = '操作状态不可恢复。保存可能已经发生,请重新读取实际配置后决定下一步。';
403
+ this.#settle({ ...entry, operation: undefined, error });
404
+ return;
405
+ }
406
+ this.#publish(handle, operation);
407
+ if (operation.status !== 'running') {
408
+ await this.#refreshOverview(handle);
409
+ return;
410
+ }
411
+ }
412
+ catch {
413
+ if (!signal.aborted &&
414
+ this.#state.handle?.id === handle.id &&
415
+ this.#visible(handle.scope))
416
+ this.#patch({ error: '正在恢复操作结果;已提交的保存不会自动重发。' });
417
+ }
418
+ await delay(1000, signal);
419
+ }
420
+ }
421
+ finally {
422
+ if (this.#polls.get(handle.id) === controller)
423
+ this.#polls.delete(handle.id);
424
+ }
425
+ }
426
+ recover() {
427
+ try {
428
+ const raw = this.#storage?.getItem(STORAGE_KEY);
429
+ if (!raw)
430
+ return;
431
+ const entries = JSON.parse(raw);
432
+ if (!Array.isArray(entries))
433
+ return;
434
+ for (const entry of entries) {
435
+ const handle = entry?.handle;
436
+ if (!handle ||
437
+ ![handle.generation, handle.id, handle.scope].every((v) => typeof v === 'string' && /^[a-f0-9-]{36}$/i.test(v)) ||
438
+ typeof entry.name !== 'string')
439
+ continue;
440
+ if (!this.#operations.has(handle.id))
441
+ this.#operations.set(handle.id, {
442
+ handle,
443
+ name: entry.name,
444
+ ...(typeof entry.workspaceId === 'string' ? { workspaceId: entry.workspaceId } : {}),
445
+ });
446
+ if (!this.#state.handle)
447
+ this.selectOperation(handle.id);
448
+ void this.#query(handle);
449
+ }
450
+ this.#patch({ operations: [...this.#operations.values()] });
451
+ }
452
+ catch {
453
+ /* invalid or unavailable local handles are not replayed */
454
+ }
455
+ }
456
+ async cancelRequest(id) {
457
+ try {
458
+ await this.#call('cancel-request', { requestId: id });
459
+ }
460
+ catch (error) {
461
+ this.#patch({ error: error instanceof Error ? error.message : '调用已结束。' });
462
+ }
463
+ }
464
+ async cancelOperation(scope) {
465
+ const handle = scope
466
+ ? [...this.#operations.values()].find((entry) => entry.handle.scope === scope &&
467
+ !entry.error &&
468
+ (!entry.operation || entry.operation.status === 'running'))?.handle
469
+ : this.#state.handle;
470
+ if (!handle)
471
+ return;
472
+ try {
473
+ await this.#call('cancel-operation', handle);
474
+ }
475
+ catch {
476
+ this.#patch({ error: '无法确认取消结果,请继续查询。' });
477
+ }
478
+ }
479
+ reset() {
480
+ this.#watch?.abort();
481
+ this.#watch = undefined;
482
+ this.session(this.#state.sessionId);
483
+ this.recover();
484
+ }
485
+ dispose() {
486
+ this.#lifetime.abort();
487
+ this.#watch?.abort();
488
+ this.#read?.abort();
489
+ this.#refresh?.abort();
490
+ for (const controller of this.#polls.values())
491
+ controller.abort();
492
+ this.#listeners.clear();
493
+ }
494
+ }
@@ -0,0 +1,17 @@
1
+ export const credentialStyles = `
2
+ .mc-input-heading{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 12px;min-width:0}.mc-input-name{font-weight:500;color:var(--mc-text);overflow-wrap:anywhere}.mc-input-description{flex:1 1 200px;min-width:0;font-size:11px;line-height:1.6;color:var(--mc-muted);overflow-wrap:anywhere}
3
+ .mc-dialog{--mc-bg:var(--dsw-alias-surface-primary,#fff);--mc-soft:var(--dsw-alias-interactive-bg-hover,#f5f7f9);--mc-text:var(--dsw-alias-label-primary,#182a33);--mc-muted:var(--dsw-alias-label-secondary,#627580);--mc-border:var(--dsw-alias-border-l2,#dce4e8);--mc-accent:#087b70;--mc-tint:#eaf6f2;--mc-danger:#b04436;--mc-warning:#886217;color:var(--mc-text);background:var(--mc-bg);border:1px solid var(--mc-border);border-radius:16px;width:calc(100vw - 48px);max-width:1060px;height:min(760px,calc(100dvh - 64px));max-height:calc(100dvh - 32px);padding:0;box-shadow:0 28px 100px #0003;pointer-events:auto;font:14px/1.55 system-ui,sans-serif;overflow:hidden}
4
+ .mc-dialog::backdrop{background:#102a3a66;backdrop-filter:blur(4px)}
5
+ .mc-dialog *{box-sizing:border-box}.mc-shell{height:100%;display:flex;flex-direction:column}.mc-dialog button,.mc-dialog input,.mc-dialog select{font:inherit;color:inherit}.mc-dialog button{cursor:pointer;border:1px solid var(--mc-border);border-radius:8px;padding:7px 12px;background:var(--mc-bg);line-height:1.5}.mc-dialog button:hover{background:var(--mc-soft)}.mc-dialog button:disabled{cursor:default;opacity:.5}.mc-dialog :focus-visible{outline:2px solid var(--mc-accent);outline-offset:3px}.mc-dialog select,.mc-dialog input{border:1px solid var(--mc-border);border-radius:8px;background:var(--mc-bg);padding:9px 10px;max-width:100%;min-width:0}.mc-dialog select{width:100%}.mc-dialog button.mc-primary{color:#fff;background:var(--mc-accent);border-color:var(--mc-accent)}.mc-dialog button.mc-primary:hover{filter:brightness(.93)}.mc-dialog button.mc-danger{color:var(--mc-danger)}.mc-dialog button.mc-quiet{border-color:transparent;background:transparent;color:var(--mc-muted)}.mc-dialog button.mc-quiet:hover{background:var(--mc-soft)}
6
+ .mc-header{display:flex;align-items:center;gap:14px;padding:22px 26px;border-bottom:1px solid var(--mc-border);flex:none}.mc-header-mark{display:grid;place-items:center;width:42px;height:42px;border:1px solid var(--mc-border);border-radius:12px;background:var(--mc-soft);color:var(--mc-accent);flex:none}.mc-header-copy{flex:1;min-width:0}.mc-header h2{font-size:20px;line-height:1.4;margin:0 0 4px;font-weight:650}.mc-header p{margin:0;font-size:12px;color:var(--mc-muted)}.mc-dialog .mc-close{display:grid;place-items:center;padding:8px;border:0;background:transparent;color:var(--mc-muted)}
7
+ .mc-columns{display:grid;grid-template-columns:244px minmax(0,1fr);flex:1;min-height:0}.mc-nav{display:flex;flex-direction:column;gap:24px;padding:22px 16px;background:var(--mc-soft);border-right:1px solid var(--mc-border);overflow:auto}.mc-workspace-label{display:block;font-size:12px;font-weight:600;color:var(--mc-muted);margin-bottom:8px}.mc-nav-heading{display:flex;align-items:center;justify-content:space-between;margin:0 0 10px;padding:0 4px;font-size:12px;color:var(--mc-muted)}.mc-nav-heading h3{font-size:inherit;font-weight:600;margin:0}.mc-nav-heading button{padding:4px}.mc-datasources{list-style:none;padding:0;margin:0;display:grid;gap:6px}.mc-dialog .mc-datasource{display:flex;align-items:start;gap:10px;width:100%;padding:12px 10px;border:1px solid transparent;background:transparent;text-align:left}.mc-dialog .mc-datasource[aria-pressed=true]{border-color:var(--mc-border);background:var(--mc-bg);box-shadow:0 2px 5px #102a3a05}.mc-datasource>svg{margin-top:2px;flex:none;color:var(--mc-muted)}.mc-datasource[aria-pressed=true]>svg{color:var(--mc-accent)}.mc-datasource-copy{min-width:0;display:grid;gap:4px}.mc-datasource-name{font-size:13px;font-weight:600;overflow-wrap:anywhere}.mc-datasource-status{font-size:11px;color:var(--mc-muted);display:flex;align-items:center;gap:5px}.mc-dot{width:6px;height:6px;background:var(--mc-muted);border-radius:50%;flex:none}.mc-dot[data-ready=true]{background:var(--mc-accent)}.mc-dot[data-ready=false]{background:#c58a26}
8
+ .mc-main{min-width:0;overflow:auto;display:flex;flex-direction:column}.mc-form{display:flex;flex-direction:column;flex:1;min-width:0}.mc-form-body{padding:26px 30px;flex:1}.mc-detail-heading{display:flex;align-items:start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:22px}.mc-eyebrow{font-size:11px;color:var(--mc-muted);margin:0 0 5px}.mc-detail-heading h3{font-size:22px;font-weight:650;line-height:1.4;margin:0;overflow-wrap:anywhere}.mc-badge{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--mc-border);border-radius:6px;padding:3px 7px;font-size:11px;white-space:nowrap;color:var(--mc-muted)}.mc-badge[data-ready=true]{color:var(--mc-accent);background:var(--mc-tint);border-color:transparent}.mc-badge[data-ready=false]{color:var(--mc-warning)}
9
+ .mc-section-heading{font-size:13px;font-weight:600;margin:0 0 12px}.mc-properties{margin:0 0 24px;min-width:0}.mc-property-list{margin:0;border:1px solid var(--mc-border);border-radius:12px;overflow:hidden}.mc-property{display:grid;grid-template-columns:minmax(0,140px) minmax(0,1fr);gap:16px;padding:12px 18px;align-items:start}.mc-property+.mc-property{border-top:1px solid var(--mc-border)}.mc-property dt{font-size:12px;color:var(--mc-muted);overflow-wrap:anywhere}.mc-property dd{margin:0;min-width:0;font-size:12px;white-space:pre-wrap;overflow-wrap:anywhere}.mc-property pre{font:12px/1.6 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.mc-property-json{max-height:240px;overflow:auto}
10
+ .mc-fields{border:1px solid var(--mc-border);border-radius:12px;overflow:hidden;margin:0 0 14px}.mc-field{min-width:0;border:0;padding:18px;margin:0}.mc-field+.mc-field{border-top:1px solid var(--mc-border)}.mc-field-header{display:flex;align-items:start;gap:12px;justify-content:space-between}.mc-field-title{min-width:0}.mc-field-title h4{font-size:13px;margin:0 0 4px;font-weight:600;overflow-wrap:anywhere}.mc-field-title p{margin:0;font-size:11px;color:var(--mc-muted);overflow-wrap:anywhere}.mc-field-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap;flex:none}.mc-dialog .mc-field-actions button{font-size:12px;padding:5px 9px}.mc-secret-input{display:grid;gap:7px;margin-top:14px;font-size:12px;color:var(--mc-muted)}.mc-secret-input input{width:100%;color:var(--mc-text)}.mc-field-note{font-size:11px;color:var(--mc-muted);margin:10px 0 0;overflow-wrap:anywhere}.mc-confirm{margin-top:12px;padding:12px;border:1px solid var(--mc-border);border-radius:8px;background:var(--mc-soft);display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:12px}.mc-confirm p{flex-basis:100%;margin:0}.mc-note{font-size:12px;color:var(--mc-muted);margin:12px 0;overflow-wrap:anywhere}
11
+ .mc-test{margin-top:24px;padding-top:20px;border-top:1px solid var(--mc-border)}.mc-test-heading{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px}.mc-test-heading h4{margin:0;font-size:13px}.mc-test-heading time{font-size:11px;color:var(--mc-muted)}.mc-result{border-radius:9px;padding:12px 14px;background:var(--mc-tint);color:var(--mc-accent);font-size:12px}.mc-result[data-tone=error]{background:var(--mc-soft);color:var(--mc-danger)}.mc-result[data-tone=stale]{background:var(--mc-soft);color:var(--mc-muted)}.mc-result-title{display:flex;align-items:center;gap:7px;font-weight:600;font-size:13px}.mc-result p{margin:6px 0 0}.mc-result details{margin-top:9px}.mc-result summary{cursor:pointer}.mc-result pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:180px;overflow:auto;font:12px/1.6 ui-monospace,monospace}.mc-empty-test{font-size:12px;color:var(--mc-muted);margin:0}
12
+ .mc-form-footer{padding:16px 30px;border-top:1px solid var(--mc-border);background:var(--mc-bg);position:sticky;bottom:0;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.mc-form-footer button{font-size:13px}.mc-form-footer .mc-cancel{margin-left:auto}.mc-alert{margin:16px 30px 0;padding:12px 14px;border-radius:8px;font-size:12px;background:var(--mc-soft);color:var(--mc-danger);white-space:pre-wrap;overflow-wrap:anywhere}.mc-request-status{margin:0 0 18px;padding:12px 14px;border-radius:9px;background:var(--mc-soft);font-size:12px}.mc-request-status p{margin:4px 0 0;color:var(--mc-muted)}.mc-requests{padding:12px 20px;border-bottom:1px solid var(--mc-border);display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}.mc-requests button{font-size:12px}.mc-pending-entry{font:inherit;font-size:12px;cursor:pointer;border:1px solid currentColor;border-radius:7px;padding:5px 9px;background:transparent;color:inherit}
13
+ .mc-activity{padding:14px 30px;background:var(--mc-soft);border-top:1px solid var(--mc-border)}.mc-activity-heading{font-size:12px;color:var(--mc-muted);margin:0 0 8px}.mc-activity-list{display:flex;gap:6px;flex-wrap:wrap}.mc-activity-list button{font-size:12px}.mc-activity-list button[aria-pressed=true]{border-color:var(--mc-accent);color:var(--mc-accent)}.mc-progress{font-size:12px;margin-top:10px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.mc-progress p{margin:0}.mc-progress button{font-size:12px;margin-left:auto}.mc-operation-note{font-size:12px;margin-top:12px;color:var(--mc-muted)}.mc-operation-note p{margin:6px 0}.mc-operation-note[data-error=true]{color:var(--mc-danger)}.mc-empty{padding:64px 28px;text-align:center;color:var(--mc-muted);margin:auto}.mc-empty>svg{margin-bottom:10px;opacity:.65}.mc-empty h3{font-size:15px;color:var(--mc-text);margin:0 0 8px}.mc-empty p{font-size:12px;margin:0}.mc-loading{padding:18px 30px;font-size:12px;color:var(--mc-muted)}
14
+ @media(prefers-color-scheme:dark){.mc-dialog{--mc-bg:var(--dsw-alias-surface-primary,#182127);--mc-soft:var(--dsw-alias-interactive-bg-hover,#202d34);--mc-text:var(--dsw-alias-label-primary,#e4edf1);--mc-muted:var(--dsw-alias-label-secondary,#9baeb9);--mc-border:var(--dsw-alias-border-l2,#35464f);--mc-accent:#39b8a8;--mc-tint:#153d39;--mc-danger:#ef998b;--mc-warning:#d6b778}.mc-dialog button.mc-primary{color:#102a29}}
15
+ @media(max-width:700px){.mc-property{grid-template-columns:minmax(0,1fr);gap:5px;padding:12px 14px}}
16
+ @media(max-width:700px){.mc-dialog{width:calc(100vw - 16px);height:calc(100dvh - 16px);max-height:calc(100dvh - 16px);border-radius:12px}.mc-header{padding:16px;gap:10px}.mc-header-mark{display:none}.mc-header h2{font-size:18px}.mc-columns{display:flex;flex-direction:column;overflow:auto}.mc-nav{flex:none;padding:14px 16px;gap:14px;overflow:visible;border-right:0;border-bottom:1px solid var(--mc-border)}.mc-workspace-label{font-size:11px;margin-bottom:5px}.mc-nav select{padding:7px 10px}.mc-nav-heading{margin-bottom:5px}.mc-datasources{display:flex;overflow-x:auto;padding:2px;gap:6px}.mc-datasources li{flex:0 0 auto;max-width:240px}.mc-dialog .mc-datasource{padding:9px 10px}.mc-datasource-name{font-size:12px}.mc-main{overflow:visible;flex:1;flex-shrink:0}.mc-form-body{padding:22px 18px}.mc-detail-heading h3{font-size:20px}.mc-field{padding:14px}.mc-field-header{flex-wrap:wrap;gap:10px}.mc-field-actions{margin-left:auto}.mc-form-footer{padding:14px 18px;position:static}.mc-form-footer .mc-cancel{margin-left:0}.mc-alert{margin:14px 18px 0}.mc-activity{padding:14px 18px}.mc-test-heading{align-items:start;flex-wrap:wrap}.mc-close{align-self:start}.mc-empty{padding:32px 18px}.mc-requests{padding:12px 16px}}
17
+ `;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const styles = `
3
+ .marivo-workspace-header-action{display:inline-flex;flex:none;align-items:center;justify-content:center;gap:5px;height:32px;padding:6px 8px;border:0;border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:20px;white-space:nowrap;cursor:pointer}
4
+ .marivo-workspace-header-action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#7b899414)}
5
+ .marivo-workspace-header-action:focus-visible{outline:2px solid var(--dsw-alias-label-primary,#087b70);outline-offset:2px}
6
+ .marivo-workspace-header-action:disabled{opacity:.45;cursor:default}
7
+ @media(max-width:640px){.marivo-workspace-header-action{width:32px;padding:8px}.marivo-workspace-header-action span{display:none}}
8
+ `;
9
+ export function WorkspaceHeaderAction({ label, icon, disabled, title, onClick, }) {
10
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: styles }), _jsxs("button", { className: "marivo-workspace-header-action", type: "button", title: title ?? label, "aria-label": `打开${label}`, disabled: disabled, onClick: onClick, children: [_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: icon === 'semantic' ? (_jsx("path", { d: "m12 3 9 5-9 5-9-5 9-5Zm-9 9 9 5 9-5M3 16l9 5 9-5" })) : (_jsxs(_Fragment, { children: [_jsx("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }), _jsx("path", { d: "M8 10V7a4 4 0 0 1 8 0v3m-4 5v2" })] })) }), _jsx("span", { children: label })] })] }));
11
+ }