@sparkelf/dsh-dataops-managed 0.3.13 → 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 CHANGED
@@ -46,7 +46,7 @@ window.__ModuleLoader__.load({
46
46
  /** Route the plugin's Host half serves for model sharing. */
47
47
  const MODEL_SYNC_PATH = "/integrations/dataops/model-sync";
48
48
  /** Narrow a response body to the status shape, rejecting anything else. */
49
- function asStatus(value) {
49
+ function asStatus$1(value) {
50
50
  if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
51
51
  const record = value;
52
52
  if (record.followState !== "following" && record.followState !== "detached") return null;
@@ -86,7 +86,7 @@ window.__ModuleLoader__.load({
86
86
  const response = await fetch(MODEL_SYNC_PATH, { headers: { accept: "application/json" } });
87
87
  if (response.status === 503) return null;
88
88
  if (!response.ok) throw new Error("model sync status failed with HTTP " + String(response.status));
89
- return asStatus(await response.json());
89
+ return asStatus$1(await response.json());
90
90
  }
91
91
  /**
92
92
  * Change what this workspace shares.
@@ -104,11 +104,85 @@ window.__ModuleLoader__.load({
104
104
  });
105
105
  if (response.status === 503) return null;
106
106
  if (!response.ok) throw new Error("model sync action failed with HTTP " + String(response.status));
107
+ return asStatus$1(await response.json());
108
+ }
109
+ //#endregion
110
+ //#region src/client/workspace-limits-client.ts
111
+ /** Same-origin workspace-limits calls from the DataOps settings surface. */
112
+ /** Route the plugin's Host half serves for workspace limits. */
113
+ const WORKSPACE_LIMITS_PATH = "/integrations/dataops/workspace-limits";
114
+ /** Narrow a response body to the status, rejecting anything else. */
115
+ function asStatus(value) {
116
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
117
+ const record = value;
118
+ const limits = record.limits;
119
+ if (typeof limits !== "object" || limits === null) return null;
120
+ const fileReadMaxBytes = limits.fileReadMaxBytes;
121
+ const uploadMaxBytes = limits.uploadMaxBytes;
122
+ if (typeof fileReadMaxBytes !== "number" || typeof uploadMaxBytes !== "number") return null;
123
+ return {
124
+ limits: {
125
+ fileReadMaxBytes,
126
+ uploadMaxBytes
127
+ },
128
+ canWrite: record.canWrite === true,
129
+ readOnlyReason: typeof record.readOnlyReason === "string" ? record.readOnlyReason : null
130
+ };
131
+ }
132
+ function readJson(value) {
133
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
134
+ }
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
+ /**
156
+ * Read the workspace limits the panel shows.
157
+ * @returns The status, or null when the workspace is not connected to DataOps.
158
+ */
159
+ async function fetchWorkspaceLimits() {
160
+ const response = await fetch(WORKSPACE_LIMITS_PATH, { credentials: "same-origin" });
161
+ if (response.status === 503) return null;
162
+ if (!response.ok) throw new Error(`Workspace limits read failed with HTTP ${String(response.status)}`);
107
163
  return asStatus(await response.json());
108
164
  }
165
+ /**
166
+ * Store new workspace limits.
167
+ * @param limits - The ceilings to store.
168
+ * @returns The stored status.
169
+ */
170
+ async function saveWorkspaceLimits(limits) {
171
+ const response = await fetch(WORKSPACE_LIMITS_PATH, {
172
+ method: "POST",
173
+ credentials: "same-origin",
174
+ headers: { "content-type": "application/json" },
175
+ body: JSON.stringify(limits)
176
+ });
177
+ if (response.status === 503) return null;
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"));
180
+ const body = readJson(await response.json());
181
+ return body === null ? null : asStatus(body);
182
+ }
109
183
  //#endregion
110
184
  //#region \0dsh-css:/root/projects/dsh-plugins-plus/packages/dataops-managed/src/client/ManagedDataOpsSection.module.css.mjs
111
- const css$2 = "._2dqzjG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}._2dqzjG_title{margin:0;font-size:16px;font-weight:500;line-height:24px}._2dqzjG_statusRow{align-items:center;gap:8px;min-height:24px;font-size:14px;line-height:22px;display:flex}._2dqzjG_description{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}._2dqzjG_details{margin:2px 0 0}._2dqzjG_detailRow{grid-template-columns:minmax(140px,2fr) minmax(0,3fr);gap:20px;padding:12px 0;display:grid}._2dqzjG_detailRow+._2dqzjG_detailRow{border-top:1px solid var(--dsw-alias-border-l2)}._2dqzjG_detailRow dt,._2dqzjG_detailRow dd{margin:0;font-size:14px;line-height:22px}._2dqzjG_detailRow dt{color:var(--dsw-alias-label-secondary)}._2dqzjG_detailRow dd{color:var(--dsw-alias-label-primary)}._2dqzjG_brandingRow{border-top:1px solid var(--dsw-alias-border-l2);align-items:center;gap:20px;padding:14px 0 12px;display:flex}._2dqzjG_brandingCopy{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}._2dqzjG_brandingCopy strong{font-size:14px;font-weight:500;line-height:22px}._2dqzjG_brandingCopy span{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}._2dqzjG_switch{background:var(--dsw-alias-border-l1);cursor:pointer;border:0;border-radius:10px;flex:none;width:34px;height:20px;padding:0;transition:background-color .12s;position:relative}._2dqzjG_switch>span{background:var(--dsw-alias-bg-base);border-radius:50%;width:14px;height:14px;transition:transform .12s;position:absolute;top:3px;left:3px;box-shadow:0 1px 2px #0003}._2dqzjG_switch[data-checked=true]{background:var(--dsw-alias-state-business-primary)}._2dqzjG_switch[data-checked=true]>span{transform:translate(14px)}._2dqzjG_switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}@media (prefers-reduced-motion:reduce){._2dqzjG_switch,._2dqzjG_switch>span{transition:none}}._2dqzjG_modelPanel{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding:14px 0 12px;display:flex}._2dqzjG_modelTitle{margin:0;font-size:14px;font-weight:500;line-height:22px}._2dqzjG_modelDescription{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}._2dqzjG_modelList{flex-direction:column;gap:8px;padding-top:4px;display:flex}._2dqzjG_modelListTitle{font-size:13px;font-weight:500;line-height:20px}._2dqzjG_modelListDescription{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}._2dqzjG_modelRow{align-items:center;gap:16px;min-height:28px;display:flex}._2dqzjG_modelId{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:13px;line-height:20px;overflow:hidden}._2dqzjG_modelAction{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:5px 12px;font-size:13px;line-height:20px}._2dqzjG_modelAction:hover:not(:disabled){background:var(--dsw-alias-bg-hover)}._2dqzjG_modelAction:disabled,._2dqzjG_switch:disabled{cursor:default;opacity:.5}";
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}";
112
186
  const tagId$2 = "@sparkelf/dsh-dataops-managed/ManagedDataOpsSection.module.css";
113
187
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
114
188
  const tag = document.createElement("style");
@@ -118,29 +192,43 @@ window.__ModuleLoader__.load({
118
192
  document.head.appendChild(tag);
119
193
  }
120
194
  var ManagedDataOpsSection_module_css_default = {
121
- "modelTitle": "_2dqzjG_modelTitle",
195
+ "refusal": "_2dqzjG_refusal",
196
+ "row": "_2dqzjG_row",
122
197
  "section": "_2dqzjG_section",
123
- "modelListDescription": "_2dqzjG_modelListDescription",
124
- "statusRow": "_2dqzjG_statusRow",
125
- "description": "_2dqzjG_description",
126
- "modelDescription": "_2dqzjG_modelDescription",
127
- "modelListTitle": "_2dqzjG_modelListTitle",
128
- "modelRow": "_2dqzjG_modelRow",
129
- "modelId": "_2dqzjG_modelId",
198
+ "actions": "_2dqzjG_actions",
199
+ "card": "_2dqzjG_card",
200
+ "heading": "_2dqzjG_heading",
130
201
  "title": "_2dqzjG_title",
131
- "brandingCopy": "_2dqzjG_brandingCopy",
132
- "brandingRow": "_2dqzjG_brandingRow",
133
- "modelPanel": "_2dqzjG_modelPanel",
134
- "detailRow": "_2dqzjG_detailRow",
135
- "switch": "_2dqzjG_switch",
136
- "modelList": "_2dqzjG_modelList",
137
- "details": "_2dqzjG_details",
138
- "modelAction": "_2dqzjG_modelAction"
202
+ "copy": "_2dqzjG_copy",
203
+ "label": "_2dqzjG_label",
204
+ "status": "_2dqzjG_status",
205
+ "meta": "_2dqzjG_meta",
206
+ "fields": "_2dqzjG_fields"
139
207
  };
140
208
  //#endregion
141
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
+ }
142
229
  /**
143
- * Explain the DataOps-managed JWT and permission owner, and share model configuration.
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.
144
232
  * @param props - Settings slot injection values.
145
233
  * @returns The localized managed DataOps section, or nothing before injection.
146
234
  */
@@ -156,40 +244,184 @@ window.__ModuleLoader__.load({
156
244
  children: t("title")
157
245
  }),
158
246
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
159
- className: ManagedDataOpsSection_module_css_default.statusRow,
160
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("managed") })]
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") })]
249
+ }),
250
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandingRow, {
251
+ t,
252
+ enabled: branding.enabled
253
+ }),
254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LimitsCard, { t }),
255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultsCard, { t })
256
+ ]
257
+ });
258
+ }
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
+ })
276
+ });
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,
282
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
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") })]
289
+ })]
290
+ });
291
+ }
292
+ /**
293
+ * The file ceilings DataOps enforces on this workspace.
294
+ *
295
+ * The values live in DataOps and are read through the plugin, which holds the JWT; the panel only
296
+ * ever holds what the administrator is allowed to see.
297
+ * @param props - Translate function from the section.
298
+ * @returns The limits card.
299
+ */
300
+ function LimitsCard({ t }) {
301
+ const [state, setState] = (0, react.useState)({ kind: "loading" });
302
+ const [fileReadMib, setFileReadMib] = (0, react.useState)("");
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);
308
+ const [busy, setBusy] = (0, react.useState)(false);
309
+ const fileReadId = (0, react.useId)();
310
+ const uploadId = (0, react.useId)();
311
+ const apply = (0, react.useCallback)((status) => {
312
+ setState({
313
+ kind: "ready",
314
+ status
315
+ });
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);
322
+ }, []);
323
+ const refresh = (0, react.useCallback)(async () => {
324
+ try {
325
+ const status = await fetchWorkspaceLimits();
326
+ if (status === null) setState({ kind: "unavailable" });
327
+ else apply(status);
328
+ } catch {
329
+ setState({ kind: "failed" });
330
+ }
331
+ }, [apply]);
332
+ (0, react.useEffect)(() => {
333
+ refresh();
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
+ }, []);
345
+ const submit = (0, react.useCallback)(async () => {
346
+ if (fileReadBytes === null || uploadBytes === null) return;
347
+ setBusy(true);
348
+ setRefusal(null);
349
+ try {
350
+ const status = await saveWorkspaceLimits({
351
+ fileReadMaxBytes: fileReadBytes,
352
+ uploadMaxBytes: uploadBytes
353
+ });
354
+ if (status === null) setState({ kind: "unavailable" });
355
+ else apply(status);
356
+ } catch (error) {
357
+ if (error instanceof WorkspaceLimitsRejection) setRefusal(error.message);
358
+ else setState({ kind: "failed" });
359
+ } finally {
360
+ setBusy(false);
361
+ }
362
+ }, [
363
+ fileReadBytes,
364
+ uploadBytes,
365
+ apply
366
+ ]);
367
+ if (state.kind === "loading") return null;
368
+ if (state.kind !== "ready") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UnavailableCard, {
369
+ title: t("limitsTitle"),
370
+ t,
371
+ state: state.kind
372
+ });
373
+ const disabled = busy || !writable;
374
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
375
+ className: ManagedDataOpsSection_module_css_default.card,
376
+ children: [
377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
378
+ className: ManagedDataOpsSection_module_css_default.heading,
379
+ children: t("limitsTitle")
161
380
  }),
