dsh-modellix 0.1.1 → 0.2.1

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 +60 -0
  2. package/README.md +107 -167
  3. package/README.zh-CN.md +105 -165
  4. package/cordis.patch.yml +0 -13
  5. package/docs/assets/chat-media-generation-en.webp +0 -0
  6. package/docs/assets/chat-media-generation-zh.webp +0 -0
  7. package/docs/assets/design-results-drawer-en.webp +0 -0
  8. package/docs/assets/design-results-drawer-zh.webp +0 -0
  9. package/docs/assets/llm-model-selector-en.webp +0 -0
  10. package/docs/assets/llm-model-selector-zh.webp +0 -0
  11. package/docs/assets/media-players-en.webp +0 -0
  12. package/docs/assets/media-players-zh.webp +0 -0
  13. package/docs/assets/settings-ready-en.webp +0 -0
  14. package/docs/assets/settings-ready-zh.webp +0 -0
  15. package/docs/assets/web-tools-auto-en.webp +0 -0
  16. package/docs/assets/web-tools-auto-zh.webp +0 -0
  17. package/docs/en-US/LOCAL_USAGE.md +20 -16
  18. package/docs/en-US/RELEASE_CHECKLIST.md +270 -0
  19. package/docs/en-US/USER_GUIDE.md +186 -394
  20. package/docs/zh-CN/LOCAL_USAGE.md +20 -16
  21. package/docs/zh-CN/RELEASE_CHECKLIST.md +270 -0
  22. package/docs/zh-CN/USER_GUIDE.md +185 -393
  23. package/lib/client.d.ts +18 -4
  24. package/lib/client.js +1271 -454
  25. package/lib/client.js.map +1 -1
  26. package/lib/index.d.ts +166 -112
  27. package/lib/index.js +1008 -197
  28. package/lib/index.js.map +1 -1
  29. package/package.json +72 -48
  30. package/docs/assets/design-desktop-en.webp +0 -0
  31. package/docs/assets/design-desktop-zh.webp +0 -0
  32. package/docs/assets/design-proposal-en.webp +0 -0
  33. package/docs/assets/design-proposal-zh.webp +0 -0
  34. package/docs/assets/design-results-media-en.webp +0 -0
  35. package/docs/assets/design-results-media-zh.webp +0 -0
  36. package/docs/assets/web-tools-en.webp +0 -0
  37. package/docs/assets/web-tools-zh.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 {
@@ -1926,8 +1928,28 @@ function DesignResultPreview({ resource, t }) {
1926
1928
  });
1927
1929
  }
1928
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
1929
1951
  //#region src/client/DesignView.tsx
