dsh-modellix 0.1.0 → 0.2.0

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 (37) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +101 -169
  3. package/README.zh-CN.md +99 -167
  4. package/docs/assets/chat-media-generation-en.webp +0 -0
  5. package/docs/assets/chat-media-generation-zh.webp +0 -0
  6. package/docs/assets/design-results-drawer-en.webp +0 -0
  7. package/docs/assets/design-results-drawer-zh.webp +0 -0
  8. package/docs/assets/llm-model-selector-en.webp +0 -0
  9. package/docs/assets/llm-model-selector-zh.webp +0 -0
  10. package/docs/assets/media-players-en.webp +0 -0
  11. package/docs/assets/media-players-zh.webp +0 -0
  12. package/docs/assets/settings-ready-en.webp +0 -0
  13. package/docs/assets/settings-ready-zh.webp +0 -0
  14. package/docs/assets/web-tools-auto-en.webp +0 -0
  15. package/docs/assets/web-tools-auto-zh.webp +0 -0
  16. package/docs/en-US/LOCAL_USAGE.md +17 -15
  17. package/docs/en-US/RELEASE_CHECKLIST.md +270 -0
  18. package/docs/en-US/USER_GUIDE.md +183 -401
  19. package/docs/zh-CN/LOCAL_USAGE.md +17 -15
  20. package/docs/zh-CN/RELEASE_CHECKLIST.md +270 -0
  21. package/docs/zh-CN/USER_GUIDE.md +182 -400
  22. package/lib/client.d.ts +16 -2
  23. package/lib/client.js +1308 -466
  24. package/lib/client.js.map +1 -1
  25. package/lib/index.d.ts +159 -109
  26. package/lib/index.js +995 -192
  27. package/lib/index.js.map +1 -1
  28. package/package.json +7 -2
  29. package/docs/assets/credential-recovery.webp +0 -0
  30. package/docs/assets/design-desktop.webp +0 -0
  31. package/docs/assets/design-mobile-en.webp +0 -0
  32. package/docs/assets/design-proposal.webp +0 -0
  33. package/docs/assets/design-results-media.webp +0 -0
  34. package/docs/assets/llm-model-selector.webp +0 -0
  35. package/docs/assets/onboarding-defaults.webp +0 -0
  36. package/docs/assets/settings-ready.webp +0 -0
  37. package/docs/assets/web-tools.webp +0 -0
package/lib/client.js CHANGED
@@ -342,6 +342,8 @@ function parseModel(value) {
342
342
  "audio",
343
343
  "unknown"
344
344
  ], "model.kind"),
345
+ ...model.taskType === void 0 ? {} : { taskType: readable(model.taskType, "model.taskType", 64) },
346
+ ...model.description === void 0 ? {} : { description: readable(model.description, "model.description", MAX_SHORT_TEXT) },
345
347
  featured: boolean(model.featured, "model.featured"),
346
348
  available: boolean(model.available, "model.available"),
347
349
  unavailableReason: model.unavailableReason === null ? null : oneOf(model.unavailableReason, DESIGN_MODEL_UNAVAILABLE_CODES, "model.unavailableReason")
@@ -440,7 +442,7 @@ function parseJob(value) {
440
442
  createdAt: isoTimestamp(job.createdAt, "job.createdAt"),
441
443
  updatedAt: isoTimestamp(job.updatedAt, "job.updatedAt"),
442
444
  resources: array(job.resources, "job.resources", MAX_RESOURCES).map(parseResource),
443
- diagnostic: job.diagnostic === null ? null : parseDiagnostic(job.diagnostic)
445
+ diagnostic: job.diagnostic === null ? null : parseDiagnostic$1(job.diagnostic)
444
446
  };
445
447
  }
446
448
  function parseResource(value) {
@@ -457,7 +459,7 @@ function parseResource(value) {
457
459
  expiresAt: resource.expiresAt === null ? null : isoTimestamp(resource.expiresAt, "resource.expiresAt")
458
460
  };
459
461
  }
460
- function parseDiagnostic(value) {
462
+ function parseDiagnostic$1(value) {
461
463
  const diagnostic = object(value, "diagnostic");
462
464
  if (Object.hasOwn(diagnostic, "message")) throw new ModellixClientContractError("diagnostic must use a stable code instead of Host prose");
463
465
  return {
@@ -911,7 +913,7 @@ const MODAL_DIALOG_SELECTOR = "[role=\"dialog\"][aria-modal=\"true\"]";
911
913
  * therefore uses only the ARIA modal contract and a marker owned by this
912
914
  * plugin; it does not depend on host classes or DOM structure.
913
915
  */
914
- function useExternalDialogGate(requestedOpen) {
916
+ function useExternalDialogGate(requestedOpen, externalDialogOwner) {
915
917
  const [decision, setDecision] = (0, react.useState)({
916
918
  requestedOpen: false,
917
919
  allowed: false
@@ -931,7 +933,7 @@ function useExternalDialogGate(requestedOpen) {
931
933
  const reconcile = () => {
932
934
  reconciliationQueued = false;
933
935
  if (disposed) return;
934
- const allowed = !hasVisibleExternalDialog(ownerDocument);
936
+ const allowed = !hasVisibleExternalDialog(ownerDocument, resolveExternalDialogOwner(externalDialogOwner));
935
937
  setDecision((current) => current.requestedOpen && current.allowed === allowed ? current : {
936
938
  requestedOpen: true,
937
939
  allowed
@@ -962,12 +964,12 @@ function useExternalDialogGate(requestedOpen) {
962
964
  disposed = true;
963
965
  observer?.disconnect();
964
966
  };
965
- }, [requestedOpen]);
967
+ }, [externalDialogOwner, requestedOpen]);
966
968
  return requestedOpen && decision.requestedOpen && decision.allowed;
967
969
  }
968
970
  /** Adds the focus behavior intentionally absent from the public Modal primitive. */
969
971
  function useDialogA11y(options) {
970
- const { open, container, initialFocusSelector, mandatory, onEscape } = options;
972
+ const { open, container, externalDialogOwner, initialFocusSelector, mandatory, onEscape } = options;
971
973
  const onEscapeRef = (0, react.useRef)(onEscape);
972
974
  const restoreTargetRef = (0, react.useRef)(null);
973
975
  onEscapeRef.current = onEscape;
@@ -1019,13 +1021,14 @@ function useDialogA11y(options) {
1019
1021
  document.removeEventListener("keydown", onKeyDown, true);
1020
1022
  unlockInert();
1021
1023
  unlockScroll();
1022
- if (!hasVisibleExternalDialog(document)) {
1024
+ if (!hasVisibleExternalDialog(document, resolveExternalDialogOwner(externalDialogOwner))) {
1023
1025
  restoreFocus(restoreTargetRef.current, dialogRoot, appRoot);
1024
1026
  restoreTargetRef.current = null;
1025
1027
  }
1026
1028
  };
1027
1029
  }, [
1028
1030
  container,
1031
+ externalDialogOwner,
1029
1032
  initialFocusSelector,
1030
1033
  mandatory,
1031
1034
  open
@@ -1111,8 +1114,11 @@ function restoreFocus(previous, dialog, appRoot) {
1111
1114
  if (fallback !== void 0 && focus(fallback)) return;
1112
1115
  }
1113
1116
  }
1114
- function hasVisibleExternalDialog(ownerDocument) {
1115
- return [...ownerDocument.querySelectorAll(MODAL_DIALOG_SELECTOR)].some((candidate) => candidate.querySelector(MODELLIX_DIALOG_SENTINEL) === null && isVisibleDialog(candidate));
1117
+ function hasVisibleExternalDialog(ownerDocument, allowedOwner = null) {
1118
+ return [...ownerDocument.querySelectorAll(MODAL_DIALOG_SELECTOR)].some((candidate) => candidate !== allowedOwner && candidate.querySelector(MODELLIX_DIALOG_SENTINEL) === null && isVisibleDialog(candidate));
1119
+ }
1120
+ function resolveExternalDialogOwner(owner) {
1121
+ return owner?.current?.closest(MODAL_DIALOG_SELECTOR) ?? null;
1116
1122
  }
1117
1123
  function isVisibleDialog(candidate) {
1118
1124
  if (candidate.tagName === "DIALOG" && !candidate.hasAttribute("open")) return false;
@@ -1276,7 +1282,7 @@ function CredentialStatus({ configured, source, verification, t }) {
1276
1282
  });
1277
1283
  }
1278
1284
  function CredentialModal(props) {
1279
- const { open, mandatory, title, description, services, onServicesChange, busy, errorCode, onSave, onSaved, onCancel, laterLabel = "cancel", t } = props;
1285
+ const { open, mandatory, title, description, services, onServicesChange, busy, errorCode, onSave, onSaved, onCancel, laterLabel = "cancel", t, externalDialogOwner } = props;
1280
1286
  const [draft, setDraft] = (0, react.useState)("");
1281
1287
  const [visible, setVisible] = (0, react.useState)(false);
1282
1288
  const [displayedErrorCode, setDisplayedErrorCode] = (0, react.useState)(errorCode);
@@ -1286,7 +1292,7 @@ function CredentialModal(props) {
1286
1292
  const helpId = `${keyId}-help`;
1287
1293
  const errorId = `${keyId}-error`;
1288
1294
  const errorPresentation = displayedErrorCode === null ? null : presentClientError(displayedErrorCode);
1289
- const surfaceOpen = useExternalDialogGate(open);
1295
+ const surfaceOpen = useExternalDialogGate(open, externalDialogOwner);
1290
1296
  const clear = (0, react.useCallback)(() => {
1291
1297
  draftRef.current = "";
1292
1298
  setDraft("");
@@ -1301,6 +1307,7 @@ function CredentialModal(props) {
1301
1307
  useDialogA11y({
1302
1308
  open: surfaceOpen,
1303
1309
  container: contentRef,
1310
+ externalDialogOwner,
1304
1311
  initialFocusSelector: "[data-mdlx-initial-focus]",
1305
1312
  mandatory,
1306
1313
  onEscape: close
@@ -1686,6 +1693,11 @@ var CredentialDialogCoordinator = class {
1686
1693
  if (this.#snapshot.activeOwner !== null) return;
1687
1694
  this.#publish({ activeOwner: owner });
1688
1695
  }
1696
+ /** Lets an explicit Credential editor adopt a queued shell recovery prompt. */
1697
+ openCredential(owner) {
1698
+ if (this.#snapshot.activeOwner !== null && this.#snapshot.activeOwner !== "shell:credential-recovery") return;
1699
+ this.#publish({ activeOwner: owner });
1700
+ }
1689
1701
  presentRecovery(token) {
1690
1702
  if (token === this.#snapshot.dismissedRecoveryToken) return;
1691
1703
  if (token === this.#snapshot.recoveryToken) {
@@ -1916,8 +1928,28 @@ function DesignResultPreview({ resource, t }) {
1916
1928
  });
1917
1929
  }
1918
1930
  //#endregion
1931
+ //#region src/client/result-tabs.ts
1932
+ /** Implements the WAI-ARIA Tabs arrow/Home/End keyboard pattern. */
1933
+ function handleResultTabKeyDown(event) {
1934
+ if (![
1935
+ "ArrowLeft",
1936
+ "ArrowRight",
1937
+ "Home",
1938
+ "End"
1939
+ ].includes(event.key)) return;
1940
+ const tabs = [...event.currentTarget.querySelectorAll("[role=\"tab\"]:not([disabled])")];
1941
+ if (tabs.length === 0) return;
1942
+ const active = tabs.findIndex((tab) => tab === event.target);
1943
+ if (active < 0) return;
1944
+ const next = event.key === "Home" ? tabs[0] : event.key === "End" ? tabs.at(-1) : tabs[(active + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length];
1945
+ if (next === void 0) return;
1946
+ event.preventDefault();
1947
+ next.focus();
1948
+ next.click();
1949
+ }
1950
+ //#endregion
1919
1951
  //#region src/client/DesignView.tsx
1920
- function ModellixDesignView({ controller, settingsController, t }) {
1952
+ function ModellixDesignView({ advancedEditorOpen = true, controller, settingsController, onAddUrl, t }) {
1921
1953
  const state = useResourceState(controller.store);
1922
1954
  const settingsState = useResourceState(settingsController.store);
1923
1955
  const snapshot = state.data;
@@ -1989,6 +2021,9 @@ function ModellixDesignView({ controller, settingsController, t }) {
1989
2021
  (0, react.useEffect)(() => {
1990
2022
  if (state.pending !== null) setOutcomeAnnouncement("");
1991
2023
  }, [state.pending]);
2024
+ (0, react.useEffect)(() => {
2025
+ setOutcomeAnnouncement("");
2026
+ }, [t]);
1992
2027
  (0, react.useEffect)(() => {
1993
2028
  if (snapshot === null) return;
1994
2029
  const transition = designOutcomeTransition(previousOutcome.current, snapshot);
@@ -2056,307 +2091,294 @@ function ModellixDesignView({ controller, settingsController, t }) {
2056
2091
  className: "mdlx-design",
2057
2092
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2058
2093
  className: "mdlx-design-shell",
2059
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2060
- className: "mdlx-design-pane",
2061
- "aria-labelledby": "mdlx-design-title",
2062
- children: [
2063
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2064
- className: "mdlx-heading",
2065
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2066
- id: "mdlx-design-title",
2067
- children: t("designTitle")
2068
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2069
- className: "mdlx-muted",
2070
- children: t("designDescription")
2071
- })]
2072
- }),
2073
- !snapshot.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2074
- className: "mdlx-info",
2075
- children: t("designDisabled")
2076
- }),
2077
- !snapshot.credentialReady && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2078
- className: "mdlx-info",
2079
- children: [
2080
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("keyRequired") }),
2081
- settings?.credential.writable === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2082
- type: "button",
2083
- variant: "primary",
2084
- onClick: () => credentialDialogs.open(credentialDialogOwner),
2085
- children: t("configureToContinue")
2086
- }),
2087
- settings?.credential.source === "env" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2088
- className: "mdlx-muted",
2089
- children: t("envReadonly")
2090
- })
2091
- ]
2092
- }),
2093
- snapshot.notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2094
- className: "mdlx-info",
2095
- role: "status",
2096
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("notice"), ": "] }), t(designNoticeMessageKey(snapshot.notice))]
2097
- }),
2098
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2099
- className: "mdlx-field",
2100
- children: [
2101
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2102
- className: "mdlx-label",
2103
- htmlFor: "mdlx-design-model",
2104
- children: t("modelLabel")
2105
- }),
2106
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2107
- className: "mdlx-model-tools",
2108
- children: [
2109
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2110
- className: "mdlx-input",
2111
- value: modelQuery,
2112
- maxLength: 512,
2113
- placeholder: t("modelSearchPlaceholder"),
2114
- "aria-label": t("modelSearchLabel"),
2115
- disabled: interactionBusy || !snapshot.enabled,
2116
- onChange: (event) => setModelQuery(event.currentTarget.value)
2117
- }),
2118
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2119
- className: "mdlx-select",
2120
- value: modelKind,
2121
- "aria-label": t("modelCategoryLabel"),
2122
- disabled: interactionBusy || !snapshot.enabled,
2123
- onChange: (event) => {
2124
- setModelKind(event.currentTarget.value);
2125
- },
2126
- children: [
2127
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2128
- value: "all",
2129
- children: t("modelCategoryAll")
2130
- }),
2131
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2132
- value: "image",
2133
- children: t("modelCategoryImage")
2134
- }),
2135
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2136
- value: "video",
2137
- children: t("modelCategoryVideo")
2138
- }),
2139
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2140
- value: "audio",
2141
- children: t("modelCategoryAudio")
2142
- })
2143
- ]
2144
- }),
2145
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2094
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2095
+ id: "mdlx-design-editor",
2096
+ className: "mdlx-design-editor",
2097
+ hidden: !advancedEditorOpen,
2098
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2099
+ className: "mdlx-design-pane",
2100
+ "aria-label": t("advancedEditor"),
2101
+ children: [
2102
+ !snapshot.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2103
+ className: "mdlx-info",
2104
+ children: t("designDisabled")
2105
+ }),
2106
+ !snapshot.credentialReady && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2107
+ className: "mdlx-info",
2108
+ children: [
2109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("keyRequired") }),
2110
+ settings?.credential.writable === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2111
+ type: "button",
2112
+ variant: "primary",
2113
+ onClick: () => credentialDialogs.openCredential(credentialDialogOwner),
2114
+ children: t("configureToContinue")
2115
+ }),
2116
+ settings?.credential.source === "env" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2117
+ className: "mdlx-muted",
2118
+ children: t("envReadonly")
2119
+ })
2120
+ ]
2121
+ }),
2122
+ snapshot.notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2123
+ className: "mdlx-info",
2124
+ role: "status",
2125
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("notice"), ": "] }), t(designNoticeMessageKey(snapshot.notice))]
2126
+ }),
2127
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2128
+ className: "mdlx-card",
2129
+ "aria-labelledby": "mdlx-assistant-title",
2130
+ children: [
2131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2132
+ className: "mdlx-heading",
2133
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2134
+ id: "mdlx-assistant-title",
2135
+ children: t("assistantTitle")
2136
+ })
2137
+ }),
2138
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2139
+ id: "mdlx-assistant-instruction",
2140
+ className: "mdlx-textarea mdlx-textarea-small",
2141
+ value: instruction,
2142
+ maxLength: 8e3,
2143
+ placeholder: t("assistantPlaceholder"),
2144
+ "aria-labelledby": "mdlx-assistant-title",
2145
+ disabled: interactionBusy,
2146
+ onChange: (event) => setInstruction(event.currentTarget.value)
2147
+ }),
2148
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2149
+ className: "mdlx-actions",
2150
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2146
2151
  type: "button",