162
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
163
- className: ManagedDataOpsSection_module_css_default.description,
164
- children: t("description")
381
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
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
+ }) })]
165
410
  }),
166
411
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
167
- className: ManagedDataOpsSection_module_css_default.brandingRow,
168
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
169
- className: ManagedDataOpsSection_module_css_default.brandingCopy,
170
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("brandingTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("brandingDescription") })]
171
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
172
- type: "button",
173
- className: ManagedDataOpsSection_module_css_default.switch,
174
- role: "switch",
175
- "aria-label": t("brandingToggle"),
176
- "aria-checked": branding.enabled,
177
- "data-checked": branding.enabled,
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,
178
421
  onClick: () => {
179
- setWanxiangBrandEnabled(!branding.enabled);
422
+ submit();
180
423
  },
181
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
182
- })]
183
- }),
184
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PublishDefaults, { t }),
185
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
186
- className: ManagedDataOpsSection_module_css_default.details,
187
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
188
- className: ManagedDataOpsSection_module_css_default.detailRow,
189
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("identityLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("identityValue") })]
190
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
191
- className: ManagedDataOpsSection_module_css_default.detailRow,
192
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("toolsLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("toolsValue") })]
424
+ children: busy ? t("limitsSaving") : t("limitsSave")
193
425
  })]