1930
- function ModellixDesignView({ controller, settingsController, t }) {
1952
+ function ModellixDesignView({ advancedEditorOpen = true, controller, settingsController, onAddUrl, t }) {
1931
1953
  const state = useResourceState(controller.store);
1932
1954
  const settingsState = useResourceState(settingsController.store);
1933
1955
  const snapshot = state.data;
@@ -2069,307 +2091,294 @@ function ModellixDesignView({ controller, settingsController, t }) {
2069
2091
  className: "mdlx-design",
2070
2092
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2071
2093
  className: "mdlx-design-shell",
2072
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2073
- className: "mdlx-design-pane",
2074
- "aria-labelledby": "mdlx-design-title",
2075
- children: [
2076
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2077
- className: "mdlx-heading",
2078
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2079
- id: "mdlx-design-title",
2080
- children: t("designTitle")
2081
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2082
- className: "mdlx-muted",
2083
- children: t("designDescription")
2084
- })]
2085
- }),
2086
- !snapshot.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2087
- className: "mdlx-info",
2088
- children: t("designDisabled")
2089
- }),
2090
- !snapshot.credentialReady && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2091
- className: "mdlx-info",
2092
- children: [
2093
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("keyRequired") }),
2094
- settings?.credential.writable === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2095
- type: "button",
2096
- variant: "primary",
2097
- onClick: () => credentialDialogs.openCredential(credentialDialogOwner),
2098
- children: t("configureToContinue")
2099
- }),
2100
- settings?.credential.source === "env" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2101
- className: "mdlx-muted",
2102
- children: t("envReadonly")
2103
- })
2104
- ]
2105
- }),
2106
- snapshot.notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2107
- className: "mdlx-info",
2108
- role: "status",
2109
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("notice"), ": "] }), t(designNoticeMessageKey(snapshot.notice))]
2110
- }),
2111
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2112
- className: "mdlx-field",
2113
- children: [
2114
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2115
- className: "mdlx-label",
2116
- htmlFor: "mdlx-design-model",
2117
- children: t("modelLabel")
2118
- }),
2119
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2120
- className: "mdlx-model-tools",
2121
- children: [
2122
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2123
- className: "mdlx-input",
2124
- value: modelQuery,
2125
- maxLength: 512,
2126
- placeholder: t("modelSearchPlaceholder"),
2127
- "aria-label": t("modelSearchLabel"),
2128
- disabled: interactionBusy || !snapshot.enabled,
2129
- onChange: (event) => setModelQuery(event.currentTarget.value)
2130
- }),
2131
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2132
- className: "mdlx-select",
2133
- value: modelKind,
2134
- "aria-label": t("modelCategoryLabel"),
2135
- disabled: interactionBusy || !snapshot.enabled,
2136
- onChange: (event) => {
2137
- setModelKind(event.currentTarget.value);
2138
- },
2139
- children: [
2140
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2141
- value: "all",
2142
- children: t("modelCategoryAll")
2143
- }),
2144
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2145
- value: "image",
2146
- children: t("modelCategoryImage")
2147
- }),
2148
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2149
- value: "video",
2150
- children: t("modelCategoryVideo")
2151
- }),
2152
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2153
- value: "audio",
2154
- children: t("modelCategoryAudio")
2155
- })
2156
- ]
2157
- }),
2158
- /* @__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, {
2159
2151
  type: "button",
2160
2152
  variant: "outline",
2161
- disabled: interactionBusy || !snapshot.enabled || !snapshot.credentialReady,
2162
- "aria-busy": state.pending === "refresh-design",
2153
+ disabled: interactionBusy || instruction.trim().length === 0,
2154
+ "aria-busy": state.pending === "propose",
2163
2155
  onClick: () => {
2164
- controller.refreshCatalog();
2156
+ const request = instruction.trim();
2157
+ if (request === "") return;
2158
+ controller.propose(request, parameters).then((accepted) => {
2159
+ if (accepted) setInstruction("");
2160
+ });
2165
2161
  },
2166
- children: state.pending === "refresh-design" ? t("modelRefreshing") : t("modelRefresh")
2162
+ children: state.pending === "propose" ? t("proposing") : t("propose")
2167
2163
  })
2168
- ]
2169
- }),
2170
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2171
- id: "mdlx-design-model",
2172
- className: "mdlx-select",
2173
- value: snapshot.selectedModelId ?? "",
2174
- "aria-describedby": selectedModel?.available === false ? "mdlx-design-model-status" : void 0,
2175
- disabled: interactionBusy || !snapshot.enabled || snapshot.models.length === 0,
2176
- onChange: (event) => {
2177
- const modelId = event.currentTarget.value;
2178
- if (modelId !== "") controller.selectModel(modelId);
2179
- },
2180
- children: [
2181
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2182
- value: "",
2183
- disabled: true,
2184
- children: t("chooseModel")
2185
- }),
2186
- snapshot.selectedModelId !== null && !visibleModels.some((model) => model.id === snapshot.selectedModelId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2187
- value: snapshot.selectedModelId,
2188
- disabled: selectedModel?.available === false,
2189
- children: snapshot.models.find((model) => model.id === snapshot.selectedModelId)?.label ?? snapshot.selectedModelId
2190
- }),
2191
- visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2192
- value: model.id,
2193
- disabled: !model.available,
2194
- children: [model.label, model.featured ? " ★" : ""]
2195
- }, model.id))
2196
- ]
2197
- }),
2198
- selectedModel?.available === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2199
- id: "mdlx-design-model-status",
2200
- className: "mdlx-error",
2201
- role: "status",
2202
- children: selectedModel.unavailableReason === null ? t("modelUnavailable") : t(designModelUnavailableMessageKey(selectedModel.unavailableReason))
2203
- }),
2204
- snapshot.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2205
- className: "mdlx-help",
2206
- children: t("noModels")
2207
- }) : visibleModels.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2208
- className: "mdlx-help",
2209
- children: t("noMatchingModels")
2210
- })
2211
- ]
2212
- }),
2213
- draft !== null && promptPath !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2214
- className: "mdlx-field",
2215
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2216
- className: "mdlx-label",
2217
- htmlFor: "mdlx-design-prompt",
2164
+ })
2165
+ ]
2166
+ }),
2167
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2168
+ className: "mdlx-field",
2218
2169
  children: [
2219
- t("promptLabel"),
2220
- " ",
2221
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2222
- className: "mdlx-required",
2223
- 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")
2224
2266
  })
2225
2267
  ]
2226
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2227
- id: "mdlx-design-prompt",
2228
- className: "mdlx-textarea",
2229
- value: promptValue,
2230
- maxLength: fieldAtPath(draft.fields, promptPath)?.maxLength ?? void 0,
2231
- placeholder: t("promptPlaceholder"),
2232
- disabled: interactionBusy,
2233
- onChange: (event) => updateParameter(promptPath, event.currentTarget.value)
2234
- })]
2235
- }),
2236
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2237
- className: "mdlx-design-scroll",
2238
- "aria-labelledby": "mdlx-parameters-title",
2239
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2240
- className: "mdlx-heading",
2241
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2242
- id: "mdlx-parameters-title",
2243
- children: t("parametersTitle")
2244
- })
2245
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2246
- className: "mdlx-parameter-list",
2247
- children: [requiredFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2248
- field,
2249
- value: parameters[field.path],
2250
- 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"),
2251
2288
  disabled: interactionBusy,
2252
- onChange: (value) => updateParameter(field.path, value),
2253
- onValidityChange: (valid) => setFieldValidity(field.path, valid),
2254
- t
2255
- }, field.path)), optionalFields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2256
- className: "mdlx-advanced",
2257
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advancedParameters", { count: optionalFields.length }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2258
- className: "mdlx-parameter-list",
2259
- children: optionalFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2260
- field,
2261
- value: parameters[field.path],
2262
- invalid: invalidFields.has(field.path),
2263
- disabled: interactionBusy,
2264
- onChange: (value) => updateParameter(field.path, value),
2265
- onValidityChange: (valid) => setFieldValidity(field.path, valid),
2266
- t
2267
- }, field.path))
2268
- })]
2289
+ onChange: (event) => updateParameter(promptPath, event.currentTarget.value)
2269
2290
  })]
2270
- })]
2271
- }),
2272
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2273
- className: "mdlx-card",
2274
- "aria-labelledby": "mdlx-assistant-title",
2275
- children: [
2276
- /* @__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", {
2277
2296
  className: "mdlx-heading",
2278
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2279
- id: "mdlx-assistant-title",
2280
- children: t("assistantTitle")
2281
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2282
- id: "mdlx-assistant-paid-notice",
2283
- className: "mdlx-help",
2284
- children: t("assistantPaidNotice")
2285
- })]
2286
- }),
2287
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2288
- id: "mdlx-assistant-instruction",
2289
- className: "mdlx-textarea mdlx-textarea-small",
2290
- value: instruction,
2291
- maxLength: 8e3,
2292
- placeholder: t("assistantPlaceholder"),
2293
- "aria-labelledby": "mdlx-assistant-title",
2294
- "aria-describedby": "mdlx-assistant-paid-notice",
2295
- disabled: interactionBusy,
2296
- onChange: (event) => setInstruction(event.currentTarget.value)
2297
- }),
2298
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2299
- className: "mdlx-actions",
2300
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2301
- type: "button",
2302
- variant: "outline",
2303
- disabled: interactionBusy || instruction.trim().length === 0,
2304
- "aria-busy": state.pending === "propose",
2305
- onClick: () => {
2306
- const request = instruction.trim();
2307
- if (request === "") return;
2308
- controller.propose(request, parameters).then((accepted) => {
2309
- if (accepted) setInstruction("");
2310
- });
2311
- },
2312
- 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")
2313
2300
  })
2314
- })
2315
- ]
2316
- }),
2317
- snapshot.proposal !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProposalCard, {
2318
- snapshot,
2319
- parameters,
2320
- controller,
2321
- t
2322
- }),
2323
- draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2324
- className: "mdlx-generate-block",
2325
- children: [
2326
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2327
- className: "mdlx-help",
2328
- children: t("paidNotice")
2329
- }),
2330
- missingRequired && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2331
- className: "mdlx-error",
2332
- children: t("requiredMissing")
2333
- }),
2334
- invalidFields.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2335
- className: "mdlx-error",
2336
- children: t("parametersInvalid")
2337
- }),
2338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2339
- className: "mdlx-actions",
2340
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2341
- type: "button",
2342
- variant: snapshot.credentialReady && snapshot.proposal === null ? "primary" : "outline",
2343
- disabled: !canGenerate,
2344
- "aria-busy": submitting,
2345
- onClick: () => {
2346
- controller.submit(parameters);
2347
- },
2348
- 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
+ })
2349
2357
  })
2350
- })
2351
- ]
2352
- }),
2353
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
2354
- code: state.errorCode,
2355
- t
2356
- }),
2357
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
2358
- busy: interactionBusy,
2359
- text: operationText(state.pending, t)
2360
- }),
2361
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2362
- className: "mdlx-live",
2363
- role: "status",
2364
- "aria-live": "polite",
2365
- children: outcomeAnnouncement
2366
- })
2367
- ]
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
+ })
2368
2376
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResults, {
2369
2377
  snapshot,
2370
2378
  dateLocale: t("dateLocale"),
2371
2379
  dialogCoordinator: credentialDialogs,
2372
2380
  dialog: credentialDialog,
2381
+ ...onAddUrl === void 0 ? {} : { onAddUrl },
2373
2382
  t
2374
2383
  })]
2375
2384
  }), settings !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
@@ -2612,125 +2621,167 @@ function ProposalCard({ snapshot, parameters, controller, t }) {
2612
2621
  ]
2613
2622
  });
2614
2623
  }
2615
- function DesignResults({ snapshot, dateLocale, dialogCoordinator, dialog, t }) {
2616
- const groups = (0, react.useMemo)(() => {
2617
- const running = [];
2618
- const succeeded = [];
2619
- const diagnostics = [];
2620
- for (const job of snapshot.jobs) if (job.status === "running") running.push(job);
2621
- else if (job.status === "succeeded") succeeded.push(job);
2622
- else diagnostics.push(job);
2623
- return {
2624
- running,
2625
- succeeded,
2626
- diagnostics
2627
- };
2628
- }, [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]);
2629
2627
  const empty = snapshot.jobs.length === 0;
2630
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2631
- className: "mdlx-design-pane",
2632
- "aria-labelledby": "mdlx-results-title",
2633
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
2634
- className: "mdlx-heading",
2635
- 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", {
2636
2635
  id: "mdlx-results-title",
2637
2636
  children: t("resultsTitle")
2638
- })
2639
- }), empty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2640
- className: "mdlx-empty",
2641
- children: t("noResults")
2642
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2643
- className: "mdlx-design-scroll",
2644
- children: [
2645
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2646
- title: t("runningTitle"),
2647
- jobs: groups.running,
2648
- dateLocale,
2649
- dialogCoordinator,
2650
- dialog,
2651
- t
2652
- }),
2653
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2654
- title: t("succeededTitle"),
2655
- jobs: groups.succeeded,
2656
- dateLocale,
2657
- dialogCoordinator,
2658
- dialog,
2659
- t
2660
- }),
2661
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2662
- title: t("diagnosticsTitle"),
2663
- 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,
2664
2650
  dateLocale,
2665
2651
  dialogCoordinator,
2666
2652
  dialog,
2653
+ ...onAddUrl === void 0 ? {} : { onAddUrl },
2667
2654
  t
2668
- })
2669
- ]
2670
- })]
2671
- });
2672
- }
2673
- function ResultSection({ title, jobs, dateLocale, dialogCoordinator, dialog, t }) {
2674
- if (jobs.length === 0) return null;
2675
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2676
- className: "mdlx-result-section",
2677
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2678
- className: "mdlx-result-list",
2679
- children: jobs.map((job) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultCard, {
2680
- job,
2681
- dateLocale,
2682
- dialogCoordinator,
2683
- dialog,
2684
- t
2685
- }, job.jobId))
2655
+ }, job.jobId))
2656
+ })
2686
2657
  })]
2687
2658
  });
2688
2659
  }
2689
- 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)();
2690
2665
  const status = jobStatus(job.status, t);
2691
2666
  const created = formatTime(job.createdAt, dateLocale);
2692
- 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", {
2693
2675
  className: "mdlx-result-card",
2694
- children: [
2695
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2696
- 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",
2697
2682
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2698
- className: "mdlx-status-copy",
2699
- 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
+ })]
2700
2691
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2701
2692
  className: "mdlx-muted",
2702
- children: t("jobCreated", { time: created })
2693
+ children: t("jobModel", { model: job.modelId })
2703
2694
  })]
2704
- }),
2705
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2706
- className: "mdlx-muted",
2707
- children: t("jobModel", { model: job.modelId })
2708
- }),
2709
- job.resources.length > 0 && job.status !== "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2710
- className: "mdlx-resource-list",
2711
- children: job.resources.map((resource) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultResource, {
2712
- resource,
2713
- dateLocale,
2714
- dialogCoordinator,
2715
- dialog,
2716
- t
2717
- }, resource.id))
2718
- }),
2719
- job.diagnostic !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2720
- className: "mdlx-error",
2721
- children: [
2722
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2723
- className: "mdlx-code",
2724
- children: job.diagnostic.code
2725
- }),
2726
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(designDiagnosticMessageKey(job.diagnostic.code)) }),
2727
- job.diagnostic.retryable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticRetryable") })
2728
- ]
2729
- })
2730
- ]
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
+ })
2731
2782
  });
2732
2783
  }
2733
- function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t }) {
2784
+ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, onAddUrl, t }) {
2734
2785
  const imageDialogOwner = `design-image:${(0, react.useId)()}`;
2735
2786
  const imageSurfaceOpen = useExternalDialogGate(dialog.activeOwner === imageDialogOwner);
2736
2787
  const imageDialogRef = (0, react.useRef)(null);
@@ -2746,29 +2797,34 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2746
2797
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
2747
2798
  className: "mdlx-resource",
2748
2799
  children: [
2749
- /* @__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, {
2750
2810
  resource,
2751
2811
  t
2752
2812
  }),
2753
2813
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2754
2814
  className: "mdlx-actions mdlx-actions-start",
2755
- 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, {
2756
2816
  type: "button",
2757
- className: "mdlx-safe-link mdlx-link-button",
2758
- onClick: () => dialogCoordinator.open(imageDialogOwner),
2759
- children: t("openImage")
2760
- }), /* @__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", {
2761
2821
  className: "mdlx-safe-link",
2762
2822
  href: resource.downloadUrl,
2763
2823
  target: "_blank",
2764
2824
  rel: "noopener noreferrer",
2765
2825
  referrerPolicy: "no-referrer",
2766
2826
  download: true,
2767
- children: [
2768
- t("download"),
2769
- " ",
2770
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2771
- ]
2827
+ children: t("download")
2772
2828
  })]
2773
2829
  }),
2774
2830
  resource.expiresAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -2779,7 +2835,6 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2779
2835
  }), resource.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2780
2836
  open: imageSurfaceOpen,
2781
2837
  title: t("imageViewerTitle"),
2782
- closeLabel: t("close"),
2783
2838
  onClose: closeImage,
2784
2839
  headless: true,
2785
2840
  className: "mdlx-modal mdlx-image-modal",
@@ -2810,18 +2865,14 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2810
2865
  "data-mdlx-initial-focus": "",
2811
2866
  onClick: closeImage,
2812
2867
  children: t("close")
2813
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2868
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2814
2869
  className: "mdlx-safe-link",
2815
2870
  href: resource.downloadUrl,
2816
2871
  target: "_blank",
2817
2872
  rel: "noopener noreferrer",
2818
2873
  referrerPolicy: "no-referrer",
2819
2874
  download: true,
2820
- children: [
2821
- t("download"),
2822
- " ",
2823
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2824
- ]
2875
+ children: t("download")
2825
2876
  })]
2826
2877
  })
2827
2878
  ]
@@ -2862,12 +2913,726 @@ function formatTime(value, locale) {
2862
2913
  }).format(new Date(value));
2863
2914
  }
2864
2915
  //#endregion
2916
+ //#region src/client/DesignDrawer.tsx
2917
+ const MODELLIX_DESIGN_SPLIT_MEDIA_QUERY = "(min-width: 1280px)";
2918
+ const MODELLIX_DRAWER_EXIT_MS = 180;
2919
+ const MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN = true;
2920
+ function isSplitViewport() {
2921
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(min-width: 1280px)").matches;
2922
+ }
2923
+ var DesignDrawerController = class {
2924
+ #rpc;
2925
+ #layout;
2926
+ #isSplitViewport;
2927
+ #listeners = /* @__PURE__ */ new Set();
2928
+ #controllers = /* @__PURE__ */ new Map();
2929
+ #watchedJobs = /* @__PURE__ */ new Map();
2930
+ #mediaCardClaims = /* @__PURE__ */ new Map();
2931
+ #mediaCardListeners = /* @__PURE__ */ new Set();
2932
+ #sessionReferences = /* @__PURE__ */ new Map();
2933
+ #sessionReleaseTimers = /* @__PURE__ */ new Map();
2934
+ #mediaCardVersion = 0;
2935
+ #mediaCardOrder = 0;
2936
+ #snapshot = {
2937
+ open: false,
2938
+ sessionId: null,
2939
+ controller: null,
2940
+ draft: "",
2941
+ inputActions: null
2942
+ };
2943
+ #trigger = null;
2944
+ #splitActive = false;
2945
+ constructor(rpc, layout, splitViewport = isSplitViewport) {
2946
+ this.#rpc = rpc;
2947
+ this.#layout = layout;
2948
+ this.#isSplitViewport = splitViewport;
2949
+ }
2950
+ subscribe = (listener) => {
2951
+ this.#listeners.add(listener);
2952
+ return () => this.#listeners.delete(listener);
2953
+ };
2954
+ getSnapshot = () => this.#snapshot;
2955
+ subscribeMediaCards = (listener) => {
2956
+ this.#mediaCardListeners.add(listener);
2957
+ return () => this.#mediaCardListeners.delete(listener);
2958
+ };
2959
+ getMediaCardVersion = () => this.#mediaCardVersion;
2960
+ claimMediaCard(sessionId, jobId, callId, operation) {
2961
+ const key = `${sessionId}\n${jobId}`;
2962
+ const claims = this.#mediaCardClaims.get(key) ?? /* @__PURE__ */ new Map();
2963
+ if (claims.has(callId)) return () => void 0;
2964
+ claims.set(callId, {
2965
+ operation,
2966
+ order: this.#mediaCardOrder++
2967
+ });
2968
+ this.#mediaCardClaims.set(key, claims);
2969
+ this.#publishMediaCards();
2970
+ return () => {
2971
+ const current = this.#mediaCardClaims.get(key);
2972
+ if (current?.delete(callId) !== true) return;
2973
+ if (current.size === 0) this.#mediaCardClaims.delete(key);
2974
+ this.#publishMediaCards();
2975
+ };
2976
+ }
2977
+ isMediaCardPrimary(sessionId, jobId, callId, operation) {
2978
+ const claims = this.#mediaCardClaims.get(`${sessionId}\n${jobId}`);
2979
+ if (claims === void 0 || claims.size === 0) return true;
2980
+ let primaryCallId = callId;
2981
+ let primary = {
2982
+ operation,
2983
+ order: Number.MAX_SAFE_INTEGER
2984
+ };
2985
+ for (const [candidateCallId, candidate] of claims) if (compareMediaCardClaims(candidate, primary) < 0) {
2986
+ primaryCallId = candidateCallId;
2987
+ primary = candidate;
2988
+ }
2989
+ return primaryCallId === callId;
2990
+ }
2991
+ controllerForSession(sessionId) {
2992
+ const existing = this.#controllers.get(sessionId);
2993
+ if (existing !== void 0) return existing;
2994
+ const controller = new DesignController(this.#rpc, sessionId);
2995
+ this.#controllers.set(sessionId, controller);
2996
+ return controller;
2997
+ }
2998
+ retainSession(sessionId) {
2999
+ const pendingRelease = this.#sessionReleaseTimers.get(sessionId);
3000
+ if (pendingRelease !== void 0) {
3001
+ window.clearTimeout(pendingRelease);
3002
+ this.#sessionReleaseTimers.delete(sessionId);
3003
+ }
3004
+ this.#sessionReferences.set(sessionId, (this.#sessionReferences.get(sessionId) ?? 0) + 1);
3005
+ let retained = true;
3006
+ return () => {
3007
+ if (!retained) return;
3008
+ retained = false;
3009
+ const remaining = Math.max(0, (this.#sessionReferences.get(sessionId) ?? 1) - 1);
3010
+ if (remaining > 0) {
3011
+ this.#sessionReferences.set(sessionId, remaining);
3012
+ return;
3013
+ }
3014
+ this.#sessionReferences.delete(sessionId);
3015
+ const timer = window.setTimeout(() => {
3016
+ this.#sessionReleaseTimers.delete(sessionId);
3017
+ if ((this.#sessionReferences.get(sessionId) ?? 0) === 0) this.releaseSession(sessionId);
3018
+ }, 0);
3019
+ this.#sessionReleaseTimers.set(sessionId, timer);
3020
+ };
3021
+ }
3022
+ watchJob(sessionId, jobId, initialStatus) {
3023
+ if (initialStatus !== "running") return () => void 0;
3024
+ const key = `${sessionId}\n${jobId}`;
3025
+ const existing = this.#watchedJobs.get(key);
3026
+ if (existing !== void 0) {
3027
+ existing.references += 1;
3028
+ return () => this.#releaseJobWatch(key, existing);
3029
+ }
3030
+ const controller = this.controllerForSession(sessionId);
3031
+ const watched = {
3032
+ references: 1,
3033
+ timer: null,
3034
+ stopped: false,
3035
+ unsubscribeStore: () => void 0
3036
+ };
3037
+ const liveStatus = () => controller.store.getSnapshot().data?.jobs.find((job) => job.jobId === jobId)?.status;
3038
+ const stopWhenTerminal = () => {
3039
+ const status = liveStatus();
3040
+ if (status === void 0 || status === "running") return;
3041
+ watched.stopped = true;
3042
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3043
+ watched.timer = null;
3044
+ };
3045
+ const poll = async () => {
3046
+ watched.timer = null;
3047
+ if (watched.stopped) return;
3048
+ await controller.load();
3049
+ stopWhenTerminal();
3050
+ if (!watched.stopped && watched.references > 0) watched.timer = window.setTimeout(() => {
3051
+ poll();
3052
+ }, 5e3);
3053
+ };
3054
+ watched.unsubscribeStore = controller.store.subscribe(stopWhenTerminal);
3055
+ this.#watchedJobs.set(key, watched);
3056
+ watched.timer = window.setTimeout(() => {
3057
+ poll();
3058
+ }, 0);
3059
+ return () => this.#releaseJobWatch(key, watched);
3060
+ }
3061
+ open(sessionId, inputActions, draft, trigger) {
3062
+ const controller = this.controllerForSession(sessionId);
3063
+ this.#trigger = trigger;
3064
+ this.#syncSplit(this.#isSplitViewport());
3065
+ this.#publish({
3066
+ open: true,
3067
+ sessionId,
3068
+ controller,
3069
+ draft,
3070
+ inputActions
3071
+ });
3072
+ }
3073
+ close(restoreFocus = true) {
3074
+ if (!this.#snapshot.open) return;
3075
+ this.#syncSplit(false);
3076
+ this.#publish({
3077
+ ...this.#snapshot,
3078
+ open: false
3079
+ });
3080
+ if (restoreFocus) queueMicrotask(() => this.#trigger?.focus());
3081
+ }
3082
+ setSplitMode(enabled) {
3083
+ if (!this.#snapshot.open) return;
3084
+ this.#syncSplit(enabled);
3085
+ }
3086
+ updateSession(sessionId, inputActions, draft) {
3087
+ if (this.#snapshot.sessionId !== sessionId) return;
3088
+ this.#publish({
3089
+ ...this.#snapshot,
3090
+ inputActions,
3091
+ draft
3092
+ });
3093
+ }
3094
+ releaseSession(sessionId) {
3095
+ if (this.#snapshot.sessionId !== sessionId) return;
3096
+ this.#syncSplit(false);
3097
+ this.#trigger = null;
3098
+ this.#publish({
3099
+ open: false,
3100
+ sessionId: null,
3101
+ controller: null,
3102
+ draft: "",
3103
+ inputActions: null
3104
+ });
3105
+ }
3106
+ #releaseJobWatch(key, watched) {
3107
+ if (this.#watchedJobs.get(key) !== watched) return;
3108
+ watched.references -= 1;
3109
+ if (watched.references > 0) return;
3110
+ watched.stopped = true;
3111
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3112
+ watched.unsubscribeStore();
3113
+ this.#watchedJobs.delete(key);
3114
+ }
3115
+ appendUrl(url) {
3116
+ const actions = this.#snapshot.inputActions;
3117
+ if (actions === null) return;
3118
+ const current = this.#snapshot.draft;
3119
+ const next = `${current}${current === "" || /\s$/u.test(current) ? "" : "\n"}${url}`;
3120
+ actions.setDraft(next);
3121
+ this.#publish({
3122
+ ...this.#snapshot,
3123
+ draft: next
3124
+ });
3125
+ }
3126
+ #publish(snapshot) {
3127
+ this.#snapshot = snapshot;
3128
+ for (const listener of this.#listeners) listener();
3129
+ }
3130
+ #publishMediaCards() {
3131
+ this.#mediaCardVersion += 1;
3132
+ for (const listener of this.#mediaCardListeners) listener();
3133
+ }
3134
+ #syncSplit(enabled) {
3135
+ if (enabled === this.#splitActive) return;
3136
+ if (enabled) {
3137
+ this.#layout.closeDetails();
3138
+ this.#layout.openDetails();
3139
+ } else this.#layout.closeDetails();
3140
+ this.#splitActive = enabled;
3141
+ }
3142
+ };
3143
+ function compareMediaCardClaims(left, right) {
3144
+ return (left.operation === "generate" ? 0 : 1) - (right.operation === "generate" ? 0 : 1) || left.order - right.order;
3145
+ }
3146
+ function ModellixDesignLauncher({ drawer, inputActions, sessionId, t, useInput }) {
3147
+ const draft = useInput((state) => state.draft);
3148
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3149
+ const active = snapshot.open && snapshot.sessionId === String(sessionId);
3150
+ (0, react.useEffect)(() => {
3151
+ drawer.updateSession(String(sessionId), inputActions, draft);
3152
+ }, [
3153
+ draft,
3154
+ drawer,
3155
+ inputActions,
3156
+ sessionId
3157
+ ]);
3158
+ (0, react.useEffect)(() => drawer.retainSession(String(sessionId)), [drawer, sessionId]);
3159
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3160
+ type: "button",
3161
+ variant: "outline",
3162
+ className: "mdlx-design-launcher",
3163
+ "aria-label": t("designTitle"),
3164
+ "aria-expanded": active,
3165
+ "aria-controls": "mdlx-design-drawer",
3166
+ onClick: (event) => {
3167
+ if (active) drawer.close();
3168
+ else drawer.open(String(sessionId), inputActions, draft, event.currentTarget);
3169
+ },
3170
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3171
+ className: "mdlx-launcher-label",
3172
+ children: t("designTitle")
3173
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3174
+ className: "mdlx-launcher-compact",
3175
+ "aria-hidden": "true",
3176
+ children: "M"
3177
+ })]
3178
+ });
3179
+ }
3180
+ function ModellixDesignDrawer({ drawer, settingsController, t }) {
3181
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3182
+ const [retainedSnapshot, setRetainedSnapshot] = (0, react.useState)(snapshot.open && snapshot.controller !== null ? snapshot : null);
3183
+ const [advancedEditorOpen, setAdvancedEditorOpen] = (0, react.useState)(false);
3184
+ (0, react.useEffect)(() => {
3185
+ if (snapshot.open && snapshot.controller !== null) setRetainedSnapshot(snapshot);
3186
+ }, [
3187
+ snapshot.controller,
3188
+ snapshot.open,
3189
+ snapshot.sessionId
3190
+ ]);
3191
+ (0, react.useEffect)(() => {
3192
+ if (snapshot.open || retainedSnapshot === null) return;
3193
+ const timer = window.setTimeout(() => setRetainedSnapshot(null), MODELLIX_DRAWER_EXIT_MS);
3194
+ return () => window.clearTimeout(timer);
3195
+ }, [retainedSnapshot, snapshot.open]);
3196
+ (0, react.useEffect)(() => {
3197
+ setAdvancedEditorOpen(false);
3198
+ }, [snapshot.sessionId]);
3199
+ (0, react.useEffect)(() => {
3200
+ if (!snapshot.open || typeof window.matchMedia !== "function") return;
3201
+ const media = window.matchMedia(MODELLIX_DESIGN_SPLIT_MEDIA_QUERY);
3202
+ const sync = () => drawer.setSplitMode(media.matches);
3203
+ sync();
3204
+ media.addEventListener("change", sync);
3205
+ return () => media.removeEventListener("change", sync);
3206
+ }, [drawer, snapshot.open]);
3207
+ const presentedSnapshot = snapshot.open && snapshot.controller !== null ? snapshot : retainedSnapshot;
3208
+ if (presentedSnapshot === null || presentedSnapshot.controller === null) return null;
3209
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3210
+ className: "mdlx-design-drawer-viewport",
3211
+ "data-closing": !snapshot.open || void 0,
3212
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3213
+ id: "mdlx-design-drawer",
3214
+ className: "mdlx-design-drawer",
3215
+ "aria-labelledby": "mdlx-design-drawer-title",
3216
+ onKeyDown: (event) => {
3217
+ if (event.key === "Escape") {
3218
+ event.preventDefault();
3219
+ drawer.close();
3220
+ }
3221
+ },
3222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3223
+ className: "mdlx-drawer-header",
3224
+ "data-editor-open": advancedEditorOpen || void 0,
3225
+ children: [
3226
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3227
+ type: "button",
3228
+ variant: "ghost",
3229
+ size: "sm",
3230
+ className: "mdlx-drawer-close",
3231
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 }),
3232
+ "aria-label": `${t("close")} ${t("designTitle")}`,
3233
+ onClick: () => drawer.close()
3234
+ }),
3235
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3236
+ className: "mdlx-heading",
3237
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3238
+ id: "mdlx-design-drawer-title",
3239
+ children: t("designTitle")
3240
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3241
+ className: "mdlx-muted",
3242
+ children: t("designDescription")
3243
+ })]
3244
+ }),
3245
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3246
+ className: "mdlx-drawer-header-reveal",
3247
+ hidden: MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN,
3248
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3249
+ type: "button",
3250
+ variant: "outline",
3251
+ className: "mdlx-drawer-editor-toggle",
3252
+ "aria-controls": "mdlx-design-editor",
3253
+ "aria-expanded": advancedEditorOpen,
3254
+ onClick: (event) => {
3255
+ const next = !advancedEditorOpen;
3256
+ setAdvancedEditorOpen(next);
3257
+ if (!next && event.detail > 0) event.currentTarget.blur();
3258
+ },
3259
+ children: t("advancedEditor")
3260
+ }) })
3261
+ })
3262
+ ]
3263
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3264
+ className: "mdlx-drawer-body",
3265
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModellixDesignView, {
3266
+ advancedEditorOpen,
3267
+ controller: presentedSnapshot.controller,
3268
+ settingsController,
3269
+ onAddUrl: (url) => drawer.appendUrl(url),
3270
+ t
3271
+ })
3272
+ })]
3273
+ })
3274
+ });
3275
+ }
3276
+ //#endregion
3277
+ //#region src/client/MediaToolView.tsx
3278
+ function ModellixMediaToolView({ block, drawer, inputActions, sessionId, t, useInput }) {
3279
+ const draft = useInput((state) => state.draft);
3280
+ const [tab, setTab] = (0, react.useState)("preview");
3281
+ const [activeIndex, setActiveIndex] = (0, react.useState)(0);
3282
+ const [imageOpen, setImageOpen] = (0, react.useState)(false);
3283
+ const tabId = (0, react.useId)();
3284
+ const imageDialogRef = (0, react.useRef)(null);
3285
+ const isResultBlock = "kind" in block && block.kind === "tool-result";
3286
+ const parsedResult = isResultBlock ? parseMediaToolResult(block.meta) : null;
3287
+ const liveState = useResourceState(drawer.controllerForSession(String(sessionId)).store);
3288
+ const trackedJobId = mediaToolJobId(parsedResult);
3289
+ const operation = parsedResult?.operation;
3290
+ const callId = "callId" in block && typeof block.callId === "string" ? block.callId : `${String(sessionId)}:${trackedJobId ?? "pending"}`;
3291
+ (0, react.useSyncExternalStore)(drawer.subscribeMediaCards, drawer.getMediaCardVersion, drawer.getMediaCardVersion);
3292
+ const parsedStatus = mediaToolStatus(parsedResult);
3293
+ const result = resolveLiveMediaToolResult(parsedResult, trackedJobId === void 0 ? void 0 : liveState.data?.jobs.find((job) => job.jobId === trackedJobId), t);
3294
+ (0, react.useEffect)(() => trackedJobId === void 0 ? void 0 : drawer.watchJob(String(sessionId), trackedJobId, parsedStatus), [
3295
+ drawer,
3296
+ parsedStatus,
3297
+ sessionId,
3298
+ trackedJobId
3299
+ ]);
3300
+ (0, react.useEffect)(() => trackedJobId === void 0 || operation === void 0 ? void 0 : drawer.claimMediaCard(String(sessionId), trackedJobId, callId, operation), [
3301
+ callId,
3302
+ drawer,
3303
+ operation,
3304
+ sessionId,
3305
+ trackedJobId
3306
+ ]);
3307
+ const resources = result?.operation === "get_result" ? result.job?.resources ?? [] : result?.resources ?? [];
3308
+ const resourceIndex = resources.length === 0 ? 0 : activeIndex % resources.length;
3309
+ const current = resources[resourceIndex];
3310
+ const imageSurfaceOpen = useExternalDialogGate(imageOpen && current?.kind === "image");
3311
+ const closeImage = (0, react.useCallback)(() => setImageOpen(false), []);
3312
+ useDialogA11y({
3313
+ open: imageSurfaceOpen,
3314
+ container: imageDialogRef,
3315
+ initialFocusSelector: "[data-mdlx-initial-focus]",
3316
+ mandatory: false,
3317
+ onEscape: closeImage
3318
+ });
3319
+ (0, react.useEffect)(() => setImageOpen(false), [current?.url]);
3320
+ if (trackedJobId !== void 0 && operation !== void 0 && !drawer.isMediaCardPrimary(String(sessionId), trackedJobId, callId, operation)) return null;
3321
+ if (!isResultBlock) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3322
+ className: "mdlx-tool-result mdlx-muted",
3323
+ role: "status",
3324
+ children: t("generating")
3325
+ });
3326
+ if (result === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3327
+ className: "mdlx-tool-result mdlx-muted",
3328
+ role: "status",
3329
+ children: t("noPreview")
3330
+ });
3331
+ const status = result.operation === "get_result" ? result.job?.status : result.status;
3332
+ const modelId = result.operation === "get_result" ? result.job?.modelId : result.modelId;
3333
+ const diagnostic = result.operation === "get_result" ? result.job?.diagnostic : result.diagnostic;
3334
+ const succeeded = status === "succeeded";
3335
+ const activeTabId = `${tabId}-${tab}-tab`;
3336
+ const activePanelId = `${tabId}-${tab}-panel`;
3337
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
3338
+ className: "mdlx-tool-result",
3339
+ children: [
3340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3341
+ className: "mdlx-result-head",
3342
+ 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", {
3343
+ className: "mdlx-muted",
3344
+ children: modelId
3345
+ })]
3346
+ }),
3347
+ succeeded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3348
+ className: "mdlx-result-tabs",
3349
+ role: "tablist",
3350
+ "aria-label": t("resultViewLabel"),
3351
+ onKeyDown: handleResultTabKeyDown,
3352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3353
+ id: `${tabId}-preview-tab`,
3354
+ type: "button",
3355
+ role: "tab",
3356
+ "aria-controls": `${tabId}-preview-panel`,
3357
+ "aria-selected": tab === "preview",
3358
+ tabIndex: tab === "preview" ? 0 : -1,
3359
+ className: "mdlx-result-tab",
3360
+ onClick: () => setTab("preview"),
3361
+ children: t("previewTab")
3362
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3363
+ id: `${tabId}-json-tab`,
3364
+ type: "button",
3365
+ role: "tab",
3366
+ "aria-controls": `${tabId}-json-panel`,
3367
+ "aria-selected": tab === "json",
3368
+ tabIndex: tab === "json" ? 0 : -1,
3369
+ className: "mdlx-result-tab",
3370
+ onClick: () => setTab("json"),
3371
+ children: t("jsonTab")
3372
+ })]
3373
+ }), tab === "json" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3374
+ id: activePanelId,
3375
+ role: "tabpanel",
3376
+ "aria-labelledby": activeTabId,
3377
+ className: "mdlx-result-json",
3378
+ children: JSON.stringify(result, null, 2)
3379
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3380
+ id: activePanelId,
3381
+ role: "tabpanel",
3382
+ "aria-labelledby": activeTabId,
3383
+ className: "mdlx-result-panel",
3384
+ children: current === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3385
+ className: "mdlx-empty mdlx-empty-compact",
3386
+ children: t("noPreview")
3387
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3388
+ current.kind === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3389
+ type: "button",
3390
+ className: "mdlx-image-preview-button",
3391
+ "aria-label": t("openImage"),
3392
+ onClick: () => setImageOpen(true),
3393
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3394
+ resource: toWireResource(current, resourceIndex),
3395
+ t
3396
+ })
3397
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3398
+ resource: toWireResource(current, resourceIndex),
3399
+ t
3400
+ }),
3401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3402
+ className: "mdlx-actions mdlx-actions-start",
3403
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3404
+ type: "button",
3405
+ variant: "outline",
3406
+ onClick: () => {
3407
+ const separator = draft === "" || /\s$/u.test(draft) ? "" : "\n";
3408
+ inputActions.setDraft(`${draft}${separator}${current.url}`);
3409
+ },
3410
+ children: t("addUrlToChat")
3411
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3412
+ className: "mdlx-safe-link",
3413
+ href: current.url,
3414
+ target: "_blank",
3415
+ rel: "noopener noreferrer",
3416
+ referrerPolicy: "no-referrer",
3417
+ download: true,
3418
+ children: t("download")
3419
+ })]
3420
+ }),
3421
+ resources.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3422
+ className: "mdlx-resource-nav",
3423
+ "aria-label": t("resourceNavigation"),
3424
+ children: [
3425
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3426
+ type: "button",
3427
+ variant: "outline",
3428
+ onClick: () => setActiveIndex((resourceIndex - 1 + resources.length) % resources.length),
3429
+ children: t("previousResource")
3430
+ }),
3431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3432
+ className: "mdlx-muted",
3433
+ children: t("resourcePosition", {
3434
+ current: resourceIndex + 1,
3435
+ total: resources.length
3436
+ })
3437
+ }),
3438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3439
+ type: "button",
3440
+ variant: "outline",
3441
+ onClick: () => setActiveIndex((resourceIndex + 1) % resources.length),
3442
+ children: t("nextResource")
3443
+ })
3444
+ ]
3445
+ })
3446
+ ] })
3447
+ })] }),
3448
+ diagnostic !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3449
+ className: "mdlx-error",
3450
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
3451
+ className: "mdlx-code",
3452
+ children: diagnostic.code
3453
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: diagnostic.message })]
3454
+ })
3455
+ ]
3456
+ }), succeeded && current?.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
3457
+ open: imageSurfaceOpen,
3458
+ title: t("imageViewerTitle"),
3459
+ onClose: closeImage,
3460
+ headless: true,
3461
+ className: "mdlx-modal mdlx-image-modal",
3462
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3463
+ ref: imageDialogRef,
3464
+ className: "mdlx-modal-content",
3465
+ "data-mdlx-dialog-surface": "",
3466
+ tabIndex: -1,
3467
+ children: [
3468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3469
+ className: "mdlx-heading",
3470
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3471
+ className: "mdlx-modal-title",
3472
+ children: t("imageViewerTitle")
3473
+ })
3474
+ }),
3475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3476
+ className: "mdlx-image-full",
3477
+ src: current.url,
3478
+ alt: t("generatedPreview"),
3479
+ referrerPolicy: "no-referrer"
3480
+ }),
3481
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3482
+ className: "mdlx-actions",
3483
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3484
+ type: "button",
3485
+ variant: "outline",
3486
+ "data-mdlx-initial-focus": "",
3487
+ onClick: closeImage,
3488
+ children: t("close")
3489
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3490
+ className: "mdlx-safe-link",
3491
+ href: current.url,
3492
+ target: "_blank",
3493
+ rel: "noopener noreferrer",
3494
+ referrerPolicy: "no-referrer",
3495
+ download: true,
3496
+ children: t("download")
3497
+ })]
3498
+ })
3499
+ ]
3500
+ })
3501
+ })] });
3502
+ }
3503
+ function mediaToolJobId(result) {
3504
+ return result?.operation === "get_result" ? result.job?.jobId : result?.jobId;
3505
+ }
3506
+ function mediaToolStatus(result) {
3507
+ return result?.operation === "get_result" ? result.job?.status : result?.status;
3508
+ }
3509
+ function resolveLiveMediaToolResult(result, liveJob, t) {
3510
+ if (result === null || liveJob === void 0) return result;
3511
+ const resources = liveJob.resources.map((resource) => ({
3512
+ kind: resource.kind,
3513
+ url: resource.url,
3514
+ ...resource.expiresAt === null ? {} : { expiresAt: resource.expiresAt }
3515
+ }));
3516
+ const diagnostic = liveJob.diagnostic === null ? void 0 : {
3517
+ code: liveJob.diagnostic.code,
3518
+ message: t(designDiagnosticMessageKey(liveJob.diagnostic.code))
3519
+ };
3520
+ if (result.operation === "generate") {
3521
+ const { diagnostic: _staleDiagnostic, ...withoutDiagnostic } = result;
3522
+ return {
3523
+ ...withoutDiagnostic,
3524
+ status: liveJob.status,
3525
+ modelId: liveJob.modelId,
3526
+ resources,
3527
+ ...diagnostic === void 0 ? {} : { diagnostic }
3528
+ };
3529
+ }
3530
+ return {
3531
+ ...result,
3532
+ found: true,
3533
+ job: {
3534
+ jobId: liveJob.jobId,
3535
+ modelId: liveJob.modelId,
3536
+ status: liveJob.status,
3537
+ resources,
3538
+ ...diagnostic === void 0 ? {} : { diagnostic }
3539
+ }
3540
+ };
3541
+ }
3542
+ function parseMediaToolResult(value) {
3543
+ const root = record(value);
3544
+ if (root === null || root.service !== "media" || root.operation !== "generate" && root.operation !== "get_result") return null;
3545
+ if (root.operation === "generate") {
3546
+ const status = safeText(root.status, 64);
3547
+ const modelId = safeText(root.modelId, 256);
3548
+ const jobId = safeText(root.jobId, 256);
3549
+ const diagnostic = parseDiagnostic(root.diagnostic);
3550
+ return {
3551
+ operation: "generate",
3552
+ ...status === void 0 ? {} : { status },
3553
+ ...modelId === void 0 ? {} : { modelId },
3554
+ ...jobId === void 0 ? {} : { jobId },
3555
+ resources: parseResources(root.resources),
3556
+ ...diagnostic === void 0 ? {} : { diagnostic }
3557
+ };
3558
+ }
3559
+ const job = record(root.job);
3560
+ const diagnostic = job === null ? void 0 : parseDiagnostic(job.diagnostic);
3561
+ return {
3562
+ operation: "get_result",
3563
+ found: root.found === true,
3564
+ ...job === null ? {} : { job: {
3565
+ jobId: safeText(job.jobId, 256) ?? "",
3566
+ modelId: safeText(job.modelId, 256) ?? "",
3567
+ status: safeText(job.status, 64) ?? "",
3568
+ resources: parseResources(job.resources),
3569
+ ...diagnostic === void 0 ? {} : { diagnostic }
3570
+ } }
3571
+ };
3572
+ }
3573
+ function parseResources(value) {
3574
+ if (!Array.isArray(value)) return [];
3575
+ const resources = [];
3576
+ for (const candidate of value.slice(0, 32)) {
3577
+ const resource = record(candidate);
3578
+ if (resource === null || resource.kind !== "image" && resource.kind !== "video" && resource.kind !== "audio" || typeof resource.url !== "string") continue;
3579
+ let url;
3580
+ try {
3581
+ url = safeResourceHref(resource.url);
3582
+ } catch {
3583
+ continue;
3584
+ }
3585
+ const expiresAt = safeText(resource.expiresAt, 128);
3586
+ resources.push({
3587
+ kind: resource.kind,
3588
+ url,
3589
+ ...expiresAt === void 0 ? {} : { expiresAt }
3590
+ });
3591
+ }
3592
+ return resources;
3593
+ }
3594
+ function parseDiagnostic(value) {
3595
+ const diagnostic = record(value);
3596
+ if (diagnostic === null) return void 0;
3597
+ const code = safeText(diagnostic.code, 128);
3598
+ const message = safeText(diagnostic.message, 1024);
3599
+ return code === void 0 || message === void 0 ? void 0 : {
3600
+ code,
3601
+ message
3602
+ };
3603
+ }
3604
+ function safeText(value, maximum) {
3605
+ return typeof value === "string" && value.length > 0 && value.length <= maximum ? value : void 0;
3606
+ }
3607
+ function record(value) {
3608
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
3609
+ }
3610
+ function mediaStatusLabel(status, t) {
3611
+ switch (status) {
3612
+ case "running": return t("running");
3613
+ case "succeeded": return t("succeeded");
3614
+ case "failed": return t("failed");
3615
+ case "canceled": return t("canceled");
3616
+ case "expired": return t("expired");
3617
+ case "submit-unknown": return t("unknown");
3618
+ default: return t("unknown");
3619
+ }
3620
+ }
3621
+ function toWireResource(resource, index) {
3622
+ return {
3623
+ id: `tool-resource-${String(index)}`,
3624
+ kind: resource.kind,
3625
+ url: resource.url,
3626
+ downloadUrl: resource.url,
3627
+ expiresAt: resource.expiresAt ?? null
3628
+ };
3629
+ }
3630
+ //#endregion
2865
3631
  //#region src/client/locales.ts