2147
2152
  variant: "outline",
2148
- disabled: interactionBusy || !snapshot.enabled || !snapshot.credentialReady,
2149
- "aria-busy": state.pending === "refresh-design",
2153
+ disabled: interactionBusy || instruction.trim().length === 0,
2154
+ "aria-busy": state.pending === "propose",
2150
2155
  onClick: () => {
2151
- controller.refreshCatalog();
2156
+ const request = instruction.trim();
2157
+ if (request === "") return;
2158
+ controller.propose(request, parameters).then((accepted) => {
2159
+ if (accepted) setInstruction("");
2160
+ });
2152
2161
  },
2153
- children: state.pending === "refresh-design" ? t("modelRefreshing") : t("modelRefresh")
2162
+ children: state.pending === "propose" ? t("proposing") : t("propose")
2154
2163
  })
2155
- ]
2156
- }),
2157
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2158
- id: "mdlx-design-model",
2159
- className: "mdlx-select",
2160
- value: snapshot.selectedModelId ?? "",
2161
- "aria-describedby": selectedModel?.available === false ? "mdlx-design-model-status" : void 0,
2162
- disabled: interactionBusy || !snapshot.enabled || snapshot.models.length === 0,
2163
- onChange: (event) => {
2164
- const modelId = event.currentTarget.value;
2165
- if (modelId !== "") controller.selectModel(modelId);
2166
- },
2167
- children: [
2168
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2169
- value: "",
2170
- disabled: true,
2171
- children: t("chooseModel")
2172
- }),
2173
- snapshot.selectedModelId !== null && !visibleModels.some((model) => model.id === snapshot.selectedModelId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2174
- value: snapshot.selectedModelId,
2175
- disabled: selectedModel?.available === false,
2176
- children: snapshot.models.find((model) => model.id === snapshot.selectedModelId)?.label ?? snapshot.selectedModelId
2177
- }),
2178
- visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2179
- value: model.id,
2180
- disabled: !model.available,
2181
- children: [model.label, model.featured ? " ★" : ""]
2182
- }, model.id))
2183
- ]
2184
- }),
2185
- selectedModel?.available === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2186
- id: "mdlx-design-model-status",
2187
- className: "mdlx-error",
2188
- role: "status",
2189
- children: selectedModel.unavailableReason === null ? t("modelUnavailable") : t(designModelUnavailableMessageKey(selectedModel.unavailableReason))
2190
- }),
2191
- snapshot.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2192
- className: "mdlx-help",
2193
- children: t("noModels")
2194
- }) : visibleModels.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2195
- className: "mdlx-help",
2196
- children: t("noMatchingModels")
2197
- })
2198
- ]
2199
- }),
2200
- draft !== null && promptPath !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2201
- className: "mdlx-field",
2202
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2203
- className: "mdlx-label",
2204
- htmlFor: "mdlx-design-prompt",
2164
+ })
2165
+ ]
2166
+ }),
2167
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2168
+ className: "mdlx-field",
2205
2169
  children: [
2206
- t("promptLabel"),
2207
- " ",
2208
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2209
- className: "mdlx-required",
2210
- children: t("required")
2170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2171
+ className: "mdlx-label",
2172
+ htmlFor: "mdlx-design-model",
2173
+ children: t("modelLabel")
2174
+ }),
2175
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2176
+ className: "mdlx-model-tools",
2177
+ children: [
2178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2179
+ className: "mdlx-input",
2180
+ value: modelQuery,
2181
+ maxLength: 512,
2182
+ placeholder: t("modelSearchPlaceholder"),
2183
+ "aria-label": t("modelSearchLabel"),
2184
+ disabled: interactionBusy || !snapshot.enabled,
2185
+ onChange: (event) => setModelQuery(event.currentTarget.value)
2186
+ }),
2187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2188
+ className: "mdlx-select",
2189
+ value: modelKind,
2190
+ "aria-label": t("modelCategoryLabel"),
2191
+ disabled: interactionBusy || !snapshot.enabled,
2192
+ onChange: (event) => {
2193
+ setModelKind(event.currentTarget.value);
2194
+ },
2195
+ children: [
2196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2197
+ value: "all",
2198
+ children: t("modelCategoryAll")
2199
+ }),
2200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2201
+ value: "image",
2202
+ children: t("modelCategoryImage")
2203
+ }),
2204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2205
+ value: "video",
2206
+ children: t("modelCategoryVideo")
2207
+ }),
2208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2209
+ value: "audio",
2210
+ children: t("modelCategoryAudio")
2211
+ })
2212
+ ]
2213
+ }),
2214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2215
+ type: "button",
2216
+ variant: "outline",
2217
+ disabled: interactionBusy || !snapshot.enabled || !snapshot.credentialReady,
2218
+ "aria-busy": state.pending === "refresh-design",
2219
+ onClick: () => {
2220
+ controller.refreshCatalog();
2221
+ },
2222
+ children: state.pending === "refresh-design" ? t("modelRefreshing") : t("modelRefresh")
2223
+ })
2224
+ ]
2225
+ }),
2226
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2227
+ id: "mdlx-design-model",
2228
+ className: "mdlx-select",
2229
+ value: snapshot.selectedModelId ?? "",
2230
+ "aria-describedby": selectedModel?.available === false ? "mdlx-design-model-status" : void 0,
2231
+ disabled: interactionBusy || !snapshot.enabled || snapshot.models.length === 0,
2232
+ onChange: (event) => {
2233
+ const modelId = event.currentTarget.value;
2234
+ if (modelId !== "") controller.selectModel(modelId);
2235
+ },
2236
+ children: [
2237
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2238
+ value: "",
2239
+ disabled: true,
2240
+ children: t("chooseModel")
2241
+ }),
2242
+ snapshot.selectedModelId !== null && !visibleModels.some((model) => model.id === snapshot.selectedModelId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2243
+ value: snapshot.selectedModelId,
2244
+ disabled: selectedModel?.available === false,
2245
+ children: snapshot.models.find((model) => model.id === snapshot.selectedModelId)?.label ?? snapshot.selectedModelId
2246
+ }),
2247
+ visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2248
+ value: model.id,
2249
+ disabled: !model.available,
2250
+ children: [model.label, model.featured ? " ★" : ""]
2251
+ }, model.id))
2252
+ ]
2253
+ }),
2254
+ selectedModel?.available === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2255
+ id: "mdlx-design-model-status",
2256
+ className: "mdlx-error",
2257
+ role: "status",
2258
+ children: selectedModel.unavailableReason === null ? t("modelUnavailable") : t(designModelUnavailableMessageKey(selectedModel.unavailableReason))
2259
+ }),
2260
+ snapshot.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2261
+ className: "mdlx-help",
2262
+ children: t("noModels")
2263
+ }) : visibleModels.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2264
+ className: "mdlx-help",
2265
+ children: t("noMatchingModels")
2211
2266
  })
2212
2267
  ]
2213
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2214
- id: "mdlx-design-prompt",
2215
- className: "mdlx-textarea",
2216
- value: promptValue,
2217
- maxLength: fieldAtPath(draft.fields, promptPath)?.maxLength ?? void 0,
2218
- placeholder: t("promptPlaceholder"),
2219
- disabled: interactionBusy,
2220
- onChange: (event) => updateParameter(promptPath, event.currentTarget.value)
2221
- })]
2222
- }),
2223
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2224
- className: "mdlx-design-scroll",
2225
- "aria-labelledby": "mdlx-parameters-title",
2226
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2227
- className: "mdlx-heading",
2228
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2229
- id: "mdlx-parameters-title",
2230
- children: t("parametersTitle")
2231
- })
2232
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2233
- className: "mdlx-parameter-list",
2234
- children: [requiredFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2235
- field,
2236
- value: parameters[field.path],
2237
- invalid: invalidFields.has(field.path),
2268
+ }),
2269
+ draft !== null && promptPath !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2270
+ className: "mdlx-field",
2271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2272
+ className: "mdlx-label",
2273
+ htmlFor: "mdlx-design-prompt",
2274
+ children: [
2275
+ t("promptLabel"),
2276
+ " ",
2277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2278
+ className: "mdlx-required",
2279
+ children: t("required")
2280
+ })
2281
+ ]
2282
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2283
+ id: "mdlx-design-prompt",
2284
+ className: "mdlx-textarea",
2285
+ value: promptValue,
2286
+ maxLength: fieldAtPath(draft.fields, promptPath)?.maxLength ?? void 0,
2287
+ placeholder: t("promptPlaceholder"),
2238
2288
  disabled: interactionBusy,
2239
- onChange: (value) => updateParameter(field.path, value),
2240
- onValidityChange: (valid) => setFieldValidity(field.path, valid),
2241
- t
2242
- }, field.path)), optionalFields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2243
- className: "mdlx-advanced",
2244
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advancedParameters", { count: optionalFields.length }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2245
- className: "mdlx-parameter-list",
2246
- children: optionalFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2247
- field,
2248
- value: parameters[field.path],
2249
- invalid: invalidFields.has(field.path),
2250
- disabled: interactionBusy,
2251
- onChange: (value) => updateParameter(field.path, value),
2252
- onValidityChange: (valid) => setFieldValidity(field.path, valid),
2253
- t
2254
- }, field.path))
2255
- })]
2289
+ onChange: (event) => updateParameter(promptPath, event.currentTarget.value)
2256
2290
  })]
2257
- })]
2258
- }),
2259
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2260
- className: "mdlx-card",
2261
- "aria-labelledby": "mdlx-assistant-title",
2262
- children: [
2263
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2291
+ }),
2292
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2293
+ className: "mdlx-design-scroll",
2294
+ "aria-labelledby": "mdlx-parameters-title",
2295
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2264
2296
  className: "mdlx-heading",
2265
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2266
- id: "mdlx-assistant-title",
2267
- children: t("assistantTitle")
2268
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2269
- id: "mdlx-assistant-paid-notice",
2270
- className: "mdlx-help",
2271
- children: t("assistantPaidNotice")
2272
- })]
2273
- }),
2274
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2275
- id: "mdlx-assistant-instruction",
2276
- className: "mdlx-textarea mdlx-textarea-small",
2277
- value: instruction,
2278
- maxLength: 8e3,
2279
- placeholder: t("assistantPlaceholder"),
2280
- "aria-labelledby": "mdlx-assistant-title",
2281
- "aria-describedby": "mdlx-assistant-paid-notice",
2282
- disabled: interactionBusy,
2283
- onChange: (event) => setInstruction(event.currentTarget.value)
2284
- }),
2285
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2286
- className: "mdlx-actions",
2287
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2288
- type: "button",
2289
- variant: "outline",
2290
- disabled: interactionBusy || instruction.trim().length === 0,
2291
- "aria-busy": state.pending === "propose",
2292
- onClick: () => {
2293
- const request = instruction.trim();
2294
- if (request === "") return;
2295
- controller.propose(request, parameters).then((accepted) => {
2296
- if (accepted) setInstruction("");
2297
- });
2298
- },
2299
- children: state.pending === "propose" ? t("proposing") : t("propose")
2297
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2298
+ id: "mdlx-parameters-title",
2299
+ children: t("parametersTitle")
2300
2300
  })