194
426
  })
195
427
  ]
@@ -199,13 +431,11 @@ window.__ModuleLoader__.load({
199
431
  * Publish this workspace's configuration as the default other users start from.
200
432
  *
201
433
  * One action rather than a set of controls: the configuration is whatever this workspace already
202
- * holds -- the models, the permission default, the general settings -- and the button copies it to
203
- * DataOps. Every other user's workspace takes that copy as its starting values and may then change
204
- * anything, at which point their own version wins and this button never reaches it again.
434
+ * holds, and the button copies it to DataOps.
205
435
  * @param props - Translate function from the section.
206
- * @returns The publish row.
436
+ * @returns The default-configuration card.
207
437
  */
208
- function PublishDefaults({ t }) {
438
+ function DefaultsCard({ t }) {
209
439
  const [state, setState] = (0, react.useState)({ kind: "loading" });
210
440
  const [busy, setBusy] = (0, react.useState)(false);
211
441
  const refresh = (0, react.useCallback)(async () => {
@@ -226,7 +456,8 @@ window.__ModuleLoader__.load({
226
456
  setBusy(true);
227
457
  try {
228
458
  const status = await postModelSyncAction({ action: "publish-defaults" });
229
- if (status !== null) setState({
459
+ if (status === null) setState({ kind: "unavailable" });
460
+ else setState({
230
461
  kind: "ready",
231
462
  status
232
463
  });
@@ -237,55 +468,38 @@ window.__ModuleLoader__.load({
237
468
  }
238
469
  }, []);
239
470
  if (state.kind === "loading") return null;
240
- if (state.kind === "unavailable" || state.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
241
- className: ManagedDataOpsSection_module_css_default.modelPanel,
242
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
243
- className: ManagedDataOpsSection_module_css_default.modelTitle,
244
- children: t("defaultsTitle")
245
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
246
- className: ManagedDataOpsSection_module_css_default.modelDescription,
247
- role: state.kind === "failed" ? "alert" : void 0,
248
- children: state.kind === "failed" ? t("loadFailed") : t("modelsUnavailable")
249
- })]
471
+ if (state.kind !== "ready") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UnavailableCard, {
472
+ title: t("defaultsTitle"),
473
+ t,
474
+ state: state.kind
250
475
  });
251
- const { status } = state;
252
- if (!status.canPublish) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
253
- className: ManagedDataOpsSection_module_css_default.modelPanel,
254
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
255
- className: ManagedDataOpsSection_module_css_default.modelTitle,
256
- children: t("defaultsTitle")
257
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
258
- className: ManagedDataOpsSection_module_css_default.modelDescription,
259
- children: t("defaultsNotAdmin")
260
- })]
261
- });
262
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
263
- className: ManagedDataOpsSection_module_css_default.modelPanel,
264
- children: [
265
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
266
- className: ManagedDataOpsSection_module_css_default.modelTitle,
267
- children: t("defaultsTitle")
268
- }),
269
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
270
- className: ManagedDataOpsSection_module_css_default.modelDescription,
271
- children: t("defaultsDescription")
272
- }),
273
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
274
- className: ManagedDataOpsSection_module_css_default.brandingRow,
275
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
276
- className: ManagedDataOpsSection_module_css_default.brandingCopy,
277
- 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 })]
278
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
279
- type: "button",
280
- className: ManagedDataOpsSection_module_css_default.modelAction,
281
- disabled: busy,
282
- onClick: () => {
283
- publish();
284
- },
285
- children: busy ? t("defaultsPublishing") : t("defaultsPublish")
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
286
489
  })]