2866
3632
  const MODELLIX_LOCALE_NAMESPACE = "modellix";
2867
3633
  const zh = {
2868
3634
  dateLocale: "zh-CN",
2869
3635
  nav: "Modellix",
2870
- designTab: "Design",
2871
3636
  onboardingTitle: "连接 Modellix",
2872
3637
  onboardingDescription: "输入一个 Modellix API Key,即可启用 Design、LLM 模型和 Web Tools。",
2873
3638
  keyLabel: "Modellix API Key",
@@ -2884,7 +3649,7 @@ const zh = {
2884
3649
  serviceLlm: "LLM 模型",
2885
3650
  serviceLlmDescription: "在 Harness 中快速切换 Modellix 支持的模型。",
2886
3651
  serviceWeb: "Web Tools",
2887
- serviceWebDescription: "为原生 web_search 与 web_fetch 提供 Modellix Provider。",
3652
+ serviceWebDescription: "提供可自动调用的 Modellix 网页搜索与读取工具。",
2888
3653
  settingsTitle: "Modellix",
2889
3654
  settingsDescription: "一个 API Key,统一使用 Design、LLM 与 Web Tools。",
2890
3655
  credentialTitle: "API Key",
@@ -2927,13 +3692,13 @@ const zh = {
2927
3692
  errorGeneric: "暂时无法完成操作,请重试。",
2928
3693
  errorConflict: "配置已在其他位置更新,请刷新后重试。",
2929
3694
  errorKeyInvalid: "这个 API Key 无效,请检查后重新输入。",
2930
- errorBilling: "账户当前无法计费,请检查 Modellix 余额或账单状态。",
3695
+ errorBilling: "当前请求无法完成,请在 Modellix 控制台查看账户状态。",
2931
3696
  errorRateLimited: "请求过于频繁,请稍后手动重试。",
2932
3697
  errorOffline: "当前无法连接 Modellix,请检查网络后重试。",
2933
3698
  errorTimeout: "Modellix 响应超时,请稍后手动重试。",
2934
3699
  errorServer: "Modellix 服务暂时不可用,请稍后手动重试。",
2935
3700
  errorPolicy: "当前账户或环境策略不允许此操作。",
2936
- errorSubmitUnknown: "提交结果未知。请先查看结果列表,避免重复计费。",
3701
+ errorSubmitUnknown: "提交结果未知。请先查看结果列表,不要重复提交。",
2937
3702
  errorAssetExpired: "结果资源已过期,无法继续访问。",
2938
3703
  errorDesignSchema: "模型参数定义已变化或无法解析,请刷新模型后重试。",
2939
3704
  designTitle: "Modellix Design",
@@ -2953,11 +3718,11 @@ const zh = {
2953
3718
  promptPlaceholder: "描述画面、视频动作或要合成的声音…",
2954
3719
  parametersTitle: "参数",
2955
3720
  advancedParameters: "高级参数({count})",
3721
+ advancedEditor: "高级参数编辑器",
2956
3722
  required: "必填",
2957
3723
  optional: "可选",
2958
3724
  requiredMissing: "请填写所有必填参数后再生成。",
2959
3725
  assistantTitle: "用对话调整参数",
2960
- assistantPaidNotice: "发送后会使用同一个 Modellix Key 调用固定 LLM 生成参数提议,可能产生费用;不会自动生成媒体。",
2961
3726
  assistantPlaceholder: "例如:改成 16:9,生成 8 秒视频,风格更电影化",
2962
3727
  propose: "生成参数提议",
2963
3728
  proposing: "正在分析…",
@@ -2969,7 +3734,6 @@ const zh = {
2969
3734
  conflicts: "需要先解决冲突",
2970
3735
  generate: "确认并生成",
2971
3736
  generating: "正在提交…",
2972
- paidNotice: "生成是计费操作;每次点击只提交一次,不会自动重试。",
2973
3737
  designDisabled: "Design 已关闭,可在 Modellix 设置中重新开启。",
2974
3738
  keyRequired: "请先在 Modellix 设置中配置 API Key。",
2975
3739
  noModels: "当前没有可用模型,请稍后刷新。",
@@ -2983,9 +3747,17 @@ const zh = {
2983
3747
  designNoticeCatalogUnavailable: "Modellix Design 模型目录暂时不可用,请稍后刷新。",
2984
3748
  designNoticeCredentialReloaded: "API Key 已更改,Design 模型和参数已重新加载。",
2985
3749
  resultsTitle: "结果",
2986
- runningTitle: "进行中",
2987
- succeededTitle: "已完成",
2988
- diagnosticsTitle: "诊断",
3750
+ resultsCount: "{count} 条",
3751
+ resultViewLabel: "结果视图",
3752
+ previewTab: "预览",
3753
+ jsonTab: "JSON",
3754
+ noPreview: "当前还没有可预览的媒体结果。",
3755
+ resultNotFound: "未找到对应的生成任务。",
3756
+ resourceNavigation: "媒体结果导航",
3757
+ previousResource: "上一项",
3758
+ nextResource: "下一项",
3759
+ resourcePosition: "第 {current} / {total} 项",
3760
+ addUrlToChat: "添加 URL 到对话框",
2989
3761
  noResults: "还没有生成记录。确认参数并生成后,结果会显示在这里。",
2990
3762
  running: "生成中",
2991
3763
  succeeded: "已完成",
@@ -2993,10 +3765,10 @@ const zh = {
2993
3765
  canceled: "已取消",
2994
3766
  unknown: "提交结果未知",
2995
3767
  expired: "结果已过期",
2996
- openImage: "打开原图",
3768
+ openImage: "放大图片",
2997
3769
  imageViewerTitle: "生成图片",
2998
3770
  close: "关闭",
2999
- download: "下载结果",
3771
+ download: "下载",
3000
3772
  expiresAt: "有效期至 {time}",
3001
3773
  invalidJson: "请输入有效的 JSON。",
3002
3774
  invalidParameter: "此参数不符合当前模型的约束。",
@@ -3007,9 +3779,9 @@ const zh = {
3007
3779
  generatedAudioPreview: "生成音频结果预览",
3008
3780
  jobModel: "模型:{model}",
3009
3781
  jobCreated: "创建于 {time}",
3010
- diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复计费生成提交。",
3782
+ diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复生成提交。",
3011
3783
  diagnosticCredentialChanged: "此任务属于更早的 API Key,无法继续刷新。",
3012
- diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,避免重复计费。",
3784
+ diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,不要重复提交。",
3013
3785
  diagnosticGenerationFailed: "生成任务未完成。",
3014
3786
  diagnosticResultUnavailable: "生成已完成,但没有可用的结果资源。",
3015
3787
  diagnosticCredentialRejected: "刷新任务时 Modellix 拒绝了当前 API Key。",
@@ -3028,7 +3800,6 @@ const zh = {
3028
3800
  const en = {
3029
3801
  dateLocale: "en-US",
3030
3802
  nav: "Modellix",
3031
- designTab: "Design",
3032
3803
  onboardingTitle: "Connect Modellix",
3033
3804
  onboardingDescription: "Enter one Modellix API Key to enable Design, LLM models, and Web Tools.",
3034
3805
  keyLabel: "Modellix API Key",
@@ -3045,7 +3816,7 @@ const en = {
3045
3816
  serviceLlm: "LLM models",
3046
3817
  serviceLlmDescription: "Quickly switch between Modellix models in Harness.",
3047
3818
  serviceWeb: "Web Tools",
3048
- serviceWebDescription: "Provide Modellix for native web_search and web_fetch.",
3819
+ serviceWebDescription: "Provide automatic Modellix Web Search and Web Fetch tools.",
3049
3820
  settingsTitle: "Modellix",
3050
3821
  settingsDescription: "One API Key for Design, LLM, and Web Tools.",
3051
3822
  credentialTitle: "API Key",
@@ -3088,13 +3859,13 @@ const en = {
3088
3859
  errorGeneric: "The operation could not be completed. Try again.",
3089
3860
  errorConflict: "Settings changed elsewhere. Refresh and try again.",
3090
3861
  errorKeyInvalid: "This API Key is invalid. Check it and enter it again.",
3091
- errorBilling: "Billing is unavailable. Check the Modellix balance or billing status.",
3862
+ errorBilling: "This request cannot be completed. Check the account status in the Modellix console.",
3092
3863
  errorRateLimited: "Too many requests. Wait before retrying manually.",
3093
3864
  errorOffline: "Modellix cannot be reached. Check the network and try again.",
3094
3865
  errorTimeout: "Modellix timed out. Retry manually later.",
3095
3866
  errorServer: "Modellix is temporarily unavailable. Retry manually later.",
3096
3867
  errorPolicy: "The current account or environment policy does not allow this operation.",
3097
- errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting another billed request.",
3868
+ errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting again.",
3098
3869
  errorAssetExpired: "This result resource has expired and is no longer available.",
3099
3870
  errorDesignSchema: "The model parameter definition changed or could not be parsed. Refresh the model and try again.",
3100
3871
  designTitle: "Modellix Design",
@@ -3114,11 +3885,11 @@ const en = {
3114
3885
  promptPlaceholder: "Describe the image, video motion, or sound…",
3115
3886
  parametersTitle: "Parameters",
3116
3887
  advancedParameters: "Advanced parameters ({count})",
3888
+ advancedEditor: "Advanced parameter editor",
3117
3889
  required: "Required",
3118
3890
  optional: "Optional",
3119
3891
  requiredMissing: "Complete every required parameter before generating.",
3120
3892
  assistantTitle: "Adjust parameters by chat",
3121
- 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.",
3122
3893
  assistantPlaceholder: "For example: make it 16:9, eight seconds, and more cinematic",
3123
3894
  propose: "Propose parameter changes",
3124
3895
  proposing: "Analyzing…",
@@ -3130,7 +3901,6 @@ const en = {
3130
3901
  conflicts: "Resolve conflicts before applying",
3131
3902
  generate: "Confirm and generate",
3132
3903
  generating: "Submitting…",
3133
- paidNotice: "Generation is billed. Each click submits once and is never retried automatically.",
3134
3904
  designDisabled: "Design is disabled. Enable it in Modellix settings.",
3135
3905
  keyRequired: "Configure a Modellix API Key in settings first.",
3136
3906
  noModels: "No model is currently available. Refresh and try again.",
@@ -3144,9 +3914,17 @@ const en = {
3144
3914
  designNoticeCatalogUnavailable: "The Modellix Design model catalog is temporarily unavailable. Refresh it later.",
3145
3915
  designNoticeCredentialReloaded: "The API Key changed, so the Design model and parameters were reloaded.",
3146
3916
  resultsTitle: "Results",
3147
- runningTitle: "Running",
3148
- succeededTitle: "Succeeded",
3149
- diagnosticsTitle: "Diagnostics",
3917
+ resultsCount: "{count} item(s)",
3918
+ resultViewLabel: "Result view",
3919
+ previewTab: "Preview",
3920
+ jsonTab: "JSON",
3921
+ noPreview: "No media preview is available yet.",
3922
+ resultNotFound: "The requested generation task was not found.",
3923
+ resourceNavigation: "Media result navigation",
3924
+ previousResource: "Previous",
3925
+ nextResource: "Next",
3926
+ resourcePosition: "{current} of {total}",
3927
+ addUrlToChat: "Add URL to chat",
3150
3928
  noResults: "No generations yet. Confirm parameters and generate to see results here.",
3151
3929
  running: "Running",
3152
3930
  succeeded: "Succeeded",
@@ -3154,10 +3932,10 @@ const en = {
3154
3932
  canceled: "Canceled",
3155
3933
  unknown: "Submission outcome unknown",
3156
3934
  expired: "Result expired",
3157
- openImage: "Open full image",
3935
+ openImage: "Enlarge image",
3158
3936
  imageViewerTitle: "Generated image",
3159
3937
  close: "Close",
3160
- download: "Download result",
3938
+ download: "Download",
3161
3939
  expiresAt: "Available until {time}",
3162
3940
  invalidJson: "Enter valid JSON.",
3163
3941
  invalidParameter: "This value does not satisfy the current model constraints.",
@@ -3168,9 +3946,9 @@ const en = {
3168
3946
  generatedAudioPreview: "Generated audio result preview",
3169
3947
  jobModel: "Model: {model}",
3170
3948
  jobCreated: "Created {time}",
3171
- diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a billed generation submission automatically.",
3949
+ diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a generation submission automatically.",
3172
3950
  diagnosticCredentialChanged: "This task belongs to an earlier API Key and cannot be refreshed.",
3173
- diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting another billed request.",
3951
+ diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting again.",
3174
3952
  diagnosticGenerationFailed: "The generation did not complete.",
3175
3953
  diagnosticResultUnavailable: "The generation completed without a usable result resource.",
3176
3954
  diagnosticCredentialRejected: "Modellix rejected the current API Key while refreshing this task.",
@@ -3461,7 +4239,6 @@ function RemoveCredentialDialog({ open, busy, errorCode, onClose, onConfirm, t,
3461
4239
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
3462
4240
  open: surfaceOpen,
3463
4241
  title: t("removeTitle"),
3464
- closeLabel: t("cancel"),
3465
4242
  onClose,
3466
4243
  headless: true,
3467
4244
  className: "mdlx-modal mdlx-modal-confirm",
@@ -3527,7 +4304,7 @@ function llmHealthText(health, t) {
3527
4304
  //#endregion
3528
4305
  //#region src/client/styles.ts
3529
4306
  const MODELLIX_CLIENT_CSS = String.raw`
3530
- .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}
4307
+ .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}
3531
4308
  .mdlx-settings{width:min(100%,760px);min-width:0;display:grid;gap:12px;padding:4px}
3532
4309
  .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}
3533
4310
  .mdlx-heading h2{font-size:20px;line-height:28px;font-weight:500}.mdlx-heading h3{font-size:15px;line-height:22px;font-weight:600}
@@ -3536,7 +4313,7 @@ const MODELLIX_CLIENT_CSS = String.raw`
3536
4313
  .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}
3537
4314
  .mdlx-card-head>.mdlx-heading{margin-bottom:0}
3538
4315
  .mdlx-status-copy{display:flex;align-items:center;gap:8px;min-width:0}.mdlx-status-copy span{overflow-wrap:anywhere}
3539
- .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}
4316
+ .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}
3540
4317
  .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)}
3541
4318
  .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}
3542
4319
  .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}
@@ -3553,25 +4330,28 @@ const MODELLIX_CLIENT_CSS = String.raw`
3553
4330
  .mdlx-link-button{appearance:none;border:0;padding:0;background:transparent;font:inherit;cursor:pointer}
3554
4331
  .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}
3555
4332
  .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)}
3556
- .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}
4333
+ .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}
4334
+ .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}
3557
4335
  .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}
3558
4336
  .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}
3559
4337
  .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)}
3560
4338
  .mdlx-generate-block{display:grid;gap:8px;min-width:0}.mdlx-generate-block>p{margin:0}
3561
4339
  .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}
3562
4340
  .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}
3563
- .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}
3564
- .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)}
3565
- .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}
4341
+ .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}
4342
+ .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}
4343
+ .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}
3566
4344
  .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)}
3567
4345
  .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}
3568
- .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}
4346
+ .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}
4347
+ @media (min-width:1280px){.mdlx-design-drawer{box-shadow:none}}
3569
4348
  @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)}}
3570
4349
  @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)}}
3571
- @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)}}
3572
- @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}}
3573
- @media (prefers-reduced-motion:reduce){.mdlx-settings *,.mdlx-design *,.mdlx-modal-content *{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
3574
- @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}}
4350
+ @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}}
4351
+ @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}}
4352
+ @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}}
4353
+ @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}}
4354
+ @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}}
3575
4355
  `;
3576
4356
  function installModellixClientStyles(target = typeof document === "undefined" ? void 0 : document) {
3577
4357
  if (target === void 0) return () => void 0;
@@ -3589,6 +4369,11 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3589
4369
  id: "modellix.credential-recovery",
3590
4370
  order: 10
3591
4371
  },
4372
+ {
4373
+ name: "shell.overlay",
4374
+ id: "modellix.design-drawer",
4375
+ order: 20
4376
+ },
3592
4377
  {
3593
4378
  name: "settings.onboarding",
3594
4379
  id: "modellix.onboarding",
@@ -3600,9 +4385,19 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3600
4385
  order: 30
3601
4386
  },
3602
4387
  {
3603
- name: "conversation.view",
3604
- id: "modellix.design",
4388
+ name: "conversation.session.header.utilities",
4389
+ id: "modellix.design-launcher",
3605
4390
  order: 20
4391
+ },
4392
+ {
4393
+ name: "tool.call.toolview",
4394
+ key: "modellix_media_generate",
4395
+ priority: 20
4396
+ },
4397
+ {
4398
+ name: "tool.call.toolview",
4399
+ key: "modellix_media_get_result",
4400
+ priority: 20
3606
4401
  }
3607
4402
  ]);
3608
4403
  //#endregion
@@ -3610,7 +4405,8 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3610
4405
  const inject = [
3611
4406
  "slots",
3612
4407
  "locale",
3613
- "connection"
4408
+ "connection",
4409
+ "layout"
3614
4410
  ];
3615
4411
  function apply(ctx) {
3616
4412
  ctx.effect(() => ctx.locale.register(MODELLIX_LOCALE_NAMESPACE, {
@@ -3621,6 +4417,7 @@ function apply(ctx) {
3621
4417
  const t = ctx.locale.bind(MODELLIX_LOCALE_NAMESPACE);
3622
4418
  const rpc = new ModellixRpcClient(ctx.connection.rpc);
3623
4419
  const settingsController = new SettingsController(rpc);
4420
+ const designDrawer = new DesignDrawerController(rpc, ctx.layout);
3624
4421
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
3625
4422
  name: "shell.overlay",
3626
4423
  id: "modellix.credential-recovery",
@@ -3628,6 +4425,16 @@ function apply(ctx) {
3628
4425
  locale: MODELLIX_LOCALE_NAMESPACE,
3629
4426
  inject: () => ({ controller: settingsController })
3630
4427
  }, CredentialRecoveryOverlay));
4428
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
4429
+ name: "shell.overlay",
4430
+ id: "modellix.design-drawer",
4431
+ order: 20,
4432
+ locale: MODELLIX_LOCALE_NAMESPACE,
4433
+ inject: () => ({
4434
+ drawer: designDrawer,
4435
+ settingsController
4436
+ })
4437
+ }, ModellixDesignDrawer));
3631
4438
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3632
4439
  name: "settings.onboarding",
3633
4440
  id: "modellix.onboarding",
@@ -3643,17 +4450,27 @@ function apply(ctx) {
3643
4450
  locale: MODELLIX_LOCALE_NAMESPACE,
3644
4451
  inject: () => ({ controller: settingsController })
3645
4452
  }, ModellixSettingsSection));
3646
- ctx.slots.inject("conversation.view", () => ctx.slots.register({
3647
- name: "conversation.view",
3648
- id: "modellix.design",
4453
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
4454
+ name: "conversation.session.header.utilities",
4455
+ id: "modellix.design-launcher",
3649
4456
  order: 20,
3650
- label: () => t("designTab"),
3651
4457
  locale: MODELLIX_LOCALE_NAMESPACE,
3652
- inject: (sessionId) => ({
3653
- controller: new DesignController(rpc, sessionId),
3654
- settingsController
3655
- })
3656
- }, ModellixDesignView));
4458
+ inject: () => ({ drawer: designDrawer })
4459
+ }, ModellixDesignLauncher));
4460
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4461
+ name: "tool.call.toolview",
4462
+ key: "modellix_media_generate",
4463
+ priority: 20,
4464
+ locale: MODELLIX_LOCALE_NAMESPACE,
4465
+ inject: () => ({ drawer: designDrawer })
4466
+ }, ModellixMediaToolView));
4467
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4468
+ name: "tool.call.toolview",
4469
+ key: "modellix_media_get_result",
4470
+ priority: 20,
4471
+ locale: MODELLIX_LOCALE_NAMESPACE,
4472
+ inject: () => ({ drawer: designDrawer })
4473
+ }, ModellixMediaToolView));
3657
4474
  }
3658
4475
  //#endregion
3659
4476
  exports.MODELLIX_CLIENT_SLOTS = MODELLIX_CLIENT_SLOTS;