2301
- })
2302
- ]
2303
- }),
2304
- snapshot.proposal !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProposalCard, {
2305
- snapshot,
2306
- parameters,
2307
- controller,
2308
- t
2309
- }),
2310
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2311
- className: "mdlx-generate-block",
2312
- children: [
2313
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2314
- className: "mdlx-help",
2315
- children: t("paidNotice")
2316
- }),
2317
- missingRequired && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2318
- className: "mdlx-error",
2319
- children: t("requiredMissing")
2320
- }),
2321
- invalidFields.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2322
- className: "mdlx-error",
2323
- children: t("parametersInvalid")
2324
- }),
2325
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2326
- className: "mdlx-actions",
2327
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2328
- type: "button",
2329
- variant: snapshot.credentialReady && snapshot.proposal === null ? "primary" : "outline",
2330
- disabled: !canGenerate,
2331
- "aria-busy": submitting,
2332
- onClick: () => {
2333
- controller.submit(parameters);
2334
- },
2335
- children: submitting ? t("generating") : t("generate")
2301
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2302
+ className: "mdlx-parameter-list",
2303
+ children: [requiredFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2304
+ field,
2305
+ value: parameters[field.path],
2306
+ invalid: invalidFields.has(field.path),
2307
+ disabled: interactionBusy,
2308
+ onChange: (value) => updateParameter(field.path, value),
2309
+ onValidityChange: (valid) => setFieldValidity(field.path, valid),
2310
+ t
2311
+ }, field.path)), optionalFields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2312
+ className: "mdlx-advanced",
2313
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advancedParameters", { count: optionalFields.length }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2314
+ className: "mdlx-parameter-list",
2315
+ children: optionalFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2316
+ field,
2317
+ value: parameters[field.path],
2318
+ invalid: invalidFields.has(field.path),
2319
+ disabled: interactionBusy,
2320
+ onChange: (value) => updateParameter(field.path, value),
2321
+ onValidityChange: (valid) => setFieldValidity(field.path, valid),
2322
+ t
2323
+ }, field.path))
2324
+ })]
2325
+ })]
2326
+ })]
2327
+ }),
2328
+ snapshot.proposal !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProposalCard, {
2329
+ snapshot,
2330
+ parameters,
2331
+ controller,
2332
+ t
2333
+ }),
2334
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2335
+ className: "mdlx-generate-block",
2336
+ children: [
2337
+ missingRequired && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2338
+ className: "mdlx-error",
2339
+ children: t("requiredMissing")
2340
+ }),
2341
+ invalidFields.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2342
+ className: "mdlx-error",
2343
+ children: t("parametersInvalid")
2344
+ }),
2345
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2346
+ className: "mdlx-actions",
2347
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2348
+ type: "button",
2349
+ variant: snapshot.credentialReady && snapshot.proposal === null ? "primary" : "outline",
2350
+ disabled: !canGenerate,
2351
+ "aria-busy": submitting,
2352
+ onClick: () => {
2353
+ controller.submit(parameters);
2354
+ },
2355
+ children: submitting ? t("generating") : t("generate")
2356
+ })
2336
2357
  })
2337
- })
2338
- ]
2339
- }),
2340
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
2341
- code: state.errorCode,
2342
- t
2343
- }),
2344
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
2345
- busy: interactionBusy,
2346
- text: operationText(state.pending, t)
2347
- }),
2348
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2349
- className: "mdlx-live",
2350
- role: "status",
2351
- "aria-live": "polite",
2352
- children: outcomeAnnouncement
2353
- })
2354
- ]
2358
+ ]
2359
+ }),
2360
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
2361
+ code: state.errorCode,
2362
+ t
2363
+ }),
2364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
2365
+ busy: interactionBusy,
2366
+ text: operationText(state.pending, t)
2367
+ }),
2368
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2369
+ className: "mdlx-live",
2370
+ role: "status",
2371
+ "aria-live": "polite",
2372
+ children: outcomeAnnouncement
2373
+ })
2374
+ ]
2375
+ })
2355
2376
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResults, {
2356
2377
  snapshot,
2357
2378
  dateLocale: t("dateLocale"),
2358
2379
  dialogCoordinator: credentialDialogs,
2359
2380
  dialog: credentialDialog,
2381
+ ...onAddUrl === void 0 ? {} : { onAddUrl },
2360
2382
  t
2361
2383
  })]
2362
2384
  }), settings !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
@@ -2599,125 +2621,167 @@ function ProposalCard({ snapshot, parameters, controller, t }) {
2599
2621
  ]
2600
2622
  });
2601
2623
  }
2602
- function DesignResults({ snapshot, dateLocale, dialogCoordinator, dialog, t }) {
2603
- const groups = (0, react.useMemo)(() => {
2604
- const running = [];
2605
- const succeeded = [];
2606
- const diagnostics = [];
2607
- for (const job of snapshot.jobs) if (job.status === "running") running.push(job);
2608
- else if (job.status === "succeeded") succeeded.push(job);
2609
- else diagnostics.push(job);
2610
- return {
2611
- running,
2612
- succeeded,
2613
- diagnostics
2614
- };
2615
- }, [snapshot.jobs]);
2624
+ function DesignResults({ snapshot, dateLocale, dialogCoordinator, dialog, onAddUrl, t }) {
2625
+ const [open, setOpen] = (0, react.useState)(true);
2626
+ const jobs = (0, react.useMemo)(() => [...snapshot.jobs].sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt)), [snapshot.jobs]);
2616
2627
  const empty = snapshot.jobs.length === 0;
2617
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2618
- className: "mdlx-design-pane",
2619
- "aria-labelledby": "mdlx-results-title",
2620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
2621
- className: "mdlx-heading",
2622
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2628
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2629
+ className: "mdlx-design-pane mdlx-results-disclosure",
2630
+ open,
2631
+ onToggle: (event) => setOpen(event.currentTarget.open),
2632
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
2633
+ className: "mdlx-results-summary",
2634
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2623
2635
  id: "mdlx-results-title",
2624
2636
  children: t("resultsTitle")
2625
- })
2626
- }), empty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2627
- className: "mdlx-empty",
2628
- children: t("noResults")
2629
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2630
- className: "mdlx-design-scroll",
2631
- children: [
2632
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2633
- title: t("runningTitle"),
2634
- jobs: groups.running,
2635
- dateLocale,
2636
- dialogCoordinator,
2637
- dialog,
2638
- t
2639
- }),
2640
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2641
- title: t("succeededTitle"),
2642
- jobs: groups.succeeded,
2643
- dateLocale,
2644
- dialogCoordinator,
2645
- dialog,
2646
- t
2647
- }),
2648
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2649
- title: t("diagnosticsTitle"),
2650
- jobs: groups.diagnostics,
2637
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2638
+ className: "mdlx-muted",
2639
+ children: t("resultsCount", { count: jobs.length })
2640
+ })]
2641
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2642
+ className: "mdlx-results-body",
2643
+ children: empty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2644
+ className: "mdlx-empty",
2645
+ children: t("noResults")
2646
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2647
+ className: "mdlx-result-list",
2648
+ children: jobs.map((job) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultCard, {
2649
+ job,
2651
2650
  dateLocale,
2652
2651
  dialogCoordinator,
2653
2652
  dialog,
2653
+ ...onAddUrl === void 0 ? {} : { onAddUrl },
2654
2654
  t
2655
- })
2656
- ]
2657
- })]
2658
- });
2659
- }
2660
- function ResultSection({ title, jobs, dateLocale, dialogCoordinator, dialog, t }) {
2661
- if (jobs.length === 0) return null;
2662
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2663
- className: "mdlx-result-section",
2664
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2665
- className: "mdlx-result-list",
2666
- children: jobs.map((job) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultCard, {
2667
- job,
2668
- dateLocale,
2669
- dialogCoordinator,
2670
- dialog,
2671
- t
2672
- }, job.jobId))
2655
+ }, job.jobId))
2656
+ })
2673
2657
  })]
2674
2658
  });
2675
2659
  }
2676
- function ResultCard({ job, dateLocale, dialogCoordinator, dialog, t }) {
2660
+ function ResultCard({ job, dateLocale, dialogCoordinator, dialog, onAddUrl, t }) {
2661
+ const [open, setOpen] = (0, react.useState)(true);
2662
+ const [tab, setTab] = (0, react.useState)("preview");
2663
+ const [activeIndex, setActiveIndex] = (0, react.useState)(0);
2664
+ const tabId = (0, react.useId)();
2677
2665
  const status = jobStatus(job.status, t);
2678
2666
  const created = formatTime(job.createdAt, dateLocale);
2679
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
2667
+ const dot = job.status === "succeeded" ? "done" : job.status === "running" ? "ongoing" : job.status === "failed" ? "error" : "warning";
2668
+ const resources = job.status === "expired" ? [] : job.resources;
2669
+ const succeeded = job.status === "succeeded";
2670
+ const resourceIndex = resources.length === 0 ? 0 : activeIndex % resources.length;
2671
+ const currentResource = resources[resourceIndex];
2672
+ const activeTabId = `${tabId}-${tab}-tab`;
2673
+ const activePanelId = `${tabId}-${tab}-panel`;
2674
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
2680
2675
  className: "mdlx-result-card",
2681
- children: [
2682
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2683
- className: "mdlx-result-head",
2676
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2677
+ className: "mdlx-result-disclosure",
2678
+ open,
2679
+ onToggle: (event) => setOpen(event.currentTarget.open),
2680
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
2681
+ className: "mdlx-result-card-summary",
2684
2682
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2685
- className: "mdlx-status-copy",
2686
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: job.status === "succeeded" ? "done" : job.status === "running" ? "ongoing" : job.status === "failed" ? "error" : "warning" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: status })]
2683
+ className: "mdlx-result-head",
2684
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2685
+ className: "mdlx-status-copy",
2686
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: dot }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: status })]
2687
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2688
+ className: "mdlx-muted",
2689
+ children: t("jobCreated", { time: created })
2690
+ })]
2687
2691
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2688
2692
  className: "mdlx-muted",
2689
- children: t("jobCreated", { time: created })
2693
+ children: t("jobModel", { model: job.modelId })
2690
2694
  })]
2691
- }),
2692
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2693
- className: "mdlx-muted",
2694
- children: t("jobModel", { model: job.modelId })
2695
- }),
2696
- job.resources.length > 0 && job.status !== "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2697
- className: "mdlx-resource-list",
2698
- children: job.resources.map((resource) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultResource, {
2699
- resource,
2700
- dateLocale,
2701
- dialogCoordinator,
2702
- dialog,
2703
- t
2704
- }, resource.id))
2705
- }),
2706
- job.diagnostic !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2707
- className: "mdlx-error",
2708
- children: [
2709
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2710
- className: "mdlx-code",
2711
- children: job.diagnostic.code
2712
- }),
2713
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(designDiagnosticMessageKey(job.diagnostic.code)) }),
2714
- job.diagnostic.retryable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticRetryable") })
2715
- ]
2716
- })
2717
- ]
2695
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2696
+ className: "mdlx-result-card-body",
2697
+ children: [succeeded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2698
+ className: "mdlx-result-tabs",
2699
+ role: "tablist",
2700
+ "aria-label": t("resultViewLabel"),
2701
+ onKeyDown: handleResultTabKeyDown,
2702
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2703
+ id: `${tabId}-preview-tab`,
2704
+ type: "button",
2705
+ role: "tab",
2706
+ "aria-controls": `${tabId}-preview-panel`,
2707
+ "aria-selected": tab === "preview",
2708
+ tabIndex: tab === "preview" ? 0 : -1,
2709
+ className: "mdlx-result-tab",
2710
+ onClick: () => setTab("preview"),
2711
+ children: t("previewTab")
2712
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2713
+ id: `${tabId}-json-tab`,
2714
+ type: "button",
2715
+ role: "tab",
2716
+ "aria-controls": `${tabId}-json-panel`,
2717
+ "aria-selected": tab === "json",
2718
+ tabIndex: tab === "json" ? 0 : -1,
2719
+ className: "mdlx-result-tab",
2720
+ onClick: () => setTab("json"),
2721
+ children: t("jsonTab")
2722
+ })]
2723
+ }), tab === "preview" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2724
+ id: activePanelId,
2725
+ role: "tabpanel",
2726
+ "aria-labelledby": activeTabId,
2727
+ className: "mdlx-result-panel",
2728
+ children: currentResource === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2729
+ className: "mdlx-empty mdlx-empty-compact",
2730
+ children: t("noPreview")
2731
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultResource, {
2732
+ resource: currentResource,
2733
+ dateLocale,
2734
+ dialogCoordinator,
2735
+ dialog,
2736
+ ...onAddUrl === void 0 ? {} : { onAddUrl },
2737
+ t
2738
+ }), resources.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2739
+ className: "mdlx-resource-nav",
2740
+ "aria-label": t("resourceNavigation"),
2741
+ children: [
2742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2743
+ type: "button",
2744
+ variant: "outline",
2745
+ onClick: () => setActiveIndex((resourceIndex - 1 + resources.length) % resources.length),
2746
+ children: t("previousResource")
2747
+ }),
2748
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2749
+ className: "mdlx-muted",
2750
+ children: t("resourcePosition", {
2751
+ current: resourceIndex + 1,
2752
+ total: resources.length
2753
+ })
2754
+ }),
2755
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2756
+ type: "button",
2757
+ variant: "outline",
2758
+ onClick: () => setActiveIndex((resourceIndex + 1) % resources.length),
2759
+ children: t("nextResource")
2760
+ })
2761
+ ]
2762
+ })] })
2763
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2764
+ id: activePanelId,
2765
+ role: "tabpanel",
2766
+ "aria-labelledby": activeTabId,
2767
+ className: "mdlx-result-json",
2768
+ children: JSON.stringify(job, null, 2)
2769
+ })] }), job.diagnostic !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2770
+ className: "mdlx-error",
2771
+ children: [
2772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2773
+ className: "mdlx-code",
2774
+ children: job.diagnostic.code
2775
+ }),
2776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(designDiagnosticMessageKey(job.diagnostic.code)) }),
2777
+ job.diagnostic.retryable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticRetryable") })
2778
+ ]
2779
+ })]
2780
+ })]
2781
+ })
2718
2782
  });
2719
2783
  }
2720
- function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t }) {
2784
+ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, onAddUrl, t }) {
2721
2785
  const imageDialogOwner = `design-image:${(0, react.useId)()}`;
2722
2786
  const imageSurfaceOpen = useExternalDialogGate(dialog.activeOwner === imageDialogOwner);
2723
2787
  const imageDialogRef = (0, react.useRef)(null);
@@ -2733,29 +2797,34 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2733
2797
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
2734
2798
  className: "mdlx-resource",
2735
2799
  children: [
2736
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
2800
+ resource.kind === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2801
+ type: "button",
2802
+ className: "mdlx-image-preview-button",
2803
+ "aria-label": t("openImage"),
2804
+ onClick: () => dialogCoordinator.open(imageDialogOwner),
2805
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
2806
+ resource,
2807
+ t
2808
+ })
2809
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
2737
2810
  resource,