287
- })
288
- ]
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
+ })
289
503
  });
290
504
  }
291
505
  //#endregion
@@ -362,28 +576,28 @@ window.__ModuleLoader__.load({
362
576
  document.head.appendChild(tag);
363
577
  }
364
578
  var SkillPlazaSection_module_css_default = {
365
- "cardTitle": "_9FdZvq_cardTitle",
366
- "title": "_9FdZvq_title",
579
+ "toolbar": "_9FdZvq_toolbar",
580
+ "card": "_9FdZvq_card",
367
581
  "mine": "_9FdZvq_mine",
368
- "intro": "_9FdZvq_intro",
582
+ "cardMarks": "_9FdZvq_cardMarks",
583
+ "emptyText": "_9FdZvq_emptyText",
369
584
  "tagBar": "_9FdZvq_tagBar",
585
+ "intro": "_9FdZvq_intro",
586
+ "cardTitle": "_9FdZvq_cardTitle",
587
+ "emptyIcon": "_9FdZvq_emptyIcon",
588
+ "search": "_9FdZvq_search",
370
589
  "notice": "_9FdZvq_notice",
590
+ "cardMain": "_9FdZvq_cardMain",
591
+ "cardEnd": "_9FdZvq_cardEnd",
592
+ "title": "_9FdZvq_title",
593
+ "cardIcon": "_9FdZvq_cardIcon",
371
594
  "cardHead": "_9FdZvq_cardHead",
372
- "card": "_9FdZvq_card",
373
595
  "cards": "_9FdZvq_cards",
374
- "cardMarks": "_9FdZvq_cardMarks",
375
- "section": "_9FdZvq_section",
376
- "cardEnd": "_9FdZvq_cardEnd",
377
- "emptyText": "_9FdZvq_emptyText",
378
- "head": "_9FdZvq_head",
379
596
  "cardDesc": "_9FdZvq_cardDesc",
380
- "empty": "_9FdZvq_empty",
381
597
  "titleIcon": "_9FdZvq_titleIcon",
382
- "toolbar": "_9FdZvq_toolbar",
383
- "search": "_9FdZvq_search",
384
- "emptyIcon": "_9FdZvq_emptyIcon",
385
- "cardMain": "_9FdZvq_cardMain",
386
- "cardIcon": "_9FdZvq_cardIcon"
598
+ "head": "_9FdZvq_head",
599
+ "empty": "_9FdZvq_empty",
600
+ "section": "_9FdZvq_section"
387
601
  };
388
602
  //#endregion
389
603
  //#region src/client/SkillPlazaSection.tsx
@@ -635,18 +849,18 @@ window.__ModuleLoader__.load({
635
849
  document.head.appendChild(tag);
636
850
  }
637
851
  var WanxiangBrand_module_css_default = {
638
- "animatedMark": "s0HPxG_animatedMark",
639
- "unit-top": "s0HPxG_unit-top",
640
- "sidebarName": "s0HPxG_sidebarName",
641
- "unitTop": "s0HPxG_unitTop",
642
- "unitLeft": "s0HPxG_unitLeft",
643
852
  "unit-left": "s0HPxG_unit-left",
644
- "unit-right": "s0HPxG_unit-right",
853
+ "unit-top": "s0HPxG_unit-top",
645
854
  "mark": "s0HPxG_mark",
646
855
  "unit-core": "s0HPxG_unit-core",
856
+ "sidebarName": "s0HPxG_sidebarName",
857
+ "unit-right": "s0HPxG_unit-right",
858
+ "animatedMark": "s0HPxG_animatedMark",
859
+ "unitLeft": "s0HPxG_unitLeft",
860
+ "unitCore": "s0HPxG_unitCore",
647
861
  "unitRight": "s0HPxG_unitRight",
648
- "harness": "s0HPxG_harness",
649
- "unitCore": "s0HPxG_unitCore"
862
+ "unitTop": "s0HPxG_unitTop",
863
+ "harness": "s0HPxG_harness"
650
864
  };
651
865
  //#endregion
652
866
  //#region src/client/WanxiangBrand.tsx
@@ -922,27 +1136,23 @@ window.__ModuleLoader__.load({
922
1136
  const en = {
923
1137
  nav: "DataOps",
924
1138
  title: "DataOps",
925
- managed: "Managed by this DataOps workspace",
926
- description: "DSH uses your current DataOps session JWT. DataOps applies its role and resource permissions to every tool request.",
927
- identityLabel: "Identity and permissions",
928
- identityValue: "Current DataOps JWT",
929
- toolsLabel: "DataOps tools",
930
- toolsValue: "Authorized by DataOps on every request",
931
- brandingTitle: "Apply Wanxiang branding",
932
- brandingDescription: "Use the Wanxiang Data Platform identity in navigation, the New Session welcome view, browser title, and favicon.",
933
- brandingToggle: "Apply Wanxiang branding",
934
- modelsTitle: "Model configuration",
935
- modelsDescription: "Share the models configured here with other users, and choose which of them they may use.",
936
- 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…",
937
1150
  defaultsTitle: "Default configuration",
938
- defaultsDescription: "Publish the configuration this workspace holds — models, permissions, and general settings — as the values other users start from. Their workspace takes that copy once and may then change anything, and a change they make is never overwritten.",
939
- defaultsActionTitle: "Publish as the default",
940
- defaultsNotAdmin: "Only an administrator may publish the default configuration.",
941
- defaultsNeverPublished: "Not published yet.",
942
- defaultsPublishedAt: "Last published:",
943
- defaultsPublish: "Publish default configuration",
1151
+ defaultsPublish: "Publish",
944
1152
  defaultsPublishing: "Publishing…",
945
- loadFailed: "The model configuration could not be loaded. Try again.",
1153
+ defaultsPublishedAt: "Last published",
1154
+ defaultsNeverPublished: "Not published yet",
1155
+ defaultsNotAdmin: "Administrators only",
946
1156
  plazaTitle: "Skill plaza",
947
1157
  plazaIntro: "Skills other users shared. Install one to use it in this workspace, or share a skill of your own.",
948
1158
  plazaSearch: "Search skills",
@@ -964,27 +1174,23 @@ window.__ModuleLoader__.load({
964
1174
  const zh = {
965
1175
  nav: "DataOps",
966
1176
  title: "DataOps",
967
- managed: "由当前 DataOps 工作区托管",
968
- description: "DSH 使用当前 DataOps 会话 JWT;DataOps 对每次工具请求应用角色与资源权限。",
969
- identityLabel: "身份与权限",
970
- identityValue: "当前 DataOps JWT",
971
- toolsLabel: "DataOps 工具",
972
- toolsValue: "每次请求均由 DataOps 授权",
1177
+ managed: "已连接 DataOps",
1178
+ unavailable: "未连接 DataOps。",
1179
+ loadFailed: "加载失败,请重试。",
973
1180
  brandingTitle: "应用万相品牌",
974
- brandingDescription: "在导航、新会话欢迎页、浏览器标题和图标中使用万相数据平台品牌。",
975
1181
  brandingToggle: "应用万相品牌",
976
- modelsTitle: "模型配置共享",
977
- modelsDescription: "把这里配置的模型共享给其他用户,并决定他们可以使用其中哪些。",
978
- modelsUnavailable: "工作区接入 DataOps 后即可使用模型配置共享。",
1182
+ limitsTitle: "文件大小限制",
1183
+ limitsFileReadLabel: "最大读取文件(MiB)",
1184
+ limitsUploadLabel: "最大上传文件(MiB)",
1185
+ limitsInvalid: "请输入以 MiB 为单位的大小。",
1186
+ limitsSave: "保存",
1187
+ limitsSaving: "保存中…",
979
1188
  defaultsTitle: "默认配置",
980
- defaultsDescription: "把本工作区当前的配置(模型、权限、通用设置)发布为其他用户的初始值。对方工作区只取用一次,之后可以随意修改,改过的内容不会被覆盖。",
981
- defaultsActionTitle: "发布为默认值",
982
- defaultsNotAdmin: "只有管理员可以发布默认配置。",
983
- defaultsNeverPublished: "尚未发布。",
984
- defaultsPublishedAt: "上次发布:",
985
- defaultsPublish: "发布默认配置",
1189
+ defaultsPublish: "发布",
986
1190
  defaultsPublishing: "发布中…",
987
- loadFailed: "模型配置加载失败,请重试。",
1191
+ defaultsPublishedAt: "上次发布",
1192
+ defaultsNeverPublished: "尚未发布",
1193
+ defaultsNotAdmin: "仅管理员可发布",
988
1194
  plazaTitle: "技能广场",
989
1195
  plazaIntro: "其他用户共享的技能。装载后即可在本工作区使用,也可以把自己的技能共享出去。",
990
1196
  plazaSearch: "搜索技能",