@sparkelf/dsh-dataops-managed 0.3.14 → 0.3.15
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/lib/client.js +296 -296
- package/lib/client.js.map +1 -1
- package/lib/index.js +102 -17
- package/lib/types/client/ManagedDataOpsSection.d.ts +2 -4
- package/lib/types/client/locales.d.ts +15 -27
- package/lib/types/client/workspace-limits-client.d.ts +9 -0
- package/lib/types/index.d.ts +33 -4
- package/lib/types/workspace-limits.d.ts +13 -0
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -133,6 +133,26 @@ window.__ModuleLoader__.load({
|
|
|
133
133
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
|
+
* A write DataOps refused, carrying the reason it gave.
|
|
137
|
+
*
|
|
138
|
+
* The message is written for the person who set the value, so the panel shows it as-is rather
|
|
139
|
+
* than replacing it with a generic failure notice.
|
|
140
|
+
*/
|
|
141
|
+
var WorkspaceLimitsRejection = class extends Error {
|
|
142
|
+
constructor(message) {
|
|
143
|
+
super(message);
|
|
144
|
+
this.name = "WorkspaceLimitsRejection";
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/** Read the reason from a refused response, falling back to the status line. */
|
|
148
|
+
async function refusalReason(response, fallback) {
|
|
149
|
+
try {
|
|
150
|
+
const error = readJson(await response.json())?.error;
|
|
151
|
+
if (typeof error === "string" && error.trim() !== "") return error;
|
|
152
|
+
} catch {}
|
|
153
|
+
return `${fallback} with HTTP ${String(response.status)}`;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
136
156
|
* Read the workspace limits the panel shows.
|
|
137
157
|
* @returns The status, or null when the workspace is not connected to DataOps.
|
|
138
158
|
*/
|
|
@@ -155,13 +175,14 @@ window.__ModuleLoader__.load({
|
|
|
155
175
|
body: JSON.stringify(limits)
|
|
156
176
|
});
|
|
157
177
|
if (response.status === 503) return null;
|
|
158
|
-
if (
|
|
178
|
+
if (response.status === 400) throw new WorkspaceLimitsRejection(await refusalReason(response, "Workspace limits write refused"));
|
|
179
|
+
if (!response.ok) throw new Error(await refusalReason(response, "Workspace limits write failed"));
|
|
159
180
|
const body = readJson(await response.json());
|
|
160
181
|
return body === null ? null : asStatus(body);
|
|
161
182
|
}
|
|
162
183
|
//#endregion
|
|
163
184
|
//#region \0dsh-css:/root/projects/dsh-plugins-plus/packages/dataops-managed/src/client/ManagedDataOpsSection.module.css.mjs
|
|
164
|
-
const css$2 = "._2dqzjG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:
|
|
185
|
+
const css$2 = "._2dqzjG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}._2dqzjG_title{margin:0;font-size:18px;font-weight:600;line-height:28px}._2dqzjG_status{color:var(--dsw-alias-label-secondary);align-items:center;gap:8px;font-size:13px;line-height:22px;display:flex}._2dqzjG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-xl);background:var(--dsw-alias-settings-card-fill);flex-direction:column;gap:12px;padding:12px 16px;display:flex}._2dqzjG_heading{margin:0;font-size:13px;font-weight:600;line-height:22px}._2dqzjG_row{justify-content:space-between;align-items:center;gap:16px;display:flex}._2dqzjG_copy{flex-direction:column;min-width:0;display:flex}._2dqzjG_label{font-size:13px;line-height:22px}._2dqzjG_meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}._2dqzjG_fields{grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:0 16px;display:grid}._2dqzjG_fields>div{min-width:0}._2dqzjG_fields input{font-variant-numeric:tabular-nums;min-width:0}._2dqzjG_actions{justify-content:flex-end;align-items:center;gap:12px;display:flex}._2dqzjG_refusal{color:var(--dsw-alias-state-error-primary);align-items:flex-start;gap:6px;margin-right:auto;font-size:12px;line-height:18px;display:flex}";
|
|
165
186
|
const tagId$2 = "@sparkelf/dsh-dataops-managed/ManagedDataOpsSection.module.css";
|
|
166
187
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
167
188
|
const tag = document.createElement("style");
|
|
@@ -171,30 +192,43 @@ window.__ModuleLoader__.load({
|
|
|
171
192
|
document.head.appendChild(tag);
|
|
172
193
|
}
|
|
173
194
|
var ManagedDataOpsSection_module_css_default = {
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
"details": "_2dqzjG_details",
|
|
177
|
-
"limitsInput": "_2dqzjG_limitsInput",
|
|
178
|
-
"modelPanel": "_2dqzjG_modelPanel",
|
|
195
|
+
"refusal": "_2dqzjG_refusal",
|
|
196
|
+
"row": "_2dqzjG_row",
|
|
179
197
|
"section": "_2dqzjG_section",
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"modelListDescription": "_2dqzjG_modelListDescription",
|
|
184
|
-
"modelRow": "_2dqzjG_modelRow",
|
|
198
|
+
"actions": "_2dqzjG_actions",
|
|
199
|
+
"card": "_2dqzjG_card",
|
|
200
|
+
"heading": "_2dqzjG_heading",
|
|
185
201
|
"title": "_2dqzjG_title",
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"modelListTitle": "_2dqzjG_modelListTitle",
|
|
192
|
-
"modelDescription": "_2dqzjG_modelDescription"
|
|
202
|
+
"copy": "_2dqzjG_copy",
|
|
203
|
+
"label": "_2dqzjG_label",
|
|
204
|
+
"status": "_2dqzjG_status",
|
|
205
|
+
"meta": "_2dqzjG_meta",
|
|
206
|
+
"fields": "_2dqzjG_fields"
|
|
193
207
|
};
|
|
194
208
|
//#endregion
|
|
195
209
|
//#region src/client/ManagedDataOpsSection.tsx
|
|
210
|
+
/** The panel's unit, so a stored byte count survives a round trip through the input. */
|
|
211
|
+
const BYTES_PER_MIB = 1048576;
|
|
212
|
+
/** Render a byte ceiling as the whole MiB the input shows. */
|
|
213
|
+
function toMib(bytes) {
|
|
214
|
+
return String(Math.round(bytes / BYTES_PER_MIB * 100) / 100);
|
|
215
|
+
}
|
|
216
|
+
/** Parse a MiB draft into a byte ceiling, or null when it is not a positive count. */
|
|
217
|
+
function toBytes(text) {
|
|
218
|
+
if (text.trim() === "") return null;
|
|
219
|
+
const mib = Number(text);
|
|
220
|
+
if (!Number.isFinite(mib) || mib <= 0) return null;
|
|
221
|
+
const bytes = Math.round(mib * BYTES_PER_MIB);
|
|
222
|
+
return Number.isSafeInteger(bytes) && bytes > 0 ? bytes : null;
|
|
223
|
+
}
|
|
224
|
+
/** Format a stored publication time for the row, or nothing when it never published. */
|
|
225
|
+
function publishedText(publishedAt) {
|
|
226
|
+
const at = new Date(publishedAt);
|
|
227
|
+
return Number.isNaN(at.getTime()) ? null : at.toLocaleString();
|
|
228
|
+
}
|
|
196
229
|
/**
|
|
197
|
-
*
|
|
230
|
+
* The DataOps settings this workspace owns: its connection state, its branding, its file ceilings,
|
|
231
|
+
* and the default configuration it publishes to other workspaces.
|
|
198
232
|
* @param props - Settings slot injection values.
|
|
199
233
|
* @returns The localized managed DataOps section, or nothing before injection.
|
|
200
234
|
*/
|
|
@@ -210,165 +244,81 @@ window.__ModuleLoader__.load({
|
|
|
210
244
|
children: t("title")
|
|
211
245
|
}),
|
|
212
246
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
213
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
214
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
247
|
+
className: ManagedDataOpsSection_module_css_default.status,
|
|
248
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("managed") })]
|
|
215
249
|
}),
|
|
216
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
217
|
-
|
|
218
|
-
|
|
250
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandingRow, {
|
|
251
|
+
t,
|
|
252
|
+
enabled: branding.enabled
|
|
219
253
|
}),
|
|
220
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
221
|
-
|
|
222
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
223
|
-
className: ManagedDataOpsSection_module_css_default.brandingCopy,
|
|
224
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("brandingTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("brandingDescription") })]
|
|
225
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
226
|
-
type: "button",
|
|
227
|
-
className: ManagedDataOpsSection_module_css_default.switch,
|
|
228
|
-
role: "switch",
|
|
229
|
-
"aria-label": t("brandingToggle"),
|
|
230
|
-
"aria-checked": branding.enabled,
|
|
231
|
-
"data-checked": branding.enabled,
|
|
232
|
-
onClick: () => {
|
|
233
|
-
setWanxiangBrandEnabled(!branding.enabled);
|
|
234
|
-
},
|
|
235
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
|
|
236
|
-
})]
|
|
237
|
-
}),
|
|
238
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PublishDefaults, { t }),
|
|
239
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceLimitsPanel, { t }),
|
|
240
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
241
|
-
className: ManagedDataOpsSection_module_css_default.details,
|
|
242
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
243
|
-
className: ManagedDataOpsSection_module_css_default.detailRow,
|
|
244
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("identityLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("identityValue") })]
|
|
245
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
246
|
-
className: ManagedDataOpsSection_module_css_default.detailRow,
|
|
247
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("toolsLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("toolsValue") })]
|
|
248
|
-
})]
|
|
249
|
-
})
|
|
254
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LimitsCard, { t }),
|
|
255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultsCard, { t })
|
|
250
256
|
]
|
|
251
257
|
});
|
|
252
258
|
}
|
|
253
|
-
/**
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
kind: "ready",
|
|
271
|
-
status
|
|
272
|
-
});
|
|
273
|
-
} catch {
|
|
274
|
-
setState({ kind: "failed" });
|
|
275
|
-
}
|
|
276
|
-
}, []);
|
|
277
|
-
(0, react.useEffect)(() => {
|
|
278
|
-
refresh();
|
|
279
|
-
}, [refresh]);
|
|
280
|
-
const publish = (0, react.useCallback)(async () => {
|
|
281
|
-
setBusy(true);
|
|
282
|
-
try {
|
|
283
|
-
const status = await postModelSyncAction({ action: "publish-defaults" });
|
|
284
|
-
if (status !== null) setState({
|
|
285
|
-
kind: "ready",
|
|
286
|
-
status
|
|
287
|
-
});
|
|
288
|
-
} catch {
|
|
289
|
-
setState({ kind: "failed" });
|
|
290
|
-
} finally {
|
|
291
|
-
setBusy(false);
|
|
292
|
-
}
|
|
293
|
-
}, []);
|
|
294
|
-
if (state.kind === "loading") return null;
|
|
295
|
-
if (state.kind === "unavailable" || state.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
296
|
-
className: ManagedDataOpsSection_module_css_default.modelPanel,
|
|
297
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
298
|
-
className: ManagedDataOpsSection_module_css_default.modelTitle,
|
|
299
|
-
children: t("defaultsTitle")
|
|
300
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
301
|
-
className: ManagedDataOpsSection_module_css_default.modelDescription,
|
|
302
|
-
role: state.kind === "failed" ? "alert" : void 0,
|
|
303
|
-
children: state.kind === "failed" ? t("loadFailed") : t("modelsUnavailable")
|
|
304
|
-
})]
|
|
259
|
+
/** The branding toggle as one settings row. */
|
|
260
|
+
function BrandingRow({ t, enabled }) {
|
|
261
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
262
|
+
className: ManagedDataOpsSection_module_css_default.card,
|
|
263
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
264
|
+
className: ManagedDataOpsSection_module_css_default.row,
|
|
265
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
266
|
+
className: ManagedDataOpsSection_module_css_default.label,
|
|
267
|
+
children: t("brandingTitle")
|
|
268
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
269
|
+
checked: enabled,
|
|
270
|
+
label: t("brandingToggle"),
|
|
271
|
+
onChange: (next) => {
|
|
272
|
+
setWanxiangBrandEnabled(next);
|
|
273
|
+
}
|
|
274
|
+
})]
|
|
275
|
+
})
|
|
305
276
|
});
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
277
|
+
}
|
|
278
|
+
/** The one row a card renders while it cannot offer its controls. */
|
|
279
|
+
function UnavailableCard({ title, t, state }) {
|
|
280
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
281
|
+
className: ManagedDataOpsSection_module_css_default.card,
|
|
309
282
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
310
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
311
|
-
children:
|
|
312
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
313
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
314
|
-
|
|
283
|
+
className: ManagedDataOpsSection_module_css_default.heading,
|
|
284
|
+
children: title
|
|
285
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
286
|
+
className: ManagedDataOpsSection_module_css_default.status,
|
|
287
|
+
role: state === "failed" ? "alert" : void 0,
|
|
288
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: state === "failed" ? "error" : "idle" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: state === "failed" ? t("loadFailed") : t("unavailable") })]
|
|
315
289
|
})]
|
|
316
290
|
});
|
|
317
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
318
|
-
className: ManagedDataOpsSection_module_css_default.modelPanel,
|
|
319
|
-
children: [
|
|
320
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
321
|
-
className: ManagedDataOpsSection_module_css_default.modelTitle,
|
|
322
|
-
children: t("defaultsTitle")
|
|
323
|
-
}),
|
|
324
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
325
|
-
className: ManagedDataOpsSection_module_css_default.modelDescription,
|
|
326
|
-
children: t("defaultsDescription")
|
|
327
|
-
}),
|
|
328
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
329
|
-
className: ManagedDataOpsSection_module_css_default.brandingRow,
|
|
330
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
331
|
-
className: ManagedDataOpsSection_module_css_default.brandingCopy,
|
|
332
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("defaultsActionTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: status.publishedAt === null ? t("defaultsNeverPublished") : t("defaultsPublishedAt") + " " + status.publishedAt })]
|
|
333
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
334
|
-
type: "button",
|
|
335
|
-
className: ManagedDataOpsSection_module_css_default.modelAction,
|
|
336
|
-
disabled: busy,
|
|
337
|
-
onClick: () => {
|
|
338
|
-
publish();
|
|
339
|
-
},
|
|
340
|
-
children: busy ? t("defaultsPublishing") : t("defaultsPublish")
|
|
341
|
-
})]
|
|
342
|
-
})
|
|
343
|
-
]
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
/** The panel's unit, so a stored byte count survives a round trip through the input. */
|
|
347
|
-
const BYTES_PER_MIB = 1048576;
|
|
348
|
-
/** Render a byte ceiling as the whole MiB the input shows. */
|
|
349
|
-
function toMib(bytes) {
|
|
350
|
-
return String(Math.round(bytes / BYTES_PER_MIB * 100) / 100);
|
|
351
291
|
}
|
|
352
292
|
/**
|
|
353
|
-
*
|
|
293
|
+
* The file ceilings DataOps enforces on this workspace.
|
|
354
294
|
*
|
|
355
295
|
* The values live in DataOps and are read through the plugin, which holds the JWT; the panel only
|
|
356
296
|
* ever holds what the administrator is allowed to see.
|
|
357
297
|
* @param props - Translate function from the section.
|
|
358
|
-
* @returns The limits
|
|
298
|
+
* @returns The limits card.
|
|
359
299
|
*/
|
|
360
|
-
function
|
|
300
|
+
function LimitsCard({ t }) {
|
|
361
301
|
const [state, setState] = (0, react.useState)({ kind: "loading" });
|
|
362
302
|
const [fileReadMib, setFileReadMib] = (0, react.useState)("");
|
|
363
303
|
const [uploadMib, setUploadMib] = (0, react.useState)("");
|
|
304
|
+
/** The ceilings the draft started from, so Save stays off until one of them changes. */
|
|
305
|
+
const [saved, setSaved] = (0, react.useState)("");
|
|
306
|
+
/** Why DataOps refused the last save, kept beside the draft it refused. */
|
|
307
|
+
const [refusal, setRefusal] = (0, react.useState)(null);
|
|
364
308
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
309
|
+
const fileReadId = (0, react.useId)();
|
|
310
|
+
const uploadId = (0, react.useId)();
|
|
365
311
|
const apply = (0, react.useCallback)((status) => {
|
|
366
312
|
setState({
|
|
367
313
|
kind: "ready",
|
|
368
314
|
status
|
|
369
315
|
});
|
|
370
|
-
|
|
371
|
-
|
|
316
|
+
const file = toMib(status.limits.fileReadMaxBytes);
|
|
317
|
+
const upload = toMib(status.limits.uploadMaxBytes);
|
|
318
|
+
setFileReadMib(file);
|
|
319
|
+
setUploadMib(upload);
|
|
320
|
+
setSaved(file + "|" + upload);
|
|
321
|
+
setRefusal(null);
|
|
372
322
|
}, []);
|
|
373
323
|
const refresh = (0, react.useCallback)(async () => {
|
|
374
324
|
try {
|
|
@@ -382,93 +332,92 @@ window.__ModuleLoader__.load({
|
|
|
382
332
|
(0, react.useEffect)(() => {
|
|
383
333
|
refresh();
|
|
384
334
|
}, [refresh]);
|
|
335
|
+
const fileReadBytes = toBytes(fileReadMib);
|
|
336
|
+
const uploadBytes = toBytes(uploadMib);
|
|
337
|
+
const writable = state.kind === "ready" && state.status.canWrite;
|
|
338
|
+
const dirty = fileReadMib + "|" + uploadMib !== saved;
|
|
339
|
+
const ready = fileReadBytes !== null && uploadBytes !== null && dirty;
|
|
340
|
+
/** Edit one ceiling, dropping a refusal that no longer describes what is in the field. */
|
|
341
|
+
const edit = (0, react.useCallback)((set) => (text) => {
|
|
342
|
+
set(text);
|
|
343
|
+
setRefusal(null);
|
|
344
|
+
}, []);
|
|
385
345
|
const submit = (0, react.useCallback)(async () => {
|
|
386
|
-
|
|
387
|
-
const uploadMaxBytes = Math.round(Number(uploadMib) * BYTES_PER_MIB);
|
|
388
|
-
if (!Number.isSafeInteger(fileReadMaxBytes) || fileReadMaxBytes <= 0) return;
|
|
389
|
-
if (!Number.isSafeInteger(uploadMaxBytes) || uploadMaxBytes <= 0) return;
|
|
346
|
+
if (fileReadBytes === null || uploadBytes === null) return;
|
|
390
347
|
setBusy(true);
|
|
348
|
+
setRefusal(null);
|
|
391
349
|
try {
|
|
392
350
|
const status = await saveWorkspaceLimits({
|
|
393
|
-
fileReadMaxBytes,
|
|
394
|
-
uploadMaxBytes
|
|
351
|
+
fileReadMaxBytes: fileReadBytes,
|
|
352
|
+
uploadMaxBytes: uploadBytes
|
|
395
353
|
});
|
|
396
354
|
if (status === null) setState({ kind: "unavailable" });
|
|
397
355
|
else apply(status);
|
|
398
|
-
} catch {
|
|
399
|
-
|
|
356
|
+
} catch (error) {
|
|
357
|
+
if (error instanceof WorkspaceLimitsRejection) setRefusal(error.message);
|
|
358
|
+
else setState({ kind: "failed" });
|
|
400
359
|
} finally {
|
|
401
360
|
setBusy(false);
|
|
402
361
|
}
|
|
403
362
|
}, [
|
|
404
|
-
|
|
405
|
-
|
|
363
|
+
fileReadBytes,
|
|
364
|
+
uploadBytes,
|
|
406
365
|
apply
|
|
407
366
|
]);
|
|
408
367
|
if (state.kind === "loading") return null;
|
|
409
|
-
if (state.kind !== "ready") return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
children: t("limitsTitle")
|
|
414
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
415
|
-
className: ManagedDataOpsSection_module_css_default.modelDescription,
|
|
416
|
-
role: state.kind === "failed" ? "alert" : void 0,
|
|
417
|
-
children: state.kind === "failed" ? t("loadFailed") : t("limitsUnavailable")
|
|
418
|
-
})]
|
|
368
|
+
if (state.kind !== "ready") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UnavailableCard, {
|
|
369
|
+
title: t("limitsTitle"),
|
|
370
|
+
t,
|
|
371
|
+
state: state.kind
|
|
419
372
|
});
|
|
420
|
-
|
|
421
|
-
|
|
373
|
+
const disabled = busy || !writable;
|
|
374
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
375
|
+
className: ManagedDataOpsSection_module_css_default.card,
|
|
422
376
|
children: [
|
|
423
377
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
424
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
378
|
+
className: ManagedDataOpsSection_module_css_default.heading,
|
|
425
379
|
children: t("limitsTitle")
|
|
426
380
|
}),
|
|
427
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
428
|
-
className: ManagedDataOpsSection_module_css_default.modelDescription,
|
|
429
|
-
children: t("limitsDescription")
|
|
430
|
-
}),
|
|
431
381
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
432
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
433
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
})]
|
|
461
|
-
})]
|
|
382
|
+
className: ManagedDataOpsSection_module_css_default.fields,
|
|
383
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SettingsValueField, {
|
|
384
|
+
id: fileReadId,
|
|
385
|
+
label: t("limitsFileReadLabel"),
|
|
386
|
+
text: fileReadMib,
|
|
387
|
+
numeric: true,
|
|
388
|
+
invalid: fileReadBytes === null,
|
|
389
|
+
overridden: false,
|
|
390
|
+
overriddenLabel: "",
|
|
391
|
+
resetLabel: "",
|
|
392
|
+
invalidLabel: t("limitsInvalid"),
|
|
393
|
+
disabled,
|
|
394
|
+
onEdit: edit(setFileReadMib),
|
|
395
|
+
onReset: () => void 0
|
|
396
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SettingsValueField, {
|
|
397
|
+
id: uploadId,
|
|
398
|
+
label: t("limitsUploadLabel"),
|
|
399
|
+
text: uploadMib,
|
|
400
|
+
numeric: true,
|
|
401
|
+
invalid: uploadBytes === null,
|
|
402
|
+
overridden: false,
|
|
403
|
+
overriddenLabel: "",
|
|
404
|
+
resetLabel: "",
|
|
405
|
+
invalidLabel: t("limitsInvalid"),
|
|
406
|
+
disabled,
|
|
407
|
+
onEdit: edit(setUploadMib),
|
|
408
|
+
onReset: () => void 0
|
|
409
|
+
}) })]
|
|
462
410
|
}),
|
|
463
411
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
464
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
465
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
466
|
-
className: ManagedDataOpsSection_module_css_default.
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
412
|
+
className: ManagedDataOpsSection_module_css_default.actions,
|
|
413
|
+
children: [refusal === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
414
|
+
className: ManagedDataOpsSection_module_css_default.refusal,
|
|
415
|
+
role: "alert",
|
|
416
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: refusal })]
|
|
417
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
418
|
+
variant: "primary",
|
|
419
|
+
size: "sm",
|
|
420
|
+
disabled: disabled || !ready,
|
|
472
421
|
onClick: () => {
|
|
473
422
|
submit();
|
|
474
423
|
},
|
|
@@ -478,6 +427,81 @@ window.__ModuleLoader__.load({
|
|
|
478
427
|
]
|
|
479
428
|
});
|
|
480
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Publish this workspace's configuration as the default other users start from.
|
|
432
|
+
*
|
|
433
|
+
* One action rather than a set of controls: the configuration is whatever this workspace already
|
|
434
|
+
* holds, and the button copies it to DataOps.
|
|
435
|
+
* @param props - Translate function from the section.
|
|
436
|
+
* @returns The default-configuration card.
|
|
437
|
+
*/
|
|
438
|
+
function DefaultsCard({ t }) {
|
|
439
|
+
const [state, setState] = (0, react.useState)({ kind: "loading" });
|
|
440
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
441
|
+
const refresh = (0, react.useCallback)(async () => {
|
|
442
|
+
try {
|
|
443
|
+
const status = await fetchModelSyncStatus();
|
|
444
|
+
setState(status === null ? { kind: "unavailable" } : {
|
|
445
|
+
kind: "ready",
|
|
446
|
+
status
|
|
447
|
+
});
|
|
448
|
+
} catch {
|
|
449
|
+
setState({ kind: "failed" });
|
|
450
|
+
}
|
|
451
|
+
}, []);
|
|
452
|
+
(0, react.useEffect)(() => {
|
|
453
|
+
refresh();
|
|
454
|
+
}, [refresh]);
|
|
455
|
+
const publish = (0, react.useCallback)(async () => {
|
|
456
|
+
setBusy(true);
|
|
457
|
+
try {
|
|
458
|
+
const status = await postModelSyncAction({ action: "publish-defaults" });
|
|
459
|
+
if (status === null) setState({ kind: "unavailable" });
|
|
460
|
+
else setState({
|
|
461
|
+
kind: "ready",
|
|
462
|
+
status
|
|
463
|
+
});
|
|
464
|
+
} catch {
|
|
465
|
+
setState({ kind: "failed" });
|
|
466
|
+
} finally {
|
|
467
|
+
setBusy(false);
|
|
468
|
+
}
|
|
469
|
+
}, []);
|
|
470
|
+
if (state.kind === "loading") return null;
|
|
471
|
+
if (state.kind !== "ready") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UnavailableCard, {
|
|
472
|
+
title: t("defaultsTitle"),
|
|
473
|
+
t,
|
|
474
|
+
state: state.kind
|
|
475
|
+
});
|
|
476
|
+
const published = state.status.publishedAt === null ? null : publishedText(state.status.publishedAt);
|
|
477
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
478
|
+
className: ManagedDataOpsSection_module_css_default.card,
|
|
479
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
480
|
+
className: ManagedDataOpsSection_module_css_default.row,
|
|
481
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
482
|
+
className: ManagedDataOpsSection_module_css_default.copy,
|
|
483
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
484
|
+
className: ManagedDataOpsSection_module_css_default.label,
|
|
485
|
+
children: t("defaultsTitle")
|
|
486
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
487
|
+
className: ManagedDataOpsSection_module_css_default.meta,
|
|
488
|
+
children: published === null ? t("defaultsNeverPublished") : t("defaultsPublishedAt") + " · " + published
|
|
489
|
+
})]
|
|
490
|
+
}), state.status.canPublish ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
491
|
+
variant: "outline",
|
|
492
|
+
size: "sm",
|
|
493
|
+
disabled: busy,
|
|
494
|
+
onClick: () => {
|
|
495
|
+
publish();
|
|
496
|
+
},
|
|
497
|
+
children: busy ? t("defaultsPublishing") : t("defaultsPublish")
|
|
498
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
499
|
+
className: ManagedDataOpsSection_module_css_default.meta,
|
|
500
|
+
children: t("defaultsNotAdmin")
|
|
501
|
+
})]
|
|
502
|
+
})
|
|
503
|
+
});
|
|
504
|
+
}
|
|
481
505
|
//#endregion
|
|
482
506
|
//#region src/client/skill-plaza-client.ts
|
|
483
507
|
/** Same-origin skill plaza calls from the DataOps skill center section. */
|
|
@@ -552,28 +576,28 @@ window.__ModuleLoader__.load({
|
|
|
552
576
|
document.head.appendChild(tag);
|
|
553
577
|
}
|
|
554
578
|
var SkillPlazaSection_module_css_default = {
|
|
555
|
-
"search": "_9FdZvq_search",
|
|
556
|
-
"titleIcon": "_9FdZvq_titleIcon",
|
|
557
|
-
"cardMarks": "_9FdZvq_cardMarks",
|
|
558
|
-
"emptyText": "_9FdZvq_emptyText",
|
|
559
|
-
"cardHead": "_9FdZvq_cardHead",
|
|
560
|
-
"emptyIcon": "_9FdZvq_emptyIcon",
|
|
561
579
|
"toolbar": "_9FdZvq_toolbar",
|
|
562
|
-
"head": "_9FdZvq_head",
|
|
563
|
-
"intro": "_9FdZvq_intro",
|
|
564
|
-
"section": "_9FdZvq_section",
|
|
565
|
-
"title": "_9FdZvq_title",
|
|
566
580
|
"card": "_9FdZvq_card",
|
|
581
|
+
"mine": "_9FdZvq_mine",
|
|
582
|
+
"cardMarks": "_9FdZvq_cardMarks",
|
|
583
|
+
"emptyText": "_9FdZvq_emptyText",
|
|
567
584
|
"tagBar": "_9FdZvq_tagBar",
|
|
568
|
-
"
|
|
569
|
-
"cardMain": "_9FdZvq_cardMain",
|
|
585
|
+
"intro": "_9FdZvq_intro",
|
|
570
586
|
"cardTitle": "_9FdZvq_cardTitle",
|
|
571
|
-
"
|
|
587
|
+
"emptyIcon": "_9FdZvq_emptyIcon",
|
|
588
|
+
"search": "_9FdZvq_search",
|
|
572
589
|
"notice": "_9FdZvq_notice",
|
|
573
|
-
"
|
|
590
|
+
"cardMain": "_9FdZvq_cardMain",
|
|
574
591
|
"cardEnd": "_9FdZvq_cardEnd",
|
|
592
|
+
"title": "_9FdZvq_title",
|
|
575
593
|
"cardIcon": "_9FdZvq_cardIcon",
|
|
576
|
-
"
|
|
594
|
+
"cardHead": "_9FdZvq_cardHead",
|
|
595
|
+
"cards": "_9FdZvq_cards",
|
|
596
|
+
"cardDesc": "_9FdZvq_cardDesc",
|
|
597
|
+
"titleIcon": "_9FdZvq_titleIcon",
|
|
598
|
+
"head": "_9FdZvq_head",
|
|
599
|
+
"empty": "_9FdZvq_empty",
|
|
600
|
+
"section": "_9FdZvq_section"
|
|
577
601
|
};
|
|
578
602
|
//#endregion
|
|
579
603
|
//#region src/client/SkillPlazaSection.tsx
|
|
@@ -825,18 +849,18 @@ window.__ModuleLoader__.load({
|
|
|
825
849
|
document.head.appendChild(tag);
|
|
826
850
|
}
|
|
827
851
|
var WanxiangBrand_module_css_default = {
|
|
828
|
-
"
|
|
829
|
-
"unitRight": "s0HPxG_unitRight",
|
|
830
|
-
"sidebarName": "s0HPxG_sidebarName",
|
|
831
|
-
"unit-core": "s0HPxG_unit-core",
|
|
832
|
-
"unitCore": "s0HPxG_unitCore",
|
|
833
|
-
"mark": "s0HPxG_mark",
|
|
834
|
-
"harness": "s0HPxG_harness",
|
|
852
|
+
"unit-left": "s0HPxG_unit-left",
|
|
835
853
|
"unit-top": "s0HPxG_unit-top",
|
|
854
|
+
"mark": "s0HPxG_mark",
|
|
855
|
+
"unit-core": "s0HPxG_unit-core",
|
|
856
|
+
"sidebarName": "s0HPxG_sidebarName",
|
|
836
857
|
"unit-right": "s0HPxG_unit-right",
|
|
858
|
+
"animatedMark": "s0HPxG_animatedMark",
|
|
837
859
|
"unitLeft": "s0HPxG_unitLeft",
|
|
860
|
+
"unitCore": "s0HPxG_unitCore",
|
|
861
|
+
"unitRight": "s0HPxG_unitRight",
|
|
838
862
|
"unitTop": "s0HPxG_unitTop",
|
|
839
|
-
"
|
|
863
|
+
"harness": "s0HPxG_harness"
|
|
840
864
|
};
|
|
841
865
|
//#endregion
|
|
842
866
|
//#region src/client/WanxiangBrand.tsx
|
|
@@ -1112,27 +1136,23 @@ window.__ModuleLoader__.load({
|
|
|
1112
1136
|
const en = {
|
|
1113
1137
|
nav: "DataOps",
|
|
1114
1138
|
title: "DataOps",
|
|
1115
|
-
managed: "
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
modelsUnavailable: "Model configuration is unavailable until this workspace is connected to DataOps.",
|
|
1139
|
+
managed: "Connected to DataOps",
|
|
1140
|
+
unavailable: "DataOps is not connected.",
|
|
1141
|
+
loadFailed: "Loading failed. Try again.",
|
|
1142
|
+
brandingTitle: "Wanxiang branding",
|
|
1143
|
+
brandingToggle: "Wanxiang branding",
|
|
1144
|
+
limitsTitle: "File size limits",
|
|
1145
|
+
limitsFileReadLabel: "Largest file to read (MiB)",
|
|
1146
|
+
limitsUploadLabel: "Largest file to upload (MiB)",
|
|
1147
|
+
limitsInvalid: "Enter a size in MiB.",
|
|
1148
|
+
limitsSave: "Save",
|
|
1149
|
+
limitsSaving: "Saving…",
|
|
1127
1150
|
defaultsTitle: "Default configuration",
|
|
1128
|
-
|
|
1129
|
-
defaultsActionTitle: "Publish as the default",
|
|
1130
|
-
defaultsNotAdmin: "Only an administrator may publish the default configuration.",
|
|
1131
|
-
defaultsNeverPublished: "Not published yet.",
|
|
1132
|
-
defaultsPublishedAt: "Last published:",
|
|
1133
|
-
defaultsPublish: "Publish default configuration",
|
|
1151
|
+
defaultsPublish: "Publish",
|
|
1134
1152
|
defaultsPublishing: "Publishing…",
|
|
1135
|
-
|
|
1153
|
+
defaultsPublishedAt: "Last published",
|
|
1154
|
+
defaultsNeverPublished: "Not published yet",
|
|
1155
|
+
defaultsNotAdmin: "Administrators only",
|
|
1136
1156
|
plazaTitle: "Skill plaza",
|
|
1137
1157
|
plazaIntro: "Skills other users shared. Install one to use it in this workspace, or share a skill of your own.",
|
|
1138
1158
|
plazaSearch: "Search skills",
|
|
@@ -1148,41 +1168,29 @@ window.__ModuleLoader__.load({
|
|
|
1148
1168
|
plazaLoadFailed: "The skill plaza could not be loaded. Try again.",
|
|
1149
1169
|
plazaActionFailed: "The action could not be completed. Try again.",
|
|
1150
1170
|
plazaRetry: "Try again",
|
|
1151
|
-
plazaRefresh: "Refresh"
|
|
1152
|
-
limitsTitle: "Workspace limits",
|
|
1153
|
-
limitsDescription: "The largest files this workspace may read and upload. Set by an administrator in DataOps.",
|
|
1154
|
-
limitsUnavailable: "Workspace limits are available once this workspace is connected to DataOps.",
|
|
1155
|
-
limitsFileReadLabel: "Largest file to read",
|
|
1156
|
-
limitsUploadLabel: "Largest file to upload",
|
|
1157
|
-
limitsUnit: "Units are MiB.",
|
|
1158
|
-
limitsSave: "Save limits",
|
|
1159
|
-
limitsSaving: "Saving…"
|
|
1171
|
+
plazaRefresh: "Refresh"
|
|
1160
1172
|
};
|
|
1161
1173
|
/** Chinese managed DataOps Settings copy. */
|
|
1162
1174
|
const zh = {
|
|
1163
1175
|
nav: "DataOps",
|
|
1164
1176
|
title: "DataOps",
|
|
1165
|
-
managed: "
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
identityValue: "当前 DataOps JWT",
|
|
1169
|
-
toolsLabel: "DataOps 工具",
|
|
1170
|
-
toolsValue: "每次请求均由 DataOps 授权",
|
|
1177
|
+
managed: "已连接 DataOps",
|
|
1178
|
+
unavailable: "未连接 DataOps。",
|
|
1179
|
+
loadFailed: "加载失败,请重试。",
|
|
1171
1180
|
brandingTitle: "应用万相品牌",
|
|
1172
|
-
brandingDescription: "在导航、新会话欢迎页、浏览器标题和图标中使用万相数据平台品牌。",
|
|
1173
1181
|
brandingToggle: "应用万相品牌",
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1182
|
+
limitsTitle: "文件大小限制",
|
|
1183
|
+
limitsFileReadLabel: "最大读取文件(MiB)",
|
|
1184
|
+
limitsUploadLabel: "最大上传文件(MiB)",
|
|
1185
|
+
limitsInvalid: "请输入以 MiB 为单位的大小。",
|
|
1186
|
+
limitsSave: "保存",
|
|
1187
|
+
limitsSaving: "保存中…",
|
|
1177
1188
|
defaultsTitle: "默认配置",
|
|
1178
|
-
|
|
1179
|
-
defaultsActionTitle: "发布为默认值",
|
|
1180
|
-
defaultsNotAdmin: "只有管理员可以发布默认配置。",
|
|
1181
|
-
defaultsNeverPublished: "尚未发布。",
|
|
1182
|
-
defaultsPublishedAt: "上次发布:",
|
|
1183
|
-
defaultsPublish: "发布默认配置",
|
|
1189
|
+
defaultsPublish: "发布",
|
|
1184
1190
|
defaultsPublishing: "发布中…",
|
|
1185
|
-
|
|
1191
|
+
defaultsPublishedAt: "上次发布",
|
|
1192
|
+
defaultsNeverPublished: "尚未发布",
|
|
1193
|
+
defaultsNotAdmin: "仅管理员可发布",
|
|
1186
1194
|
plazaTitle: "技能广场",
|
|
1187
1195
|
plazaIntro: "其他用户共享的技能。装载后即可在本工作区使用,也可以把自己的技能共享出去。",
|
|
1188
1196
|
plazaSearch: "搜索技能",
|
|
@@ -1198,15 +1206,7 @@ window.__ModuleLoader__.load({
|
|
|
1198
1206
|
plazaLoadFailed: "技能广场加载失败,请重试。",
|
|
1199
1207
|
plazaActionFailed: "操作未完成,请重试。",
|
|
1200
1208
|
plazaRetry: "重试",
|
|
1201
|
-
plazaRefresh: "刷新"
|
|
1202
|
-
limitsTitle: "工作区限制",
|
|
1203
|
-
limitsDescription: "本工作区可读取与上传的最大文件。由 DataOps 管理员设置。",
|
|
1204
|
-
limitsUnavailable: "工作区连接到 DataOps 后可用。",
|
|
1205
|
-
limitsFileReadLabel: "最大读取文件",
|
|
1206
|
-
limitsUploadLabel: "最大上传文件",
|
|
1207
|
-
limitsUnit: "单位:MiB。",
|
|
1208
|
-
limitsSave: "保存限制",
|
|
1209
|
-
limitsSaving: "保存中…"
|
|
1209
|
+
plazaRefresh: "刷新"
|
|
1210
1210
|
};
|
|
1211
1211
|
//#endregion
|
|
1212
1212
|
//#region src/client/index.ts
|