2738
2811
  t
2739
2812
  }),
2740
2813
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2741
2814
  className: "mdlx-actions mdlx-actions-start",
2742
- children: [resource.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2815
+ children: [onAddUrl !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2743
2816
  type: "button",
2744
- className: "mdlx-safe-link mdlx-link-button",
2745
- onClick: () => dialogCoordinator.open(imageDialogOwner),
2746
- children: t("openImage")
2747
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2817
+ variant: "outline",
2818
+ onClick: () => onAddUrl(resource.url),
2819
+ children: t("addUrlToChat")
2820
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2748
2821
  className: "mdlx-safe-link",
2749
2822
  href: resource.downloadUrl,
2750
2823
  target: "_blank",
2751
2824
  rel: "noopener noreferrer",
2752
2825
  referrerPolicy: "no-referrer",
2753
2826
  download: true,
2754
- children: [
2755
- t("download"),
2756
- " ",
2757
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2758
- ]
2827
+ children: t("download")
2759
2828
  })]
2760
2829
  }),
2761
2830
  resource.expiresAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -2797,18 +2866,14 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2797
2866
  "data-mdlx-initial-focus": "",
2798
2867
  onClick: closeImage,
2799
2868
  children: t("close")
2800
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2869
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2801
2870
  className: "mdlx-safe-link",
2802
2871
  href: resource.downloadUrl,
2803
2872
  target: "_blank",
2804
2873
  rel: "noopener noreferrer",
2805
2874
  referrerPolicy: "no-referrer",
2806
2875
  download: true,
2807
- children: [
2808
- t("download"),
2809
- " ",
2810
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2811
- ]
2876
+ children: t("download")
2812
2877
  })]
2813
2878
  })
2814
2879
  ]
@@ -2849,12 +2914,727 @@ function formatTime(value, locale) {
2849
2914
  }).format(new Date(value));
2850
2915
  }
2851
2916
  //#endregion
2917
+ //#region src/client/DesignDrawer.tsx
2918
+ const MODELLIX_DESIGN_SPLIT_MEDIA_QUERY = "(min-width: 1280px)";
2919
+ const MODELLIX_DRAWER_EXIT_MS = 180;
2920
+ const MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN = true;
2921
+ function isSplitViewport() {
2922
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(min-width: 1280px)").matches;
2923
+ }
2924
+ var DesignDrawerController = class {
2925
+ #rpc;
2926
+ #layout;
2927
+ #isSplitViewport;
2928
+ #listeners = /* @__PURE__ */ new Set();
2929
+ #controllers = /* @__PURE__ */ new Map();
2930
+ #watchedJobs = /* @__PURE__ */ new Map();
2931
+ #mediaCardClaims = /* @__PURE__ */ new Map();
2932
+ #mediaCardListeners = /* @__PURE__ */ new Set();
2933
+ #sessionReferences = /* @__PURE__ */ new Map();
2934
+ #sessionReleaseTimers = /* @__PURE__ */ new Map();
2935
+ #mediaCardVersion = 0;
2936
+ #mediaCardOrder = 0;
2937
+ #snapshot = {
2938
+ open: false,
2939
+ sessionId: null,
2940
+ controller: null,
2941
+ draft: "",
2942
+ inputActions: null
2943
+ };
2944
+ #trigger = null;
2945
+ #splitActive = false;
2946
+ constructor(rpc, layout, splitViewport = isSplitViewport) {
2947
+ this.#rpc = rpc;
2948
+ this.#layout = layout;
2949
+ this.#isSplitViewport = splitViewport;
2950
+ }
2951
+ subscribe = (listener) => {
2952
+ this.#listeners.add(listener);
2953
+ return () => this.#listeners.delete(listener);
2954
+ };
2955
+ getSnapshot = () => this.#snapshot;
2956
+ subscribeMediaCards = (listener) => {
2957
+ this.#mediaCardListeners.add(listener);
2958
+ return () => this.#mediaCardListeners.delete(listener);
2959
+ };
2960
+ getMediaCardVersion = () => this.#mediaCardVersion;
2961
+ claimMediaCard(sessionId, jobId, callId, operation) {
2962
+ const key = `${sessionId}\n${jobId}`;
2963
+ const claims = this.#mediaCardClaims.get(key) ?? /* @__PURE__ */ new Map();
2964
+ if (claims.has(callId)) return () => void 0;
2965
+ claims.set(callId, {
2966
+ operation,
2967
+ order: this.#mediaCardOrder++
2968
+ });
2969
+ this.#mediaCardClaims.set(key, claims);
2970
+ this.#publishMediaCards();
2971
+ return () => {
2972
+ const current = this.#mediaCardClaims.get(key);
2973
+ if (current?.delete(callId) !== true) return;
2974
+ if (current.size === 0) this.#mediaCardClaims.delete(key);
2975
+ this.#publishMediaCards();
2976
+ };
2977
+ }
2978
+ isMediaCardPrimary(sessionId, jobId, callId, operation) {
2979
+ const claims = this.#mediaCardClaims.get(`${sessionId}\n${jobId}`);
2980
+ if (claims === void 0 || claims.size === 0) return true;
2981
+ let primaryCallId = callId;
2982
+ let primary = {
2983
+ operation,
2984
+ order: Number.MAX_SAFE_INTEGER
2985
+ };
2986
+ for (const [candidateCallId, candidate] of claims) if (compareMediaCardClaims(candidate, primary) < 0) {
2987
+ primaryCallId = candidateCallId;
2988
+ primary = candidate;
2989
+ }
2990
+ return primaryCallId === callId;
2991
+ }
2992
+ controllerForSession(sessionId) {
2993
+ const existing = this.#controllers.get(sessionId);
2994
+ if (existing !== void 0) return existing;
2995
+ const controller = new DesignController(this.#rpc, sessionId);
2996
+ this.#controllers.set(sessionId, controller);
2997
+ return controller;
2998
+ }
2999
+ retainSession(sessionId) {
3000
+ const pendingRelease = this.#sessionReleaseTimers.get(sessionId);
3001
+ if (pendingRelease !== void 0) {
3002
+ window.clearTimeout(pendingRelease);
3003
+ this.#sessionReleaseTimers.delete(sessionId);
3004
+ }
3005
+ this.#sessionReferences.set(sessionId, (this.#sessionReferences.get(sessionId) ?? 0) + 1);
3006
+ let retained = true;
3007
+ return () => {
3008
+ if (!retained) return;
3009
+ retained = false;
3010
+ const remaining = Math.max(0, (this.#sessionReferences.get(sessionId) ?? 1) - 1);
3011
+ if (remaining > 0) {
3012
+ this.#sessionReferences.set(sessionId, remaining);
3013
+ return;
3014
+ }
3015
+ this.#sessionReferences.delete(sessionId);
3016
+ const timer = window.setTimeout(() => {
3017
+ this.#sessionReleaseTimers.delete(sessionId);
3018
+ if ((this.#sessionReferences.get(sessionId) ?? 0) === 0) this.releaseSession(sessionId);
3019
+ }, 0);
3020
+ this.#sessionReleaseTimers.set(sessionId, timer);
3021
+ };
3022
+ }
3023
+ watchJob(sessionId, jobId, initialStatus) {
3024
+ if (initialStatus !== "running") return () => void 0;
3025
+ const key = `${sessionId}\n${jobId}`;
3026
+ const existing = this.#watchedJobs.get(key);
3027
+ if (existing !== void 0) {
3028
+ existing.references += 1;
3029
+ return () => this.#releaseJobWatch(key, existing);
3030
+ }
3031
+ const controller = this.controllerForSession(sessionId);
3032
+ const watched = {
3033
+ references: 1,
3034
+ timer: null,
3035
+ stopped: false,
3036
+ unsubscribeStore: () => void 0
3037
+ };
3038
+ const liveStatus = () => controller.store.getSnapshot().data?.jobs.find((job) => job.jobId === jobId)?.status;
3039
+ const stopWhenTerminal = () => {
3040
+ const status = liveStatus();
3041
+ if (status === void 0 || status === "running") return;
3042
+ watched.stopped = true;
3043
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3044
+ watched.timer = null;
3045
+ };
3046
+ const poll = async () => {
3047
+ watched.timer = null;
3048
+ if (watched.stopped) return;
3049
+ await controller.load();
3050
+ stopWhenTerminal();
3051
+ if (!watched.stopped && watched.references > 0) watched.timer = window.setTimeout(() => {
3052
+ poll();
3053
+ }, 5e3);
3054
+ };
3055
+ watched.unsubscribeStore = controller.store.subscribe(stopWhenTerminal);
3056
+ this.#watchedJobs.set(key, watched);
3057
+ watched.timer = window.setTimeout(() => {
3058
+ poll();
3059
+ }, 0);
3060
+ return () => this.#releaseJobWatch(key, watched);
3061
+ }
3062
+ open(sessionId, inputActions, draft, trigger) {
3063
+ const controller = this.controllerForSession(sessionId);
3064
+ this.#trigger = trigger;
3065
+ this.#syncSplit(this.#isSplitViewport());
3066
+ this.#publish({
3067
+ open: true,
3068
+ sessionId,
3069
+ controller,
3070
+ draft,
3071
+ inputActions
3072
+ });
3073
+ }
3074
+ close(restoreFocus = true) {
3075
+ if (!this.#snapshot.open) return;
3076
+ this.#syncSplit(false);
3077
+ this.#publish({
3078
+ ...this.#snapshot,
3079
+ open: false
3080
+ });
3081
+ if (restoreFocus) queueMicrotask(() => this.#trigger?.focus());
3082
+ }
3083
+ setSplitMode(enabled) {
3084
+ if (!this.#snapshot.open) return;
3085
+ this.#syncSplit(enabled);
3086
+ }
3087
+ updateSession(sessionId, inputActions, draft) {
3088
+ if (this.#snapshot.sessionId !== sessionId) return;
3089
+ this.#publish({
3090
+ ...this.#snapshot,
3091
+ inputActions,
3092
+ draft
3093
+ });
3094
+ }
3095
+ releaseSession(sessionId) {
3096
+ if (this.#snapshot.sessionId !== sessionId) return;
3097
+ this.#syncSplit(false);
3098
+ this.#trigger = null;
3099
+ this.#publish({
3100
+ open: false,
3101
+ sessionId: null,
3102
+ controller: null,
3103
+ draft: "",
3104
+ inputActions: null
3105
+ });
3106
+ }
3107
+ #releaseJobWatch(key, watched) {
3108
+ if (this.#watchedJobs.get(key) !== watched) return;
3109
+ watched.references -= 1;
3110
+ if (watched.references > 0) return;
3111
+ watched.stopped = true;
3112
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3113
+ watched.unsubscribeStore();
3114
+ this.#watchedJobs.delete(key);
3115
+ }
3116
+ appendUrl(url) {
3117
+ const actions = this.#snapshot.inputActions;
3118
+ if (actions === null) return;
3119
+ const current = this.#snapshot.draft;
3120
+ const next = `${current}${current === "" || /\s$/u.test(current) ? "" : "\n"}${url}`;
3121
+ actions.setDraft(next);
3122
+ this.#publish({
3123
+ ...this.#snapshot,
3124
+ draft: next
3125
+ });
3126
+ }
3127
+ #publish(snapshot) {
3128
+ this.#snapshot = snapshot;
3129
+ for (const listener of this.#listeners) listener();
3130
+ }
3131
+ #publishMediaCards() {
3132
+ this.#mediaCardVersion += 1;
3133
+ for (const listener of this.#mediaCardListeners) listener();
3134
+ }
3135
+ #syncSplit(enabled) {
3136
+ if (enabled === this.#splitActive) return;
3137
+ if (enabled) {
3138
+ this.#layout.closeDetails();
3139
+ this.#layout.openDetails();
3140
+ } else this.#layout.closeDetails();
3141
+ this.#splitActive = enabled;
3142
+ }
3143
+ };
3144
+ function compareMediaCardClaims(left, right) {
3145
+ return (left.operation === "generate" ? 0 : 1) - (right.operation === "generate" ? 0 : 1) || left.order - right.order;
3146
+ }
3147
+ function ModellixDesignLauncher({ drawer, inputActions, sessionId, t, useInput }) {
3148
+ const draft = useInput((state) => state.draft);
3149
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3150
+ const active = snapshot.open && snapshot.sessionId === String(sessionId);
3151
+ (0, react.useEffect)(() => {
3152
+ drawer.updateSession(String(sessionId), inputActions, draft);
3153
+ }, [
3154
+ draft,
3155
+ drawer,
3156
+ inputActions,
3157
+ sessionId
3158
+ ]);
3159
+ (0, react.useEffect)(() => drawer.retainSession(String(sessionId)), [drawer, sessionId]);
3160
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3161
+ type: "button",
3162
+ variant: "outline",
3163
+ className: "mdlx-design-launcher",
3164
+ "aria-label": t("designTitle"),
3165
+ "aria-expanded": active,
3166
+ "aria-controls": "mdlx-design-drawer",
3167
+ onClick: (event) => {
3168
+ if (active) drawer.close();
3169
+ else drawer.open(String(sessionId), inputActions, draft, event.currentTarget);
3170
+ },
3171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3172
+ className: "mdlx-launcher-label",
3173
+ children: t("designTitle")
3174
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3175
+ className: "mdlx-launcher-compact",
3176
+ "aria-hidden": "true",
3177
+ children: "M"
3178
+ })]
3179
+ });
3180
+ }
3181
+ function ModellixDesignDrawer({ drawer, settingsController, t }) {
3182
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3183
+ const [retainedSnapshot, setRetainedSnapshot] = (0, react.useState)(snapshot.open && snapshot.controller !== null ? snapshot : null);
3184
+ const [advancedEditorOpen, setAdvancedEditorOpen] = (0, react.useState)(false);
3185
+ (0, react.useEffect)(() => {
3186
+ if (snapshot.open && snapshot.controller !== null) setRetainedSnapshot(snapshot);
3187
+ }, [
3188
+ snapshot.controller,
3189
+ snapshot.open,
3190
+ snapshot.sessionId
3191
+ ]);
3192
+ (0, react.useEffect)(() => {
3193
+ if (snapshot.open || retainedSnapshot === null) return;
3194
+ const timer = window.setTimeout(() => setRetainedSnapshot(null), MODELLIX_DRAWER_EXIT_MS);
3195
+ return () => window.clearTimeout(timer);
3196
+ }, [retainedSnapshot, snapshot.open]);
3197
+ (0, react.useEffect)(() => {
3198
+ setAdvancedEditorOpen(false);
3199
+ }, [snapshot.sessionId]);
3200
+ (0, react.useEffect)(() => {
3201
+ if (!snapshot.open || typeof window.matchMedia !== "function") return;
3202
+ const media = window.matchMedia(MODELLIX_DESIGN_SPLIT_MEDIA_QUERY);
3203
+ const sync = () => drawer.setSplitMode(media.matches);
3204
+ sync();
3205
+ media.addEventListener("change", sync);
3206
+ return () => media.removeEventListener("change", sync);
3207
+ }, [drawer, snapshot.open]);
3208
+ const presentedSnapshot = snapshot.open && snapshot.controller !== null ? snapshot : retainedSnapshot;
3209
+ if (presentedSnapshot === null || presentedSnapshot.controller === null) return null;
3210
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3211
+ className: "mdlx-design-drawer-viewport",
3212
+ "data-closing": !snapshot.open || void 0,
3213
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3214
+ id: "mdlx-design-drawer",
3215
+ className: "mdlx-design-drawer",
3216
+ "aria-labelledby": "mdlx-design-drawer-title",
3217
+ onKeyDown: (event) => {
3218
+ if (event.key === "Escape") {
3219
+ event.preventDefault();
3220
+ drawer.close();
3221
+ }
3222
+ },
3223
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3224
+ className: "mdlx-drawer-header",
3225
+ "data-editor-open": advancedEditorOpen || void 0,
3226
+ children: [
3227
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3228
+ type: "button",
3229
+ variant: "ghost",
3230
+ size: "sm",
3231
+ className: "mdlx-drawer-close",
3232
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 }),
3233
+ "aria-label": `${t("close")} ${t("designTitle")}`,
3234
+ onClick: () => drawer.close()
3235
+ }),
3236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3237
+ className: "mdlx-heading",
3238
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3239
+ id: "mdlx-design-drawer-title",
3240
+ children: t("designTitle")
3241
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3242
+ className: "mdlx-muted",
3243
+ children: t("designDescription")
3244
+ })]
3245
+ }),
3246
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3247
+ className: "mdlx-drawer-header-reveal",
3248
+ hidden: MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN,
3249
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3250
+ type: "button",
3251
+ variant: "outline",
3252
+ className: "mdlx-drawer-editor-toggle",
3253
+ "aria-controls": "mdlx-design-editor",
3254
+ "aria-expanded": advancedEditorOpen,
3255
+ onClick: (event) => {
3256
+ const next = !advancedEditorOpen;
3257
+ setAdvancedEditorOpen(next);
3258
+ if (!next && event.detail > 0) event.currentTarget.blur();
3259
+ },
3260
+ children: t("advancedEditor")
3261
+ }) })
3262
+ })
3263
+ ]
3264
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3265
+ className: "mdlx-drawer-body",
3266
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModellixDesignView, {
3267
+ advancedEditorOpen,
3268
+ controller: presentedSnapshot.controller,
3269
+ settingsController,
3270
+ onAddUrl: (url) => drawer.appendUrl(url),
3271
+ t
3272
+ })
3273
+ })]
3274
+ })
3275
+ });
3276
+ }
3277
+ //#endregion
3278
+ //#region src/client/MediaToolView.tsx
3279
+ function ModellixMediaToolView({ block, drawer, inputActions, sessionId, t, useInput }) {
3280
+ const draft = useInput((state) => state.draft);
3281
+ const [tab, setTab] = (0, react.useState)("preview");
3282
+ const [activeIndex, setActiveIndex] = (0, react.useState)(0);
3283
+ const [imageOpen, setImageOpen] = (0, react.useState)(false);
3284
+ const tabId = (0, react.useId)();
3285
+ const imageDialogRef = (0, react.useRef)(null);
3286
+ const isResultBlock = "kind" in block && block.kind === "tool-result";
3287
+ const parsedResult = isResultBlock ? parseMediaToolResult(block.meta) : null;
3288
+ const liveState = useResourceState(drawer.controllerForSession(String(sessionId)).store);
3289
+ const trackedJobId = mediaToolJobId(parsedResult);
3290
+ const operation = parsedResult?.operation;
3291
+ const callId = "callId" in block && typeof block.callId === "string" ? block.callId : `${String(sessionId)}:${trackedJobId ?? "pending"}`;
3292
+ (0, react.useSyncExternalStore)(drawer.subscribeMediaCards, drawer.getMediaCardVersion, drawer.getMediaCardVersion);
3293
+ const parsedStatus = mediaToolStatus(parsedResult);
3294
+ const result = resolveLiveMediaToolResult(parsedResult, trackedJobId === void 0 ? void 0 : liveState.data?.jobs.find((job) => job.jobId === trackedJobId), t);
3295
+ (0, react.useEffect)(() => trackedJobId === void 0 ? void 0 : drawer.watchJob(String(sessionId), trackedJobId, parsedStatus), [
3296
+ drawer,
3297
+ parsedStatus,
3298
+ sessionId,
3299
+ trackedJobId
3300
+ ]);
3301
+ (0, react.useEffect)(() => trackedJobId === void 0 || operation === void 0 ? void 0 : drawer.claimMediaCard(String(sessionId), trackedJobId, callId, operation), [
3302
+ callId,
3303
+ drawer,
3304
+ operation,
3305
+ sessionId,
3306
+ trackedJobId
3307
+ ]);
3308
+ const resources = result?.operation === "get_result" ? result.job?.resources ?? [] : result?.resources ?? [];
3309
+ const resourceIndex = resources.length === 0 ? 0 : activeIndex % resources.length;
3310
+ const current = resources[resourceIndex];
3311
+ const imageSurfaceOpen = useExternalDialogGate(imageOpen && current?.kind === "image");
3312
+ const closeImage = (0, react.useCallback)(() => setImageOpen(false), []);
3313
+ useDialogA11y({
3314
+ open: imageSurfaceOpen,
3315
+ container: imageDialogRef,
3316
+ initialFocusSelector: "[data-mdlx-initial-focus]",
3317
+ mandatory: false,
3318
+ onEscape: closeImage
3319
+ });
3320
+ (0, react.useEffect)(() => setImageOpen(false), [current?.url]);
3321
+ if (trackedJobId !== void 0 && operation !== void 0 && !drawer.isMediaCardPrimary(String(sessionId), trackedJobId, callId, operation)) return null;
3322
+ if (!isResultBlock) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3323
+ className: "mdlx-tool-result mdlx-muted",
3324
+ role: "status",
3325
+ children: t("generating")
3326
+ });
3327
+ if (result === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3328
+ className: "mdlx-tool-result mdlx-muted",
3329
+ role: "status",
3330
+ children: t("noPreview")
3331
+ });
3332
+ const status = result.operation === "get_result" ? result.job?.status : result.status;
3333
+ const modelId = result.operation === "get_result" ? result.job?.modelId : result.modelId;
3334
+ const diagnostic = result.operation === "get_result" ? result.job?.diagnostic : result.diagnostic;
3335
+ const succeeded = status === "succeeded";
3336
+ const activeTabId = `${tabId}-${tab}-tab`;
3337
+ const activePanelId = `${tabId}-${tab}-panel`;
3338
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
3339
+ className: "mdlx-tool-result",
3340
+ children: [
3341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3342
+ className: "mdlx-result-head",
3343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: result.found === false ? t("resultNotFound") : mediaStatusLabel(status, t) }), modelId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3344
+ className: "mdlx-muted",
3345
+ children: modelId
3346
+ })]
3347
+ }),
3348
+ succeeded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3349
+ className: "mdlx-result-tabs",
3350
+ role: "tablist",
3351
+ "aria-label": t("resultViewLabel"),
3352
+ onKeyDown: handleResultTabKeyDown,
3353
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3354
+ id: `${tabId}-preview-tab`,
3355
+ type: "button",
3356
+ role: "tab",
3357
+ "aria-controls": `${tabId}-preview-panel`,
3358
+ "aria-selected": tab === "preview",
3359
+ tabIndex: tab === "preview" ? 0 : -1,
3360
+ className: "mdlx-result-tab",
3361
+ onClick: () => setTab("preview"),
3362
+ children: t("previewTab")
3363
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3364
+ id: `${tabId}-json-tab`,
3365
+ type: "button",
3366
+ role: "tab",
3367
+ "aria-controls": `${tabId}-json-panel`,
3368
+ "aria-selected": tab === "json",
3369
+ tabIndex: tab === "json" ? 0 : -1,
3370
+ className: "mdlx-result-tab",
3371
+ onClick: () => setTab("json"),
3372
+ children: t("jsonTab")
3373
+ })]
3374
+ }), tab === "json" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3375
+ id: activePanelId,
3376
+ role: "tabpanel",
3377
+ "aria-labelledby": activeTabId,
3378
+ className: "mdlx-result-json",
3379
+ children: JSON.stringify(result, null, 2)
3380
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3381
+ id: activePanelId,
3382
+ role: "tabpanel",
3383
+ "aria-labelledby": activeTabId,
3384
+ className: "mdlx-result-panel",
3385
+ children: current === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3386
+ className: "mdlx-empty mdlx-empty-compact",
3387
+ children: t("noPreview")
3388
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3389
+ current.kind === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3390
+ type: "button",
3391
+ className: "mdlx-image-preview-button",
3392
+ "aria-label": t("openImage"),
3393
+ onClick: () => setImageOpen(true),
3394
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3395
+ resource: toWireResource(current, resourceIndex),
3396
+ t
3397
+ })
3398
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3399
+ resource: toWireResource(current, resourceIndex),
3400
+ t
3401
+ }),
3402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3403
+ className: "mdlx-actions mdlx-actions-start",
3404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3405
+ type: "button",
3406
+ variant: "outline",
3407
+ onClick: () => {
3408
+ const separator = draft === "" || /\s$/u.test(draft) ? "" : "\n";
3409
+ inputActions.setDraft(`${draft}${separator}${current.url}`);
3410
+ },
3411
+ children: t("addUrlToChat")
3412
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3413
+ className: "mdlx-safe-link",
3414
+ href: current.url,
3415
+ target: "_blank",
3416
+ rel: "noopener noreferrer",
3417
+ referrerPolicy: "no-referrer",
3418
+ download: true,
3419
+ children: t("download")
3420
+ })]
3421
+ }),
3422
+ resources.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3423
+ className: "mdlx-resource-nav",
3424
+ "aria-label": t("resourceNavigation"),
3425
+ children: [
3426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3427
+ type: "button",
3428
+ variant: "outline",
3429
+ onClick: () => setActiveIndex((resourceIndex - 1 + resources.length) % resources.length),
3430
+ children: t("previousResource")
3431
+ }),
3432
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3433
+ className: "mdlx-muted",
3434
+ children: t("resourcePosition", {
3435
+ current: resourceIndex + 1,
3436
+ total: resources.length
3437
+ })
3438
+ }),
3439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3440
+ type: "button",
3441
+ variant: "outline",
3442
+ onClick: () => setActiveIndex((resourceIndex + 1) % resources.length),
3443
+ children: t("nextResource")
3444
+ })
3445
+ ]
3446
+ })
3447
+ ] })
3448
+ })] }),
3449
+ diagnostic !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3450
+ className: "mdlx-error",
3451
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
3452
+ className: "mdlx-code",
3453
+ children: diagnostic.code
3454
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: diagnostic.message })]
3455
+ })
3456
+ ]
3457
+ }), succeeded && current?.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
3458
+ open: imageSurfaceOpen,
3459
+ title: t("imageViewerTitle"),
3460
+ closeLabel: t("close"),
3461
+ onClose: closeImage,
3462
+ headless: true,
3463
+ className: "mdlx-modal mdlx-image-modal",
3464
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3465
+ ref: imageDialogRef,
3466
+ className: "mdlx-modal-content",
3467
+ "data-mdlx-dialog-surface": "",
3468
+ tabIndex: -1,
3469
+ children: [
3470
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3471
+ className: "mdlx-heading",
3472
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3473
+ className: "mdlx-modal-title",
3474
+ children: t("imageViewerTitle")
3475
+ })
3476
+ }),
3477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3478
+ className: "mdlx-image-full",
3479
+ src: current.url,
3480
+ alt: t("generatedPreview"),
3481
+ referrerPolicy: "no-referrer"
3482
+ }),
3483
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3484
+ className: "mdlx-actions",
3485
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3486
+ type: "button",
3487
+ variant: "outline",
3488
+ "data-mdlx-initial-focus": "",
3489
+ onClick: closeImage,
3490
+ children: t("close")
3491
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3492
+ className: "mdlx-safe-link",
3493
+ href: current.url,
3494
+ target: "_blank",
3495
+ rel: "noopener noreferrer",
3496
+ referrerPolicy: "no-referrer",
3497
+ download: true,
3498
+ children: t("download")
3499
+ })]
3500
+ })
3501
+ ]
3502
+ })
3503
+ })] });
3504
+ }
3505
+ function mediaToolJobId(result) {
3506
+ return result?.operation === "get_result" ? result.job?.jobId : result?.jobId;
3507
+ }
3508
+ function mediaToolStatus(result) {
3509
+ return result?.operation === "get_result" ? result.job?.status : result?.status;
3510
+ }
3511
+ function resolveLiveMediaToolResult(result, liveJob, t) {
3512
+ if (result === null || liveJob === void 0) return result;
3513
+ const resources = liveJob.resources.map((resource) => ({
3514
+ kind: resource.kind,
3515
+ url: resource.url,
3516
+ ...resource.expiresAt === null ? {} : { expiresAt: resource.expiresAt }
3517
+ }));
3518
+ const diagnostic = liveJob.diagnostic === null ? void 0 : {
3519
+ code: liveJob.diagnostic.code,
3520
+ message: t(designDiagnosticMessageKey(liveJob.diagnostic.code))
3521
+ };
3522
+ if (result.operation === "generate") {
3523
+ const { diagnostic: _staleDiagnostic, ...withoutDiagnostic } = result;
3524
+ return {
3525
+ ...withoutDiagnostic,
3526
+ status: liveJob.status,
3527
+ modelId: liveJob.modelId,
3528
+ resources,
3529
+ ...diagnostic === void 0 ? {} : { diagnostic }
3530
+ };
3531
+ }
3532
+ return {
3533
+ ...result,
3534
+ found: true,
3535
+ job: {
3536
+ jobId: liveJob.jobId,
3537
+ modelId: liveJob.modelId,
3538
+ status: liveJob.status,
3539
+ resources,
3540
+ ...diagnostic === void 0 ? {} : { diagnostic }
3541
+ }
3542
+ };
3543
+ }
3544
+ function parseMediaToolResult(value) {
3545
+ const root = record(value);
3546
+ if (root === null || root.service !== "media" || root.operation !== "generate" && root.operation !== "get_result") return null;
3547
+ if (root.operation === "generate") {
3548
+ const status = safeText(root.status, 64);
3549
+ const modelId = safeText(root.modelId, 256);
3550
+ const jobId = safeText(root.jobId, 256);
3551
+ const diagnostic = parseDiagnostic(root.diagnostic);
3552
+ return {
3553
+ operation: "generate",
3554
+ ...status === void 0 ? {} : { status },
3555
+ ...modelId === void 0 ? {} : { modelId },
3556
+ ...jobId === void 0 ? {} : { jobId },
3557
+ resources: parseResources(root.resources),
3558
+ ...diagnostic === void 0 ? {} : { diagnostic }
3559
+ };
3560
+ }
3561
+ const job = record(root.job);
3562
+ const diagnostic = job === null ? void 0 : parseDiagnostic(job.diagnostic);
3563
+ return {
3564
+ operation: "get_result",
3565
+ found: root.found === true,
3566
+ ...job === null ? {} : { job: {
3567
+ jobId: safeText(job.jobId, 256) ?? "",
3568
+ modelId: safeText(job.modelId, 256) ?? "",
3569
+ status: safeText(job.status, 64) ?? "",
3570
+ resources: parseResources(job.resources),
3571
+ ...diagnostic === void 0 ? {} : { diagnostic }
3572
+ } }
3573
+ };
3574
+ }
3575
+ function parseResources(value) {
3576
+ if (!Array.isArray(value)) return [];
3577
+ const resources = [];
3578
+ for (const candidate of value.slice(0, 32)) {
3579
+ const resource = record(candidate);
3580
+ if (resource === null || resource.kind !== "image" && resource.kind !== "video" && resource.kind !== "audio" || typeof resource.url !== "string") continue;
3581
+ let url;
3582
+ try {
3583
+ url = safeResourceHref(resource.url);
3584
+ } catch {
3585
+ continue;
3586
+ }
3587
+ const expiresAt = safeText(resource.expiresAt, 128);
3588
+ resources.push({
3589
+ kind: resource.kind,
3590
+ url,
3591
+ ...expiresAt === void 0 ? {} : { expiresAt }
3592
+ });
3593
+ }
3594
+ return resources;
3595
+ }
3596
+ function parseDiagnostic(value) {
3597
+ const diagnostic = record(value);
3598
+ if (diagnostic === null) return void 0;
3599
+ const code = safeText(diagnostic.code, 128);
3600
+ const message = safeText(diagnostic.message, 1024);
3601
+ return code === void 0 || message === void 0 ? void 0 : {
3602
+ code,
3603
+ message
3604
+ };
3605
+ }
3606
+ function safeText(value, maximum) {
3607
+ return typeof value === "string" && value.length > 0 && value.length <= maximum ? value : void 0;
3608
+ }
3609
+ function record(value) {
3610
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
3611
+ }
3612
+ function mediaStatusLabel(status, t) {
3613
+ switch (status) {
3614
+ case "running": return t("running");
3615
+ case "succeeded": return t("succeeded");
3616
+ case "failed": return t("failed");
3617
+ case "canceled": return t("canceled");
3618
+ case "expired": return t("expired");
3619
+ case "submit-unknown": return t("unknown");
3620
+ default: return t("unknown");
3621
+ }
3622
+ }
3623
+ function toWireResource(resource, index) {
3624
+ return {
3625
+ id: `tool-resource-${String(index)}`,
3626
+ kind: resource.kind,
3627
+ url: resource.url,
3628
+ downloadUrl: resource.url,
3629
+ expiresAt: resource.expiresAt ?? null
3630
+ };
3631
+ }
3632
+ //#endregion
2852
3633
  //#region src/client/locales.ts
2853
3634
  const MODELLIX_LOCALE_NAMESPACE = "modellix";
2854
3635
  const zh = {
2855
3636
  dateLocale: "zh-CN",
2856
3637
  nav: "Modellix",
2857
- designTab: "Design",
2858
3638
  onboardingTitle: "连接 Modellix",
2859
3639
  onboardingDescription: "输入一个 Modellix API Key,即可启用 Design、LLM 模型和 Web Tools。",
2860
3640
  keyLabel: "Modellix API Key",
@@ -2871,7 +3651,7 @@ const zh = {
2871
3651
  serviceLlm: "LLM 模型",
2872
3652
  serviceLlmDescription: "在 Harness 中快速切换 Modellix 支持的模型。",
2873
3653
  serviceWeb: "Web Tools",
2874
- serviceWebDescription: "为原生 web_search 与 web_fetch 提供 Modellix Provider。",
3654
+ serviceWebDescription: "提供可自动调用的 Modellix 网页搜索与读取工具。",
2875
3655
  settingsTitle: "Modellix",
2876
3656
  settingsDescription: "一个 API Key,统一使用 Design、LLM 与 Web Tools。",
2877
3657
  credentialTitle: "API Key",
@@ -2914,13 +3694,13 @@ const zh = {
2914
3694
  errorGeneric: "暂时无法完成操作,请重试。",
2915
3695
  errorConflict: "配置已在其他位置更新,请刷新后重试。",
2916
3696
  errorKeyInvalid: "这个 API Key 无效,请检查后重新输入。",
2917
- errorBilling: "账户当前无法计费,请检查 Modellix 余额或账单状态。",
3697
+ errorBilling: "当前请求无法完成,请在 Modellix 控制台查看账户状态。",
2918
3698
  errorRateLimited: "请求过于频繁,请稍后手动重试。",
2919
3699
  errorOffline: "当前无法连接 Modellix,请检查网络后重试。",
2920
3700
  errorTimeout: "Modellix 响应超时,请稍后手动重试。",
2921
3701
  errorServer: "Modellix 服务暂时不可用,请稍后手动重试。",
2922
3702
  errorPolicy: "当前账户或环境策略不允许此操作。",
2923
- errorSubmitUnknown: "提交结果未知。请先查看结果列表,避免重复计费。",
3703
+ errorSubmitUnknown: "提交结果未知。请先查看结果列表,不要重复提交。",
2924
3704
  errorAssetExpired: "结果资源已过期,无法继续访问。",
2925
3705
  errorDesignSchema: "模型参数定义已变化或无法解析,请刷新模型后重试。",
2926
3706
  designTitle: "Modellix Design",
@@ -2940,11 +3720,11 @@ const zh = {
2940
3720
  promptPlaceholder: "描述画面、视频动作或要合成的声音…",
2941
3721
  parametersTitle: "参数",
2942
3722
  advancedParameters: "高级参数({count})",
3723
+ advancedEditor: "高级参数编辑器",
2943
3724
  required: "必填",
2944
3725
  optional: "可选",
2945
3726
  requiredMissing: "请填写所有必填参数后再生成。",
2946
3727
  assistantTitle: "用对话调整参数",
2947
- assistantPaidNotice: "发送后会使用同一个 Modellix Key 调用固定 LLM 生成参数提议,可能产生费用;不会自动生成媒体。",
2948
3728
  assistantPlaceholder: "例如:改成 16:9,生成 8 秒视频,风格更电影化",
2949
3729
  propose: "生成参数提议",
2950
3730
  proposing: "正在分析…",
@@ -2956,7 +3736,6 @@ const zh = {
2956
3736
  conflicts: "需要先解决冲突",
2957
3737
  generate: "确认并生成",
2958
3738
  generating: "正在提交…",
2959
- paidNotice: "生成是计费操作;每次点击只提交一次,不会自动重试。",
2960
3739
  designDisabled: "Design 已关闭,可在 Modellix 设置中重新开启。",
2961
3740
  keyRequired: "请先在 Modellix 设置中配置 API Key。",
2962
3741
  noModels: "当前没有可用模型,请稍后刷新。",
@@ -2970,9 +3749,17 @@ const zh = {
2970
3749
  designNoticeCatalogUnavailable: "Modellix Design 模型目录暂时不可用,请稍后刷新。",
2971
3750
  designNoticeCredentialReloaded: "API Key 已更改,Design 模型和参数已重新加载。",
2972
3751
  resultsTitle: "结果",
2973
- runningTitle: "进行中",
2974
- succeededTitle: "已完成",
2975
- diagnosticsTitle: "诊断",
3752
+ resultsCount: "{count} 条",
3753
+ resultViewLabel: "结果视图",
3754
+ previewTab: "预览",
3755
+ jsonTab: "JSON",
3756
+ noPreview: "当前还没有可预览的媒体结果。",
3757
+ resultNotFound: "未找到对应的生成任务。",
3758
+ resourceNavigation: "媒体结果导航",
3759
+ previousResource: "上一项",
3760
+ nextResource: "下一项",
3761
+ resourcePosition: "第 {current} / {total} 项",
3762
+ addUrlToChat: "添加 URL 到对话框",
2976
3763
  noResults: "还没有生成记录。确认参数并生成后,结果会显示在这里。",
2977
3764
  running: "生成中",
2978
3765
  succeeded: "已完成",
@@ -2980,10 +3767,10 @@ const zh = {
2980
3767
  canceled: "已取消",
2981
3768
  unknown: "提交结果未知",
2982
3769
  expired: "结果已过期",
2983
- openImage: "打开原图",
3770
+ openImage: "放大图片",
2984
3771
  imageViewerTitle: "生成图片",
2985
3772
  close: "关闭",
2986
- download: "下载结果",
3773
+ download: "下载",
2987
3774
  expiresAt: "有效期至 {time}",
2988
3775
  invalidJson: "请输入有效的 JSON。",
2989
3776
  invalidParameter: "此参数不符合当前模型的约束。",
@@ -2994,9 +3781,9 @@ const zh = {
2994
3781
  generatedAudioPreview: "生成音频结果预览",
2995
3782
  jobModel: "模型:{model}",
2996
3783
  jobCreated: "创建于 {time}",
2997
- diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复计费生成提交。",
3784
+ diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复生成提交。",
2998
3785
  diagnosticCredentialChanged: "此任务属于更早的 API Key,无法继续刷新。",
2999
- diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,避免重复计费。",
3786
+ diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,不要重复提交。",
3000
3787
  diagnosticGenerationFailed: "生成任务未完成。",
3001
3788
  diagnosticResultUnavailable: "生成已完成,但没有可用的结果资源。",
3002
3789
  diagnosticCredentialRejected: "刷新任务时 Modellix 拒绝了当前 API Key。",
@@ -3015,7 +3802,6 @@ const zh = {
3015
3802
  const en = {
3016
3803
  dateLocale: "en-US",
3017
3804
  nav: "Modellix",
3018
- designTab: "Design",
3019
3805
  onboardingTitle: "Connect Modellix",
3020
3806
  onboardingDescription: "Enter one Modellix API Key to enable Design, LLM models, and Web Tools.",
3021
3807
  keyLabel: "Modellix API Key",
@@ -3032,7 +3818,7 @@ const en = {
3032
3818
  serviceLlm: "LLM models",
3033
3819
  serviceLlmDescription: "Quickly switch between Modellix models in Harness.",
3034
3820
  serviceWeb: "Web Tools",
3035
- serviceWebDescription: "Provide Modellix for native web_search and web_fetch.",
3821
+ serviceWebDescription: "Provide automatic Modellix Web Search and Web Fetch tools.",
3036
3822
  settingsTitle: "Modellix",
3037
3823
  settingsDescription: "One API Key for Design, LLM, and Web Tools.",
3038
3824
  credentialTitle: "API Key",
@@ -3075,13 +3861,13 @@ const en = {
3075
3861
  errorGeneric: "The operation could not be completed. Try again.",
3076
3862
  errorConflict: "Settings changed elsewhere. Refresh and try again.",
3077
3863
  errorKeyInvalid: "This API Key is invalid. Check it and enter it again.",
3078
- errorBilling: "Billing is unavailable. Check the Modellix balance or billing status.",
3864
+ errorBilling: "This request cannot be completed. Check the account status in the Modellix console.",
3079
3865
  errorRateLimited: "Too many requests. Wait before retrying manually.",
3080
3866
  errorOffline: "Modellix cannot be reached. Check the network and try again.",
3081
3867
  errorTimeout: "Modellix timed out. Retry manually later.",
3082
3868
  errorServer: "Modellix is temporarily unavailable. Retry manually later.",
3083
3869
  errorPolicy: "The current account or environment policy does not allow this operation.",
3084
- errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting another billed request.",
3870
+ errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting again.",
3085
3871
  errorAssetExpired: "This result resource has expired and is no longer available.",
3086
3872
  errorDesignSchema: "The model parameter definition changed or could not be parsed. Refresh the model and try again.",
3087
3873
  designTitle: "Modellix Design",
@@ -3101,11 +3887,11 @@ const en = {
3101
3887
  promptPlaceholder: "Describe the image, video motion, or sound…",
3102
3888
  parametersTitle: "Parameters",
3103
3889
  advancedParameters: "Advanced parameters ({count})",
3890
+ advancedEditor: "Advanced parameter editor",
3104
3891
  required: "Required",
3105
3892
  optional: "Optional",
3106
3893
  requiredMissing: "Complete every required parameter before generating.",
3107
3894
  assistantTitle: "Adjust parameters by chat",
3108
- assistantPaidNotice: "Sending calls a fixed LLM with the same Modellix Key to propose parameter changes and may incur a charge. It never starts media generation.",
3109
3895
  assistantPlaceholder: "For example: make it 16:9, eight seconds, and more cinematic",
3110
3896
  propose: "Propose parameter changes",
3111
3897
  proposing: "Analyzing…",
@@ -3117,7 +3903,6 @@ const en = {
3117
3903
  conflicts: "Resolve conflicts before applying",
3118
3904
  generate: "Confirm and generate",
3119
3905
  generating: "Submitting…",
3120
- paidNotice: "Generation is billed. Each click submits once and is never retried automatically.",
3121
3906
  designDisabled: "Design is disabled. Enable it in Modellix settings.",
3122
3907
  keyRequired: "Configure a Modellix API Key in settings first.",
3123
3908
  noModels: "No model is currently available. Refresh and try again.",
@@ -3131,9 +3916,17 @@ const en = {
3131
3916
  designNoticeCatalogUnavailable: "The Modellix Design model catalog is temporarily unavailable. Refresh it later.",
3132
3917
  designNoticeCredentialReloaded: "The API Key changed, so the Design model and parameters were reloaded.",
3133
3918
  resultsTitle: "Results",
3134
- runningTitle: "Running",
3135
- succeededTitle: "Succeeded",
3136
- diagnosticsTitle: "Diagnostics",
3919
+ resultsCount: "{count} item(s)",
3920
+ resultViewLabel: "Result view",
3921
+ previewTab: "Preview",
3922
+ jsonTab: "JSON",
3923
+ noPreview: "No media preview is available yet.",
3924
+ resultNotFound: "The requested generation task was not found.",
3925
+ resourceNavigation: "Media result navigation",
3926
+ previousResource: "Previous",
3927
+ nextResource: "Next",
3928
+ resourcePosition: "{current} of {total}",
3929
+ addUrlToChat: "Add URL to chat",
3137
3930
  noResults: "No generations yet. Confirm parameters and generate to see results here.",
3138
3931
  running: "Running",
3139
3932
  succeeded: "Succeeded",
@@ -3141,10 +3934,10 @@ const en = {
3141
3934
  canceled: "Canceled",
3142
3935
  unknown: "Submission outcome unknown",
3143
3936
  expired: "Result expired",
3144
- openImage: "Open full image",
3937
+ openImage: "Enlarge image",
3145
3938
  imageViewerTitle: "Generated image",
3146
3939
  close: "Close",
3147
- download: "Download result",
3940
+ download: "Download",
3148
3941
  expiresAt: "Available until {time}",
3149
3942
  invalidJson: "Enter valid JSON.",
3150
3943
  invalidParameter: "This value does not satisfy the current model constraints.",
@@ -3155,9 +3948,9 @@ const en = {
3155
3948
  generatedAudioPreview: "Generated audio result preview",
3156
3949
  jobModel: "Model: {model}",
3157
3950
  jobCreated: "Created {time}",
3158
- diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a billed generation submission automatically.",
3951
+ diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a generation submission automatically.",
3159
3952
  diagnosticCredentialChanged: "This task belongs to an earlier API Key and cannot be refreshed.",
3160
- diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting another billed request.",
3953
+ diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting again.",
3161
3954
  diagnosticGenerationFailed: "The generation did not complete.",
3162
3955
  diagnosticResultUnavailable: "The generation completed without a usable result resource.",
3163
3956
  diagnosticCredentialRejected: "Modellix rejected the current API Key while refreshing this task.",
@@ -3177,6 +3970,7 @@ const en = {
3177
3970
  //#region src/client/SettingsSection.tsx
3178
3971
  function ModellixSettingsSection({ controller, t }) {
3179
3972
  const state = useResourceState(controller.store);
3973
+ const settingsSectionRef = (0, react.useRef)(null);
3180
3974
  const snapshot = state.data;
3181
3975
  const [services, setServices] = (0, react.useState)(null);
3182
3976
  const [announcement, setAnnouncement] = (0, react.useState)("");
@@ -3204,6 +3998,9 @@ function ModellixSettingsSection({ controller, t }) {
3204
3998
  (0, react.useEffect)(() => {
3205
3999
  if (snapshot !== null) setServices(snapshot.services);
3206
4000
  }, [snapshot?.settingsRevision]);
4001
+ (0, react.useEffect)(() => {
4002
+ setAnnouncement("");
4003
+ }, [t]);
3207
4004
  const busy = state.pending !== null;
3208
4005
  const saveServices = (0, react.useCallback)(() => {
3209
4006
  if (snapshot === null || services === null || busy) return;
@@ -3219,6 +4016,7 @@ function ModellixSettingsSection({ controller, t }) {
3219
4016
  t
3220
4017
  ]);
3221
4018
  if (snapshot === null || services === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4019
+ ref: settingsSectionRef,
3222
4020
  className: "mdlx-settings",
3223
4021
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3224
4022
  className: "mdlx-live",
@@ -3239,6 +4037,7 @@ function ModellixSettingsSection({ controller, t }) {
3239
4037
  const canWriteCredential = credential.writable && credential.source !== "env";
3240
4038
  const llmRefreshDisabled = busy || !snapshot.services.llm || !credential.configured;
3241
4039
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4040
+ ref: settingsSectionRef,
3242
4041
  className: "mdlx-settings",
3243
4042
  children: [
3244
4043
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
@@ -3277,7 +4076,7 @@ function ModellixSettingsSection({ controller, t }) {
3277
4076
  type: "button",
3278
4077
  variant: !credential.configured || credential.verification === "invalid" ? "primary" : "outline",
3279
4078
  disabled: busy,
3280
- onClick: () => credentialDialogs.open(credentialDialogOwner),
4079
+ onClick: () => credentialDialogs.openCredential(credentialDialogOwner),
3281
4080
  children: credential.configured ? t("replaceKey") : t("configureKey")
3282
4081
  }), credential.configured && canWriteCredential && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3283
4082
  type: "button",
@@ -3356,7 +4155,8 @@ function ModellixSettingsSection({ controller, t }) {
3356
4155
  },
3357
4156
  onCancel: () => credentialDialogs.dismissCredential(credentialDialogOwner),
3358
4157
  laterLabel: credentialRecovery ? "later" : "cancel",
3359
- t
4158
+ t,
4159
+ externalDialogOwner: settingsSectionRef
3360
4160
  }),
3361
4161
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RemoveCredentialDialog, {
3362
4162
  open: removeOpen,
@@ -3371,7 +4171,8 @@ function ModellixSettingsSection({ controller, t }) {
3371
4171
  setAnnouncement(t("saved"));
3372
4172
  });
3373
4173
  },
3374
- t
4174
+ t,
4175
+ externalDialogOwner: settingsSectionRef
3375
4176
  })
3376
4177
  ]
3377
4178
  });
@@ -3426,12 +4227,13 @@ function LlmCatalogCard({ snapshot, dateLocale, busy, disabled, onRefresh, t })
3426
4227
  ]
3427
4228
  });
3428
4229
  }
3429
- function RemoveCredentialDialog({ open, busy, errorCode, onClose, onConfirm, t }) {
4230
+ function RemoveCredentialDialog({ open, busy, errorCode, onClose, onConfirm, t, externalDialogOwner }) {
3430
4231
  const contentRef = (0, react.useRef)(null);
3431
- const surfaceOpen = useExternalDialogGate(open);
4232
+ const surfaceOpen = useExternalDialogGate(open, externalDialogOwner);
3432
4233
  useDialogA11y({
3433
4234
  open: surfaceOpen,
3434
4235
  container: contentRef,
4236
+ externalDialogOwner,
3435
4237
  initialFocusSelector: "[data-mdlx-initial-focus]",
3436
4238
  mandatory: false,
3437
4239
  onEscape: onClose
@@ -3505,7 +4307,7 @@ function llmHealthText(health, t) {
3505
4307
  //#endregion
3506
4308
  //#region src/client/styles.ts
3507
4309
  const MODELLIX_CLIENT_CSS = String.raw`
3508
- .mdlx-settings,.mdlx-design,.mdlx-modal-content{font-family:var(--dsw-font-family,inherit);color:var(--dsw-alias-label-primary);box-sizing:border-box}.mdlx-settings *,.mdlx-design *,.mdlx-modal-content *{box-sizing:border-box}
4310
+ .mdlx-settings,.mdlx-design,.mdlx-design-drawer,.mdlx-tool-result,.mdlx-modal-content{font-family:var(--dsw-font-family,inherit);color:var(--dsw-alias-label-primary);box-sizing:border-box}.mdlx-settings *,.mdlx-design *,.mdlx-design-drawer *,.mdlx-tool-result *,.mdlx-modal-content *{box-sizing:border-box}
3509
4311
  .mdlx-settings{width:min(100%,760px);min-width:0;display:grid;gap:12px;padding:4px}
3510
4312
  .mdlx-heading{display:grid;gap:4px;min-width:0;margin:0 0 8px}.mdlx-heading h2,.mdlx-heading h3,.mdlx-heading p{margin:0;overflow-wrap:anywhere}
3511
4313
  .mdlx-heading h2{font-size:20px;line-height:28px;font-weight:500}.mdlx-heading h3{font-size:15px;line-height:22px;font-weight:600}
@@ -3514,7 +4316,7 @@ const MODELLIX_CLIENT_CSS = String.raw`
3514
4316
  .mdlx-card-head,.mdlx-status-row,.mdlx-actions,.mdlx-result-head{display:flex;align-items:center;gap:12px;min-width:0;justify-content:space-between;flex-wrap:wrap}
3515
4317
  .mdlx-card-head>.mdlx-heading{margin-bottom:0}
3516
4318
  .mdlx-status-copy{display:flex;align-items:center;gap:8px;min-width:0}.mdlx-status-copy span{overflow-wrap:anywhere}
3517
- .mdlx-actions{justify-content:flex-end}.mdlx-actions>*{min-width:0;max-width:100%}.mdlx-actions-start{justify-content:flex-start}.mdlx-grow{flex:1 1 auto}.mdlx-settings button,.mdlx-design button,.mdlx-modal-content button{height:auto;min-height:36px}
4319
+ .mdlx-actions{justify-content:flex-end}.mdlx-actions>*{min-width:0;max-width:100%}.mdlx-actions-start{justify-content:flex-start}.mdlx-grow{flex:1 1 auto}.mdlx-settings button,.mdlx-design button,.mdlx-design-drawer button,.mdlx-tool-result button,.mdlx-modal-content button{height:auto;min-height:36px}
3518
4320
  .mdlx-service-list{display:grid;gap:8px}.mdlx-switch-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;padding:12px;border-radius:12px;border:1px solid var(--dsw-alias-border-l2)}
3519
4321
  .mdlx-switch-copy{display:grid;gap:2px;min-width:0}.mdlx-switch-copy strong{font-size:14px;font-weight:500;overflow-wrap:anywhere}.mdlx-switch-copy span{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;overflow-wrap:anywhere}
3520
4322
  .mdlx-switch-target{display:inline-flex;width:24px;height:24px;align-items:center;justify-content:center;justify-self:center}.mdlx-switch{width:24px;height:24px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}.mdlx-switch:disabled{cursor:not-allowed}
@@ -3531,25 +4333,28 @@ const MODELLIX_CLIENT_CSS = String.raw`
3531
4333
  .mdlx-link-button{appearance:none;border:0;padding:0;background:transparent;font:inherit;cursor:pointer}
3532
4334
  .mdlx-modal{box-sizing:border-box;width:min(600px,calc(100vw - 48px));max-height:calc(100dvh - 48px);padding:0!important;gap:0!important;border-radius:24px!important;box-shadow:var(--dsw-shadow-lv3)!important;overflow:auto;overscroll-behavior:contain}.mdlx-modal-confirm{width:min(380px,calc(100vw - 48px))}.mdlx-modal-content{width:100%;min-width:0;padding:28px;display:grid;gap:20px;outline:none}.mdlx-modal-content>.mdlx-heading{margin-bottom:0}.mdlx-modal-title{font-size:20px;line-height:28px;font-weight:500;margin:0;overflow-wrap:anywhere}.mdlx-modal-description{font-size:14px;line-height:22px;color:var(--dsw-alias-label-secondary);margin:0;overflow-wrap:anywhere}
3533
4335
  .mdlx-design{container-type:inline-size;height:100%;min-height:0;box-sizing:border-box;padding:16px;padding-bottom:max(16px,env(safe-area-inset-bottom));scroll-padding-block-end:max(24px,env(safe-area-inset-bottom));overflow:auto;background:var(--dsw-alias-bg-base)}
3534
- .mdlx-design-shell{width:100%;min-height:100%;display:grid;grid-template-columns:minmax(300px,42fr) minmax(360px,58fr);gap:16px}.mdlx-design-pane{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:16px;display:flex;flex-direction:column;gap:16px;overflow-wrap:anywhere}
4336
+ .mdlx-design-shell{width:100%;min-height:100%;display:grid;grid-template-columns:minmax(0,1fr);align-content:start;gap:12px}.mdlx-design-pane{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:16px;display:flex;flex-direction:column;gap:16px;overflow-wrap:anywhere}
4337
+ .mdlx-design-launcher{height:32px!important;min-height:32px!important;padding:6px 12px!important;border-radius:18px!important;font-size:13px!important;font-weight:400!important;line-height:20px!important}.mdlx-design-launcher .mdlx-launcher-compact{display:none}.mdlx-design-drawer-viewport{position:fixed;inset:0 0 0 auto;width:360px;max-width:100vw;overflow:hidden;pointer-events:none}.mdlx-design-drawer{position:absolute;inset:0 0 0 auto;width:360px;min-width:360px;display:grid;grid-template-rows:auto minmax(0,1fr);border-inline-start:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);pointer-events:auto;transform:translateX(0);opacity:1;transition:transform 180ms ease-out,opacity 180ms ease-out}@starting-style{.mdlx-design-drawer{transform:translateX(100%);opacity:.72}}.mdlx-design-drawer-viewport[data-closing="true"] .mdlx-design-drawer{transform:translateX(100%);opacity:.72;pointer-events:none}.mdlx-drawer-header{position:relative;display:grid;gap:0;padding:16px 60px 16px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.mdlx-drawer-header>.mdlx-heading{margin:0}.mdlx-drawer-close{position:absolute!important;inset-block-start:12px;inset-inline-end:12px;width:36px!important;min-width:36px!important;height:36px!important;min-height:36px!important;padding:0!important;border-radius:50%!important;justify-content:center}.mdlx-drawer-header-reveal{display:grid;grid-template-rows:0fr;opacity:0;margin-top:0;transition:grid-template-rows 160ms ease,opacity 120ms ease,margin-top 160ms ease}.mdlx-drawer-header-reveal[hidden]{display:none!important}.mdlx-drawer-header-reveal>div{min-height:0;overflow:hidden}.mdlx-drawer-header:hover .mdlx-drawer-header-reveal,.mdlx-drawer-header:focus-within .mdlx-drawer-header-reveal,.mdlx-drawer-header[data-editor-open="true"] .mdlx-drawer-header-reveal{grid-template-rows:1fr;opacity:1;margin-top:12px}.mdlx-drawer-editor-toggle{justify-self:start}.mdlx-drawer-body{min-height:0;overflow:hidden}.mdlx-drawer-body>.mdlx-design{padding:12px}.mdlx-design-editor{display:grid;gap:12px}.mdlx-design-editor[hidden]{display:none}.mdlx-results-summary{cursor:pointer;min-height:40px;display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:15px;font-weight:600;overflow-wrap:anywhere}.mdlx-results-disclosure{display:block}.mdlx-results-summary{margin:-4px 0;padding:4px 0}.mdlx-results-body{padding-top:12px}
3535
4338
  .mdlx-design-scroll{display:grid;gap:16px;min-height:0}.mdlx-parameter-list{display:grid;gap:12px}.mdlx-parameter{display:grid;gap:6px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}
3536
4339
  .mdlx-advanced{display:grid;gap:12px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}.mdlx-advanced>summary{cursor:pointer;font-size:14px;font-weight:500;min-height:24px;overflow-wrap:anywhere}
3537
4340
  .mdlx-proposal{display:grid;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:12px;background:var(--dsw-alias-bg-layer-2)}
3538
4341
  .mdlx-generate-block{display:grid;gap:8px;min-width:0}.mdlx-generate-block>p{margin:0}
3539
4342
  .mdlx-change-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.mdlx-change{display:grid;grid-template-columns:minmax(100px,.4fr) minmax(0,1fr);gap:8px;min-width:0;font-size:13px}.mdlx-change>*{min-width:0;overflow-wrap:anywhere}.mdlx-code{font-family:var(--ds-font-family-code,monospace);font-size:12px;overflow-wrap:anywhere}
3540
4343
  .mdlx-result-section{display:grid;gap:10px}.mdlx-result-section h3{margin:0;font-size:14px;font-weight:600}.mdlx-result-list{display:grid;gap:12px;margin:0;padding:0;list-style:none}
3541
- .mdlx-result-card{display:grid;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:12px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}
3542
- .mdlx-resource-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.mdlx-resource{display:grid;gap:8px;min-width:0}.mdlx-media{display:block;width:100%;max-height:360px;object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
3543
- .mdlx-media-audio{min-height:48px}.mdlx-empty{display:grid;place-items:center;min-height:160px;text-align:center;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;padding:24px}
4344
+ .mdlx-result-card{display:block;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}.mdlx-result-disclosure{display:block}.mdlx-result-card-summary{position:relative;display:grid;gap:8px;min-height:72px;padding:12px 44px 12px 12px;cursor:pointer;list-style:none}.mdlx-result-card-summary::-webkit-details-marker{display:none}.mdlx-result-card-summary::after{content:"";position:absolute;inset-block-start:18px;inset-inline-end:18px;width:8px;height:8px;border-inline-end:2px solid currentColor;border-block-end:2px solid currentColor;transform:rotate(-45deg);transition:transform 160ms ease}.mdlx-result-disclosure[open]>.mdlx-result-card-summary::after{transform:rotate(45deg)}.mdlx-result-card-body{display:grid;gap:12px;padding:0 12px 12px}
4345
+ .mdlx-result-tabs{display:flex;align-items:center;gap:4px;border-bottom:1px solid var(--dsw-alias-border-l2)}.mdlx-result-tab{appearance:none;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;padding:8px 12px;cursor:pointer}.mdlx-result-tab[aria-selected="true"]{border-bottom-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary);font-weight:600}.mdlx-result-panel{display:grid;gap:12px;min-width:0}.mdlx-result-json{max-height:320px;margin:0;padding:12px;border-radius:12px;background:var(--dsw-alias-markdown-code-block,var(--dsw-alias-bg-layer-3));font-family:var(--ds-font-family-code,monospace);font-size:12px;line-height:18px;white-space:pre-wrap;overflow:auto;overflow-wrap:anywhere}.mdlx-resource-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.mdlx-resource{display:grid;gap:8px;min-width:0}.mdlx-resource-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}.mdlx-media{display:block;width:100%;max-height:420px;object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}.mdlx-image-preview-button{appearance:none;width:100%;padding:0;border:0;border-radius:12px;background:transparent;cursor:zoom-in}.mdlx-image-preview-button>.mdlx-media{pointer-events:none}
4346
+ .mdlx-media-audio{min-height:48px}.mdlx-empty{display:grid;place-items:center;min-height:160px;text-align:center;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;padding:24px}.mdlx-empty-compact{min-height:96px}.mdlx-tool-result{display:grid;gap:12px;margin:4px 0 4px 4px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}
3544
4347
  .mdlx-image-modal{width:min(1000px,calc(100vw - 48px))}.mdlx-image-full{display:block;width:100%;max-height:calc(100dvh - 220px);object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
3545
4348
  .mdlx-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
3546
- .mdlx-input{height:auto;min-height:40px}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
4349
+ .mdlx-input{height:auto;min-height:40px}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible,.mdlx-result-tab:focus-visible,.mdlx-image-preview-button:focus-visible,.mdlx-drawer-editor-toggle:focus-visible,.mdlx-results-summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.mdlx-result-card-summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
4350
+ @media (min-width:1280px){.mdlx-design-drawer{box-shadow:none}}
3547
4351
  @media (max-width:768px){.mdlx-design{padding:12px;padding-bottom:max(12px,env(safe-area-inset-bottom))}.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
3548
4352
  @container (max-width:992px){.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
3549
- @media (max-width:560px){.mdlx-modal{width:calc(100vw - 48px)}.mdlx-modal-content{padding:24px}.mdlx-input-row,.mdlx-model-tools{grid-template-columns:minmax(0,1fr)}.mdlx-input-row>button,.mdlx-model-tools button,.mdlx-actions button{width:100%;min-width:0;justify-self:stretch}.mdlx-actions{display:grid;grid-template-columns:minmax(0,1fr)}.mdlx-switch-row{grid-template-columns:minmax(0,1fr) 48px}.mdlx-change{grid-template-columns:minmax(0,1fr)}}
3550
- @media (pointer:coarse){.mdlx-switch-row,.mdlx-switch-target,.mdlx-safe-link{min-width:48px;min-height:48px}.mdlx-settings button,.mdlx-design button,.mdlx-modal-content button{min-height:48px}.mdlx-input,.mdlx-select,.mdlx-native-input,.mdlx-advanced>summary{min-height:48px}}
3551
- @media (prefers-reduced-motion:reduce){.mdlx-settings *,.mdlx-design *,.mdlx-modal-content *{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
3552
- @media (forced-colors:active){.mdlx-modal{border:1px solid CanvasText;box-shadow:none!important}.mdlx-card,.mdlx-switch-row,.mdlx-design-pane,.mdlx-result-card,.mdlx-proposal,.mdlx-error,.mdlx-info{forced-color-adjust:auto;border:1px solid CanvasText}.mdlx-input,.mdlx-textarea,.mdlx-select,.mdlx-native-input{border-color:CanvasText}.mdlx-safe-link{color:LinkText}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible{outline-color:Highlight}}
4353
+ @media (max-width:560px){.mdlx-design-launcher{width:40px;min-width:40px;padding-inline:0!important}.mdlx-design-launcher .mdlx-launcher-label{display:none}.mdlx-design-launcher .mdlx-launcher-compact{display:inline}.mdlx-modal{width:calc(100vw - 48px)}.mdlx-modal-content{padding:24px}.mdlx-input-row,.mdlx-model-tools{grid-template-columns:minmax(0,1fr)}.mdlx-input-row>button,.mdlx-model-tools button,.mdlx-actions button{width:100%;min-width:0;justify-self:stretch}.mdlx-actions{display:grid;grid-template-columns:minmax(0,1fr)}.mdlx-switch-row{grid-template-columns:minmax(0,1fr) 48px}.mdlx-change{grid-template-columns:minmax(0,1fr)}.mdlx-design-drawer-viewport,.mdlx-design-drawer{width:100vw;min-width:0}.mdlx-drawer-header{padding:12px 56px 12px 12px}.mdlx-drawer-close{inset-block-start:8px;inset-inline-end:8px}.mdlx-resource-nav{display:grid;grid-template-columns:1fr}.mdlx-resource-nav>*{width:100%;text-align:center}}
4354
+ @media (max-width:360px){.mdlx-design-launcher{position:fixed!important;inset-block-start:92px;inset-inline-end:12px;z-index:10;width:48px;min-width:48px;height:48px;min-height:48px!important;padding:0!important;border-radius:50%!important}}
4355
+ @media (pointer:coarse){.mdlx-design-launcher{height:48px!important;min-height:48px!important}.mdlx-drawer-header-reveal{grid-template-rows:1fr;opacity:1;margin-top:12px}.mdlx-switch-row,.mdlx-switch-target,.mdlx-safe-link{min-width:48px;min-height:48px}.mdlx-settings button,.mdlx-design button,.mdlx-design-drawer button,.mdlx-tool-result button,.mdlx-modal-content button{min-height:48px}.mdlx-input,.mdlx-select,.mdlx-native-input,.mdlx-advanced>summary{min-height:48px}}
4356
+ @media (prefers-reduced-motion:reduce){.mdlx-design-drawer-viewport,.mdlx-design-drawer,.mdlx-settings *,.mdlx-design *,.mdlx-design-drawer *,.mdlx-tool-result *,.mdlx-modal-content *{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
4357
+ @media (forced-colors:active){.mdlx-modal,.mdlx-design-drawer{border:1px solid CanvasText;box-shadow:none!important}.mdlx-card,.mdlx-switch-row,.mdlx-design-pane,.mdlx-result-card,.mdlx-tool-result,.mdlx-proposal,.mdlx-error,.mdlx-info{forced-color-adjust:auto;border:1px solid CanvasText}.mdlx-input,.mdlx-textarea,.mdlx-select,.mdlx-native-input{border-color:CanvasText}.mdlx-safe-link{color:LinkText}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible,.mdlx-result-tab:focus-visible,.mdlx-image-preview-button:focus-visible,.mdlx-drawer-editor-toggle:focus-visible,.mdlx-results-summary:focus-visible,.mdlx-result-card-summary:focus-visible{outline-color:Highlight}}
3553
4358
  `;
3554
4359
  function installModellixClientStyles(target = typeof document === "undefined" ? void 0 : document) {
3555
4360
  if (target === void 0) return () => void 0;
@@ -3567,6 +4372,11 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3567
4372
  id: "modellix.credential-recovery",
3568
4373
  order: 10
3569
4374
  },
4375
+ {
4376
+ name: "shell.overlay",
4377
+ id: "modellix.design-drawer",
4378
+ order: 20
4379
+ },
3570
4380
  {
3571
4381
  name: "settings.onboarding",
3572
4382
  id: "modellix.onboarding",
@@ -3578,9 +4388,19 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3578
4388
  order: 30
3579
4389
  },
3580
4390
  {
3581
- name: "conversation.view",
3582
- id: "modellix.design",
4391
+ name: "conversation.session.header.utilities",
4392
+ id: "modellix.design-launcher",
3583
4393
  order: 20
4394
+ },
4395
+ {
4396
+ name: "tool.call.toolview",
4397
+ key: "modellix_media_generate",
4398
+ priority: 20
4399
+ },
4400
+ {
4401
+ name: "tool.call.toolview",
4402
+ key: "modellix_media_get_result",
4403
+ priority: 20
3584
4404
  }
3585
4405
  ]);
3586
4406
  //#endregion
@@ -3588,7 +4408,8 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3588
4408
  const inject = [
3589
4409
  "slots",
3590
4410
  "locale",
3591
- "connection"
4411
+ "connection",
4412
+ "layout"
3592
4413
  ];
3593
4414
  function apply(ctx) {
3594
4415
  ctx.effect(() => ctx.locale.register(MODELLIX_LOCALE_NAMESPACE, {
@@ -3599,6 +4420,7 @@ function apply(ctx) {
3599
4420
  const t = ctx.locale.bind(MODELLIX_LOCALE_NAMESPACE);
3600
4421
  const rpc = new ModellixRpcClient(ctx.connection.rpc);
3601
4422
  const settingsController = new SettingsController(rpc);
4423
+ const designDrawer = new DesignDrawerController(rpc, ctx.layout);
3602
4424
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
3603
4425
  name: "shell.overlay",
3604
4426
  id: "modellix.credential-recovery",
@@ -3606,6 +4428,16 @@ function apply(ctx) {
3606
4428
  locale: MODELLIX_LOCALE_NAMESPACE,
3607
4429
  inject: () => ({ controller: settingsController })
3608
4430
  }, CredentialRecoveryOverlay));
4431
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
4432
+ name: "shell.overlay",
4433
+ id: "modellix.design-drawer",
4434
+ order: 20,
4435
+ locale: MODELLIX_LOCALE_NAMESPACE,
4436
+ inject: () => ({
4437
+ drawer: designDrawer,
4438
+ settingsController
4439
+ })
4440
+ }, ModellixDesignDrawer));
3609
4441
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3610
4442
  name: "settings.onboarding",
3611
4443
  id: "modellix.onboarding",
@@ -3621,17 +4453,27 @@ function apply(ctx) {
3621
4453
  locale: MODELLIX_LOCALE_NAMESPACE,
3622
4454
  inject: () => ({ controller: settingsController })
3623
4455
  }, ModellixSettingsSection));
3624
- ctx.slots.inject("conversation.view", () => ctx.slots.register({
3625
- name: "conversation.view",
3626
- id: "modellix.design",
4456
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
4457
+ name: "conversation.session.header.utilities",
4458
+ id: "modellix.design-launcher",
3627
4459
  order: 20,
3628
- label: () => t("designTab"),
3629
4460
  locale: MODELLIX_LOCALE_NAMESPACE,
3630
- inject: (sessionId) => ({
3631
- controller: new DesignController(rpc, sessionId),
3632
- settingsController
3633
- })
3634
- }, ModellixDesignView));
4461
+ inject: () => ({ drawer: designDrawer })
4462
+ }, ModellixDesignLauncher));
4463
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4464
+ name: "tool.call.toolview",
4465
+ key: "modellix_media_generate",
4466
+ priority: 20,
4467
+ locale: MODELLIX_LOCALE_NAMESPACE,
4468
+ inject: () => ({ drawer: designDrawer })
4469
+ }, ModellixMediaToolView));
4470
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4471
+ name: "tool.call.toolview",
4472
+ key: "modellix_media_get_result",
4473
+ priority: 20,
4474
+ locale: MODELLIX_LOCALE_NAMESPACE,
4475
+ inject: () => ({ drawer: designDrawer })
4476
+ }, ModellixMediaToolView));
3635
4477
  }
3636
4478
  //#endregion
3637
4479
  exports.MODELLIX_CLIENT_SLOTS = MODELLIX_CLIENT_SLOTS;