dsh-modellix 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +101 -166
  3. package/README.zh-CN.md +99 -164
  4. package/docs/assets/chat-media-generation-en.webp +0 -0
  5. package/docs/assets/chat-media-generation-zh.webp +0 -0
  6. package/docs/assets/design-results-drawer-en.webp +0 -0
  7. package/docs/assets/design-results-drawer-zh.webp +0 -0
  8. package/docs/assets/llm-model-selector-en.webp +0 -0
  9. package/docs/assets/llm-model-selector-zh.webp +0 -0
  10. package/docs/assets/media-players-en.webp +0 -0
  11. package/docs/assets/media-players-zh.webp +0 -0
  12. package/docs/assets/settings-ready-en.webp +0 -0
  13. package/docs/assets/settings-ready-zh.webp +0 -0
  14. package/docs/assets/web-tools-auto-en.webp +0 -0
  15. package/docs/assets/web-tools-auto-zh.webp +0 -0
  16. package/docs/en-US/LOCAL_USAGE.md +17 -15
  17. package/docs/en-US/RELEASE_CHECKLIST.md +270 -0
  18. package/docs/en-US/USER_GUIDE.md +183 -395
  19. package/docs/zh-CN/LOCAL_USAGE.md +17 -15
  20. package/docs/zh-CN/RELEASE_CHECKLIST.md +270 -0
  21. package/docs/zh-CN/USER_GUIDE.md +182 -394
  22. package/lib/client.d.ts +16 -2
  23. package/lib/client.js +1272 -452
  24. package/lib/client.js.map +1 -1
  25. package/lib/index.d.ts +159 -109
  26. package/lib/index.js +995 -192
  27. package/lib/index.js.map +1 -1
  28. package/package.json +7 -2
  29. package/docs/assets/design-desktop-en.webp +0 -0
  30. package/docs/assets/design-desktop-zh.webp +0 -0
  31. package/docs/assets/design-proposal-en.webp +0 -0
  32. package/docs/assets/design-proposal-zh.webp +0 -0
  33. package/docs/assets/design-results-media-en.webp +0 -0
  34. package/docs/assets/design-results-media-zh.webp +0 -0
  35. package/docs/assets/web-tools-en.webp +0 -0
  36. 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", {
@@ -2810,18 +2866,14 @@ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t })
2810
2866
  "data-mdlx-initial-focus": "",
2811
2867
  onClick: closeImage,
2812
2868
  children: t("close")
2813
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2869
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2814
2870
  className: "mdlx-safe-link",
2815
2871
  href: resource.downloadUrl,
2816
2872
  target: "_blank",
2817
2873
  rel: "noopener noreferrer",
2818
2874
  referrerPolicy: "no-referrer",
2819
2875
  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
- ]
2876
+ children: t("download")
2825
2877
  })]
2826
2878
  })
2827
2879
  ]
@@ -2862,12 +2914,727 @@ function formatTime(value, locale) {
2862
2914
  }).format(new Date(value));
2863
2915
  }
2864
2916
  //#endregion
2917
+ //#region src/client/DesignDrawer.tsx
2918
+ const MODELLIX_DESIGN_SPLIT_MEDIA_QUERY = "(min-width: 1280px)";
2919
+ const MODELLIX_DRAWER_EXIT_MS = 180;
2920
+ const MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN = true;
2921
+ function isSplitViewport() {
2922
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(min-width: 1280px)").matches;
2923
+ }
2924
+ var DesignDrawerController = class {
2925
+ #rpc;
2926
+ #layout;
2927
+ #isSplitViewport;
2928
+ #listeners = /* @__PURE__ */ new Set();
2929
+ #controllers = /* @__PURE__ */ new Map();
2930
+ #watchedJobs = /* @__PURE__ */ new Map();
2931
+ #mediaCardClaims = /* @__PURE__ */ new Map();
2932
+ #mediaCardListeners = /* @__PURE__ */ new Set();
2933
+ #sessionReferences = /* @__PURE__ */ new Map();
2934
+ #sessionReleaseTimers = /* @__PURE__ */ new Map();
2935
+ #mediaCardVersion = 0;
2936
+ #mediaCardOrder = 0;
2937
+ #snapshot = {
2938
+ open: false,
2939
+ sessionId: null,
2940
+ controller: null,
2941
+ draft: "",
2942
+ inputActions: null
2943
+ };
2944
+ #trigger = null;
2945
+ #splitActive = false;
2946
+ constructor(rpc, layout, splitViewport = isSplitViewport) {
2947
+ this.#rpc = rpc;
2948
+ this.#layout = layout;
2949
+ this.#isSplitViewport = splitViewport;
2950
+ }
2951
+ subscribe = (listener) => {
2952
+ this.#listeners.add(listener);
2953
+ return () => this.#listeners.delete(listener);
2954
+ };
2955
+ getSnapshot = () => this.#snapshot;
2956
+ subscribeMediaCards = (listener) => {
2957
+ this.#mediaCardListeners.add(listener);
2958
+ return () => this.#mediaCardListeners.delete(listener);
2959
+ };
2960
+ getMediaCardVersion = () => this.#mediaCardVersion;
2961
+ claimMediaCard(sessionId, jobId, callId, operation) {
2962
+ const key = `${sessionId}\n${jobId}`;
2963
+ const claims = this.#mediaCardClaims.get(key) ?? /* @__PURE__ */ new Map();
2964
+ if (claims.has(callId)) return () => void 0;
2965
+ claims.set(callId, {
2966
+ operation,
2967
+ order: this.#mediaCardOrder++
2968
+ });
2969
+ this.#mediaCardClaims.set(key, claims);
2970
+ this.#publishMediaCards();
2971
+ return () => {
2972
+ const current = this.#mediaCardClaims.get(key);
2973
+ if (current?.delete(callId) !== true) return;
2974
+ if (current.size === 0) this.#mediaCardClaims.delete(key);
2975
+ this.#publishMediaCards();
2976
+ };
2977
+ }
2978
+ isMediaCardPrimary(sessionId, jobId, callId, operation) {
2979
+ const claims = this.#mediaCardClaims.get(`${sessionId}\n${jobId}`);
2980
+ if (claims === void 0 || claims.size === 0) return true;
2981
+ let primaryCallId = callId;
2982
+ let primary = {
2983
+ operation,
2984
+ order: Number.MAX_SAFE_INTEGER
2985
+ };
2986
+ for (const [candidateCallId, candidate] of claims) if (compareMediaCardClaims(candidate, primary) < 0) {
2987
+ primaryCallId = candidateCallId;
2988
+ primary = candidate;
2989
+ }
2990
+ return primaryCallId === callId;
2991
+ }
2992
+ controllerForSession(sessionId) {
2993
+ const existing = this.#controllers.get(sessionId);
2994
+ if (existing !== void 0) return existing;
2995
+ const controller = new DesignController(this.#rpc, sessionId);
2996
+ this.#controllers.set(sessionId, controller);
2997
+ return controller;
2998
+ }
2999
+ retainSession(sessionId) {
3000
+ const pendingRelease = this.#sessionReleaseTimers.get(sessionId);
3001
+ if (pendingRelease !== void 0) {
3002
+ window.clearTimeout(pendingRelease);
3003
+ this.#sessionReleaseTimers.delete(sessionId);
3004
+ }
3005
+ this.#sessionReferences.set(sessionId, (this.#sessionReferences.get(sessionId) ?? 0) + 1);
3006
+ let retained = true;
3007
+ return () => {
3008
+ if (!retained) return;
3009
+ retained = false;
3010
+ const remaining = Math.max(0, (this.#sessionReferences.get(sessionId) ?? 1) - 1);
3011
+ if (remaining > 0) {
3012
+ this.#sessionReferences.set(sessionId, remaining);
3013
+ return;
3014
+ }
3015
+ this.#sessionReferences.delete(sessionId);
3016
+ const timer = window.setTimeout(() => {
3017
+ this.#sessionReleaseTimers.delete(sessionId);
3018
+ if ((this.#sessionReferences.get(sessionId) ?? 0) === 0) this.releaseSession(sessionId);
3019
+ }, 0);
3020
+ this.#sessionReleaseTimers.set(sessionId, timer);
3021
+ };
3022
+ }
3023
+ watchJob(sessionId, jobId, initialStatus) {
3024
+ if (initialStatus !== "running") return () => void 0;
3025
+ const key = `${sessionId}\n${jobId}`;
3026
+ const existing = this.#watchedJobs.get(key);
3027
+ if (existing !== void 0) {
3028
+ existing.references += 1;
3029
+ return () => this.#releaseJobWatch(key, existing);
3030
+ }
3031
+ const controller = this.controllerForSession(sessionId);
3032
+ const watched = {
3033
+ references: 1,
3034
+ timer: null,
3035
+ stopped: false,
3036
+ unsubscribeStore: () => void 0
3037
+ };
3038
+ const liveStatus = () => controller.store.getSnapshot().data?.jobs.find((job) => job.jobId === jobId)?.status;
3039
+ const stopWhenTerminal = () => {
3040
+ const status = liveStatus();
3041
+ if (status === void 0 || status === "running") return;
3042
+ watched.stopped = true;
3043
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3044
+ watched.timer = null;
3045
+ };
3046
+ const poll = async () => {
3047
+ watched.timer = null;
3048
+ if (watched.stopped) return;
3049
+ await controller.load();
3050
+ stopWhenTerminal();
3051
+ if (!watched.stopped && watched.references > 0) watched.timer = window.setTimeout(() => {
3052
+ poll();
3053
+ }, 5e3);
3054
+ };
3055
+ watched.unsubscribeStore = controller.store.subscribe(stopWhenTerminal);
3056
+ this.#watchedJobs.set(key, watched);
3057
+ watched.timer = window.setTimeout(() => {
3058
+ poll();
3059
+ }, 0);
3060
+ return () => this.#releaseJobWatch(key, watched);
3061
+ }
3062
+ open(sessionId, inputActions, draft, trigger) {
3063
+ const controller = this.controllerForSession(sessionId);
3064
+ this.#trigger = trigger;
3065
+ this.#syncSplit(this.#isSplitViewport());
3066
+ this.#publish({
3067
+ open: true,
3068
+ sessionId,
3069
+ controller,
3070
+ draft,
3071
+ inputActions
3072
+ });
3073
+ }
3074
+ close(restoreFocus = true) {
3075
+ if (!this.#snapshot.open) return;
3076
+ this.#syncSplit(false);
3077
+ this.#publish({
3078
+ ...this.#snapshot,
3079
+ open: false
3080
+ });
3081
+ if (restoreFocus) queueMicrotask(() => this.#trigger?.focus());
3082
+ }
3083
+ setSplitMode(enabled) {
3084
+ if (!this.#snapshot.open) return;
3085
+ this.#syncSplit(enabled);
3086
+ }
3087
+ updateSession(sessionId, inputActions, draft) {
3088
+ if (this.#snapshot.sessionId !== sessionId) return;
3089
+ this.#publish({
3090
+ ...this.#snapshot,
3091
+ inputActions,
3092
+ draft
3093
+ });
3094
+ }
3095
+ releaseSession(sessionId) {
3096
+ if (this.#snapshot.sessionId !== sessionId) return;
3097
+ this.#syncSplit(false);
3098
+ this.#trigger = null;
3099
+ this.#publish({
3100
+ open: false,
3101
+ sessionId: null,
3102
+ controller: null,
3103
+ draft: "",
3104
+ inputActions: null
3105
+ });
3106
+ }
3107
+ #releaseJobWatch(key, watched) {
3108
+ if (this.#watchedJobs.get(key) !== watched) return;
3109
+ watched.references -= 1;
3110
+ if (watched.references > 0) return;
3111
+ watched.stopped = true;
3112
+ if (watched.timer !== null) window.clearTimeout(watched.timer);
3113
+ watched.unsubscribeStore();
3114
+ this.#watchedJobs.delete(key);
3115
+ }
3116
+ appendUrl(url) {
3117
+ const actions = this.#snapshot.inputActions;
3118
+ if (actions === null) return;
3119
+ const current = this.#snapshot.draft;
3120
+ const next = `${current}${current === "" || /\s$/u.test(current) ? "" : "\n"}${url}`;
3121
+ actions.setDraft(next);
3122
+ this.#publish({
3123
+ ...this.#snapshot,
3124
+ draft: next
3125
+ });
3126
+ }
3127
+ #publish(snapshot) {
3128
+ this.#snapshot = snapshot;
3129
+ for (const listener of this.#listeners) listener();
3130
+ }
3131
+ #publishMediaCards() {
3132
+ this.#mediaCardVersion += 1;
3133
+ for (const listener of this.#mediaCardListeners) listener();
3134
+ }
3135
+ #syncSplit(enabled) {
3136
+ if (enabled === this.#splitActive) return;
3137
+ if (enabled) {
3138
+ this.#layout.closeDetails();
3139
+ this.#layout.openDetails();
3140
+ } else this.#layout.closeDetails();
3141
+ this.#splitActive = enabled;
3142
+ }
3143
+ };
3144
+ function compareMediaCardClaims(left, right) {
3145
+ return (left.operation === "generate" ? 0 : 1) - (right.operation === "generate" ? 0 : 1) || left.order - right.order;
3146
+ }
3147
+ function ModellixDesignLauncher({ drawer, inputActions, sessionId, t, useInput }) {
3148
+ const draft = useInput((state) => state.draft);
3149
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3150
+ const active = snapshot.open && snapshot.sessionId === String(sessionId);
3151
+ (0, react.useEffect)(() => {
3152
+ drawer.updateSession(String(sessionId), inputActions, draft);
3153
+ }, [
3154
+ draft,
3155
+ drawer,
3156
+ inputActions,
3157
+ sessionId
3158
+ ]);
3159
+ (0, react.useEffect)(() => drawer.retainSession(String(sessionId)), [drawer, sessionId]);
3160
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3161
+ type: "button",
3162
+ variant: "outline",
3163
+ className: "mdlx-design-launcher",
3164
+ "aria-label": t("designTitle"),
3165
+ "aria-expanded": active,
3166
+ "aria-controls": "mdlx-design-drawer",
3167
+ onClick: (event) => {
3168
+ if (active) drawer.close();
3169
+ else drawer.open(String(sessionId), inputActions, draft, event.currentTarget);
3170
+ },
3171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3172
+ className: "mdlx-launcher-label",
3173
+ children: t("designTitle")
3174
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3175
+ className: "mdlx-launcher-compact",
3176
+ "aria-hidden": "true",
3177
+ children: "M"
3178
+ })]
3179
+ });
3180
+ }
3181
+ function ModellixDesignDrawer({ drawer, settingsController, t }) {
3182
+ const snapshot = (0, react.useSyncExternalStore)(drawer.subscribe, drawer.getSnapshot, drawer.getSnapshot);
3183
+ const [retainedSnapshot, setRetainedSnapshot] = (0, react.useState)(snapshot.open && snapshot.controller !== null ? snapshot : null);
3184
+ const [advancedEditorOpen, setAdvancedEditorOpen] = (0, react.useState)(false);
3185
+ (0, react.useEffect)(() => {
3186
+ if (snapshot.open && snapshot.controller !== null) setRetainedSnapshot(snapshot);
3187
+ }, [
3188
+ snapshot.controller,
3189
+ snapshot.open,
3190
+ snapshot.sessionId
3191
+ ]);
3192
+ (0, react.useEffect)(() => {
3193
+ if (snapshot.open || retainedSnapshot === null) return;
3194
+ const timer = window.setTimeout(() => setRetainedSnapshot(null), MODELLIX_DRAWER_EXIT_MS);
3195
+ return () => window.clearTimeout(timer);
3196
+ }, [retainedSnapshot, snapshot.open]);
3197
+ (0, react.useEffect)(() => {
3198
+ setAdvancedEditorOpen(false);
3199
+ }, [snapshot.sessionId]);
3200
+ (0, react.useEffect)(() => {
3201
+ if (!snapshot.open || typeof window.matchMedia !== "function") return;
3202
+ const media = window.matchMedia(MODELLIX_DESIGN_SPLIT_MEDIA_QUERY);
3203
+ const sync = () => drawer.setSplitMode(media.matches);
3204
+ sync();
3205
+ media.addEventListener("change", sync);
3206
+ return () => media.removeEventListener("change", sync);
3207
+ }, [drawer, snapshot.open]);
3208
+ const presentedSnapshot = snapshot.open && snapshot.controller !== null ? snapshot : retainedSnapshot;
3209
+ if (presentedSnapshot === null || presentedSnapshot.controller === null) return null;
3210
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3211
+ className: "mdlx-design-drawer-viewport",
3212
+ "data-closing": !snapshot.open || void 0,
3213
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3214
+ id: "mdlx-design-drawer",
3215
+ className: "mdlx-design-drawer",
3216
+ "aria-labelledby": "mdlx-design-drawer-title",
3217
+ onKeyDown: (event) => {
3218
+ if (event.key === "Escape") {
3219
+ event.preventDefault();
3220
+ drawer.close();
3221
+ }
3222
+ },
3223
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3224
+ className: "mdlx-drawer-header",
3225
+ "data-editor-open": advancedEditorOpen || void 0,
3226
+ children: [
3227
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3228
+ type: "button",
3229
+ variant: "ghost",
3230
+ size: "sm",
3231
+ className: "mdlx-drawer-close",
3232
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 }),
3233
+ "aria-label": `${t("close")} ${t("designTitle")}`,
3234
+ onClick: () => drawer.close()
3235
+ }),
3236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3237
+ className: "mdlx-heading",
3238
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3239
+ id: "mdlx-design-drawer-title",
3240
+ children: t("designTitle")
3241
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3242
+ className: "mdlx-muted",
3243
+ children: t("designDescription")
3244
+ })]
3245
+ }),
3246
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3247
+ className: "mdlx-drawer-header-reveal",
3248
+ hidden: MODELLIX_ADVANCED_EDITOR_ENTRY_HIDDEN,
3249
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3250
+ type: "button",
3251
+ variant: "outline",
3252
+ className: "mdlx-drawer-editor-toggle",
3253
+ "aria-controls": "mdlx-design-editor",
3254
+ "aria-expanded": advancedEditorOpen,
3255
+ onClick: (event) => {
3256
+ const next = !advancedEditorOpen;
3257
+ setAdvancedEditorOpen(next);
3258
+ if (!next && event.detail > 0) event.currentTarget.blur();
3259
+ },
3260
+ children: t("advancedEditor")
3261
+ }) })
3262
+ })
3263
+ ]
3264
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3265
+ className: "mdlx-drawer-body",
3266
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModellixDesignView, {
3267
+ advancedEditorOpen,
3268
+ controller: presentedSnapshot.controller,
3269
+ settingsController,
3270
+ onAddUrl: (url) => drawer.appendUrl(url),
3271
+ t
3272
+ })
3273
+ })]
3274
+ })
3275
+ });
3276
+ }
3277
+ //#endregion
3278
+ //#region src/client/MediaToolView.tsx
3279
+ function ModellixMediaToolView({ block, drawer, inputActions, sessionId, t, useInput }) {
3280
+ const draft = useInput((state) => state.draft);
3281
+ const [tab, setTab] = (0, react.useState)("preview");
3282
+ const [activeIndex, setActiveIndex] = (0, react.useState)(0);
3283
+ const [imageOpen, setImageOpen] = (0, react.useState)(false);
3284
+ const tabId = (0, react.useId)();
3285
+ const imageDialogRef = (0, react.useRef)(null);
3286
+ const isResultBlock = "kind" in block && block.kind === "tool-result";
3287
+ const parsedResult = isResultBlock ? parseMediaToolResult(block.meta) : null;
3288
+ const liveState = useResourceState(drawer.controllerForSession(String(sessionId)).store);
3289
+ const trackedJobId = mediaToolJobId(parsedResult);
3290
+ const operation = parsedResult?.operation;
3291
+ const callId = "callId" in block && typeof block.callId === "string" ? block.callId : `${String(sessionId)}:${trackedJobId ?? "pending"}`;
3292
+ (0, react.useSyncExternalStore)(drawer.subscribeMediaCards, drawer.getMediaCardVersion, drawer.getMediaCardVersion);
3293
+ const parsedStatus = mediaToolStatus(parsedResult);
3294
+ const result = resolveLiveMediaToolResult(parsedResult, trackedJobId === void 0 ? void 0 : liveState.data?.jobs.find((job) => job.jobId === trackedJobId), t);
3295
+ (0, react.useEffect)(() => trackedJobId === void 0 ? void 0 : drawer.watchJob(String(sessionId), trackedJobId, parsedStatus), [
3296
+ drawer,
3297
+ parsedStatus,
3298
+ sessionId,
3299
+ trackedJobId
3300
+ ]);
3301
+ (0, react.useEffect)(() => trackedJobId === void 0 || operation === void 0 ? void 0 : drawer.claimMediaCard(String(sessionId), trackedJobId, callId, operation), [
3302
+ callId,
3303
+ drawer,
3304
+ operation,
3305
+ sessionId,
3306
+ trackedJobId
3307
+ ]);
3308
+ const resources = result?.operation === "get_result" ? result.job?.resources ?? [] : result?.resources ?? [];
3309
+ const resourceIndex = resources.length === 0 ? 0 : activeIndex % resources.length;
3310
+ const current = resources[resourceIndex];
3311
+ const imageSurfaceOpen = useExternalDialogGate(imageOpen && current?.kind === "image");
3312
+ const closeImage = (0, react.useCallback)(() => setImageOpen(false), []);
3313
+ useDialogA11y({
3314
+ open: imageSurfaceOpen,
3315
+ container: imageDialogRef,
3316
+ initialFocusSelector: "[data-mdlx-initial-focus]",
3317
+ mandatory: false,
3318
+ onEscape: closeImage
3319
+ });
3320
+ (0, react.useEffect)(() => setImageOpen(false), [current?.url]);
3321
+ if (trackedJobId !== void 0 && operation !== void 0 && !drawer.isMediaCardPrimary(String(sessionId), trackedJobId, callId, operation)) return null;
3322
+ if (!isResultBlock) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3323
+ className: "mdlx-tool-result mdlx-muted",
3324
+ role: "status",
3325
+ children: t("generating")
3326
+ });
3327
+ if (result === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3328
+ className: "mdlx-tool-result mdlx-muted",
3329
+ role: "status",
3330
+ children: t("noPreview")
3331
+ });
3332
+ const status = result.operation === "get_result" ? result.job?.status : result.status;
3333
+ const modelId = result.operation === "get_result" ? result.job?.modelId : result.modelId;
3334
+ const diagnostic = result.operation === "get_result" ? result.job?.diagnostic : result.diagnostic;
3335
+ const succeeded = status === "succeeded";
3336
+ const activeTabId = `${tabId}-${tab}-tab`;
3337
+ const activePanelId = `${tabId}-${tab}-panel`;
3338
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
3339
+ className: "mdlx-tool-result",
3340
+ children: [
3341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3342
+ className: "mdlx-result-head",
3343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: result.found === false ? t("resultNotFound") : mediaStatusLabel(status, t) }), modelId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3344
+ className: "mdlx-muted",
3345
+ children: modelId
3346
+ })]
3347
+ }),
3348
+ succeeded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3349
+ className: "mdlx-result-tabs",
3350
+ role: "tablist",
3351
+ "aria-label": t("resultViewLabel"),
3352
+ onKeyDown: handleResultTabKeyDown,
3353
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3354
+ id: `${tabId}-preview-tab`,
3355
+ type: "button",
3356
+ role: "tab",
3357
+ "aria-controls": `${tabId}-preview-panel`,
3358
+ "aria-selected": tab === "preview",
3359
+ tabIndex: tab === "preview" ? 0 : -1,
3360
+ className: "mdlx-result-tab",
3361
+ onClick: () => setTab("preview"),
3362
+ children: t("previewTab")
3363
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3364
+ id: `${tabId}-json-tab`,
3365
+ type: "button",
3366
+ role: "tab",
3367
+ "aria-controls": `${tabId}-json-panel`,
3368
+ "aria-selected": tab === "json",
3369
+ tabIndex: tab === "json" ? 0 : -1,
3370
+ className: "mdlx-result-tab",
3371
+ onClick: () => setTab("json"),
3372
+ children: t("jsonTab")
3373
+ })]
3374
+ }), tab === "json" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3375
+ id: activePanelId,
3376
+ role: "tabpanel",
3377
+ "aria-labelledby": activeTabId,
3378
+ className: "mdlx-result-json",
3379
+ children: JSON.stringify(result, null, 2)
3380
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3381
+ id: activePanelId,
3382
+ role: "tabpanel",
3383
+ "aria-labelledby": activeTabId,
3384
+ className: "mdlx-result-panel",
3385
+ children: current === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3386
+ className: "mdlx-empty mdlx-empty-compact",
3387
+ children: t("noPreview")
3388
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3389
+ current.kind === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3390
+ type: "button",
3391
+ className: "mdlx-image-preview-button",
3392
+ "aria-label": t("openImage"),
3393
+ onClick: () => setImageOpen(true),
3394
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3395
+ resource: toWireResource(current, resourceIndex),
3396
+ t
3397
+ })
3398
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
3399
+ resource: toWireResource(current, resourceIndex),
3400
+ t
3401
+ }),
3402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3403
+ className: "mdlx-actions mdlx-actions-start",
3404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3405
+ type: "button",
3406
+ variant: "outline",
3407
+ onClick: () => {
3408
+ const separator = draft === "" || /\s$/u.test(draft) ? "" : "\n";
3409
+ inputActions.setDraft(`${draft}${separator}${current.url}`);
3410
+ },
3411
+ children: t("addUrlToChat")
3412
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3413
+ className: "mdlx-safe-link",
3414
+ href: current.url,
3415
+ target: "_blank",
3416
+ rel: "noopener noreferrer",
3417
+ referrerPolicy: "no-referrer",
3418
+ download: true,
3419
+ children: t("download")
3420
+ })]
3421
+ }),
3422
+ resources.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3423
+ className: "mdlx-resource-nav",
3424
+ "aria-label": t("resourceNavigation"),
3425
+ children: [
3426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3427
+ type: "button",
3428
+ variant: "outline",
3429
+ onClick: () => setActiveIndex((resourceIndex - 1 + resources.length) % resources.length),
3430
+ children: t("previousResource")
3431
+ }),
3432
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3433
+ className: "mdlx-muted",
3434
+ children: t("resourcePosition", {
3435
+ current: resourceIndex + 1,
3436
+ total: resources.length
3437
+ })
3438
+ }),
3439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3440
+ type: "button",
3441
+ variant: "outline",
3442
+ onClick: () => setActiveIndex((resourceIndex + 1) % resources.length),
3443
+ children: t("nextResource")
3444
+ })
3445
+ ]
3446
+ })
3447
+ ] })
3448
+ })] }),
3449
+ diagnostic !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3450
+ className: "mdlx-error",
3451
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
3452
+ className: "mdlx-code",
3453
+ children: diagnostic.code
3454
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: diagnostic.message })]
3455
+ })
3456
+ ]
3457
+ }), succeeded && current?.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
3458
+ open: imageSurfaceOpen,
3459
+ title: t("imageViewerTitle"),
3460
+ closeLabel: t("close"),
3461
+ onClose: closeImage,
3462
+ headless: true,
3463
+ className: "mdlx-modal mdlx-image-modal",
3464
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3465
+ ref: imageDialogRef,
3466
+ className: "mdlx-modal-content",
3467
+ "data-mdlx-dialog-surface": "",
3468
+ tabIndex: -1,
3469
+ children: [
3470
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3471
+ className: "mdlx-heading",
3472
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3473
+ className: "mdlx-modal-title",
3474
+ children: t("imageViewerTitle")
3475
+ })
3476
+ }),
3477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3478
+ className: "mdlx-image-full",
3479
+ src: current.url,
3480
+ alt: t("generatedPreview"),
3481
+ referrerPolicy: "no-referrer"
3482
+ }),
3483
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3484
+ className: "mdlx-actions",
3485
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3486
+ type: "button",
3487
+ variant: "outline",
3488
+ "data-mdlx-initial-focus": "",
3489
+ onClick: closeImage,
3490
+ children: t("close")
3491
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3492
+ className: "mdlx-safe-link",
3493
+ href: current.url,
3494
+ target: "_blank",
3495
+ rel: "noopener noreferrer",
3496
+ referrerPolicy: "no-referrer",
3497
+ download: true,
3498
+ children: t("download")
3499
+ })]
3500
+ })
3501
+ ]
3502
+ })
3503
+ })] });
3504
+ }
3505
+ function mediaToolJobId(result) {
3506
+ return result?.operation === "get_result" ? result.job?.jobId : result?.jobId;
3507
+ }
3508
+ function mediaToolStatus(result) {
3509
+ return result?.operation === "get_result" ? result.job?.status : result?.status;
3510
+ }
3511
+ function resolveLiveMediaToolResult(result, liveJob, t) {
3512
+ if (result === null || liveJob === void 0) return result;
3513
+ const resources = liveJob.resources.map((resource) => ({
3514
+ kind: resource.kind,
3515
+ url: resource.url,
3516
+ ...resource.expiresAt === null ? {} : { expiresAt: resource.expiresAt }
3517
+ }));
3518
+ const diagnostic = liveJob.diagnostic === null ? void 0 : {
3519
+ code: liveJob.diagnostic.code,
3520
+ message: t(designDiagnosticMessageKey(liveJob.diagnostic.code))
3521
+ };
3522
+ if (result.operation === "generate") {
3523
+ const { diagnostic: _staleDiagnostic, ...withoutDiagnostic } = result;
3524
+ return {
3525
+ ...withoutDiagnostic,
3526
+ status: liveJob.status,
3527
+ modelId: liveJob.modelId,
3528
+ resources,
3529
+ ...diagnostic === void 0 ? {} : { diagnostic }
3530
+ };
3531
+ }
3532
+ return {
3533
+ ...result,
3534
+ found: true,
3535
+ job: {
3536
+ jobId: liveJob.jobId,
3537
+ modelId: liveJob.modelId,
3538
+ status: liveJob.status,
3539
+ resources,
3540
+ ...diagnostic === void 0 ? {} : { diagnostic }
3541
+ }
3542
+ };
3543
+ }
3544
+ function parseMediaToolResult(value) {
3545
+ const root = record(value);
3546
+ if (root === null || root.service !== "media" || root.operation !== "generate" && root.operation !== "get_result") return null;
3547
+ if (root.operation === "generate") {
3548
+ const status = safeText(root.status, 64);
3549
+ const modelId = safeText(root.modelId, 256);
3550
+ const jobId = safeText(root.jobId, 256);
3551
+ const diagnostic = parseDiagnostic(root.diagnostic);
3552
+ return {
3553
+ operation: "generate",
3554
+ ...status === void 0 ? {} : { status },
3555
+ ...modelId === void 0 ? {} : { modelId },
3556
+ ...jobId === void 0 ? {} : { jobId },
3557
+ resources: parseResources(root.resources),
3558
+ ...diagnostic === void 0 ? {} : { diagnostic }
3559
+ };
3560
+ }
3561
+ const job = record(root.job);
3562
+ const diagnostic = job === null ? void 0 : parseDiagnostic(job.diagnostic);
3563
+ return {
3564
+ operation: "get_result",
3565
+ found: root.found === true,
3566
+ ...job === null ? {} : { job: {
3567
+ jobId: safeText(job.jobId, 256) ?? "",
3568
+ modelId: safeText(job.modelId, 256) ?? "",
3569
+ status: safeText(job.status, 64) ?? "",
3570
+ resources: parseResources(job.resources),
3571
+ ...diagnostic === void 0 ? {} : { diagnostic }
3572
+ } }
3573
+ };
3574
+ }
3575
+ function parseResources(value) {
3576
+ if (!Array.isArray(value)) return [];
3577
+ const resources = [];
3578
+ for (const candidate of value.slice(0, 32)) {
3579
+ const resource = record(candidate);
3580
+ if (resource === null || resource.kind !== "image" && resource.kind !== "video" && resource.kind !== "audio" || typeof resource.url !== "string") continue;
3581
+ let url;
3582
+ try {
3583
+ url = safeResourceHref(resource.url);
3584
+ } catch {
3585
+ continue;
3586
+ }
3587
+ const expiresAt = safeText(resource.expiresAt, 128);
3588
+ resources.push({
3589
+ kind: resource.kind,
3590
+ url,
3591
+ ...expiresAt === void 0 ? {} : { expiresAt }
3592
+ });
3593
+ }
3594
+ return resources;
3595
+ }
3596
+ function parseDiagnostic(value) {
3597
+ const diagnostic = record(value);
3598
+ if (diagnostic === null) return void 0;
3599
+ const code = safeText(diagnostic.code, 128);
3600
+ const message = safeText(diagnostic.message, 1024);
3601
+ return code === void 0 || message === void 0 ? void 0 : {
3602
+ code,
3603
+ message
3604
+ };
3605
+ }
3606
+ function safeText(value, maximum) {
3607
+ return typeof value === "string" && value.length > 0 && value.length <= maximum ? value : void 0;
3608
+ }
3609
+ function record(value) {
3610
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
3611
+ }
3612
+ function mediaStatusLabel(status, t) {
3613
+ switch (status) {
3614
+ case "running": return t("running");
3615
+ case "succeeded": return t("succeeded");
3616
+ case "failed": return t("failed");
3617
+ case "canceled": return t("canceled");
3618
+ case "expired": return t("expired");
3619
+ case "submit-unknown": return t("unknown");
3620
+ default: return t("unknown");
3621
+ }
3622
+ }
3623
+ function toWireResource(resource, index) {
3624
+ return {
3625
+ id: `tool-resource-${String(index)}`,
3626
+ kind: resource.kind,
3627
+ url: resource.url,
3628
+ downloadUrl: resource.url,
3629
+ expiresAt: resource.expiresAt ?? null
3630
+ };
3631
+ }
3632
+ //#endregion
2865
3633
  //#region src/client/locales.ts
2866
3634
  const MODELLIX_LOCALE_NAMESPACE = "modellix";
2867
3635
  const zh = {
2868
3636
  dateLocale: "zh-CN",
2869
3637
  nav: "Modellix",
2870
- designTab: "Design",
2871
3638
  onboardingTitle: "连接 Modellix",
2872
3639
  onboardingDescription: "输入一个 Modellix API Key,即可启用 Design、LLM 模型和 Web Tools。",
2873
3640
  keyLabel: "Modellix API Key",
@@ -2884,7 +3651,7 @@ const zh = {
2884
3651
  serviceLlm: "LLM 模型",
2885
3652
  serviceLlmDescription: "在 Harness 中快速切换 Modellix 支持的模型。",
2886
3653
  serviceWeb: "Web Tools",
2887
- serviceWebDescription: "为原生 web_search 与 web_fetch 提供 Modellix Provider。",
3654
+ serviceWebDescription: "提供可自动调用的 Modellix 网页搜索与读取工具。",
2888
3655
  settingsTitle: "Modellix",
2889
3656
  settingsDescription: "一个 API Key,统一使用 Design、LLM 与 Web Tools。",
2890
3657
  credentialTitle: "API Key",
@@ -2927,13 +3694,13 @@ const zh = {
2927
3694
  errorGeneric: "暂时无法完成操作,请重试。",
2928
3695
  errorConflict: "配置已在其他位置更新,请刷新后重试。",
2929
3696
  errorKeyInvalid: "这个 API Key 无效,请检查后重新输入。",
2930
- errorBilling: "账户当前无法计费,请检查 Modellix 余额或账单状态。",
3697
+ errorBilling: "当前请求无法完成,请在 Modellix 控制台查看账户状态。",
2931
3698
  errorRateLimited: "请求过于频繁,请稍后手动重试。",
2932
3699
  errorOffline: "当前无法连接 Modellix,请检查网络后重试。",
2933
3700
  errorTimeout: "Modellix 响应超时,请稍后手动重试。",
2934
3701
  errorServer: "Modellix 服务暂时不可用,请稍后手动重试。",
2935
3702
  errorPolicy: "当前账户或环境策略不允许此操作。",
2936
- errorSubmitUnknown: "提交结果未知。请先查看结果列表,避免重复计费。",
3703
+ errorSubmitUnknown: "提交结果未知。请先查看结果列表,不要重复提交。",
2937
3704
  errorAssetExpired: "结果资源已过期,无法继续访问。",
2938
3705
  errorDesignSchema: "模型参数定义已变化或无法解析,请刷新模型后重试。",
2939
3706
  designTitle: "Modellix Design",
@@ -2953,11 +3720,11 @@ const zh = {
2953
3720
  promptPlaceholder: "描述画面、视频动作或要合成的声音…",
2954
3721
  parametersTitle: "参数",
2955
3722
  advancedParameters: "高级参数({count})",
3723
+ advancedEditor: "高级参数编辑器",
2956
3724
  required: "必填",
2957
3725
  optional: "可选",
2958
3726
  requiredMissing: "请填写所有必填参数后再生成。",
2959
3727
  assistantTitle: "用对话调整参数",
2960
- assistantPaidNotice: "发送后会使用同一个 Modellix Key 调用固定 LLM 生成参数提议,可能产生费用;不会自动生成媒体。",
2961
3728
  assistantPlaceholder: "例如:改成 16:9,生成 8 秒视频,风格更电影化",
2962
3729
  propose: "生成参数提议",
2963
3730
  proposing: "正在分析…",
@@ -2969,7 +3736,6 @@ const zh = {
2969
3736
  conflicts: "需要先解决冲突",
2970
3737
  generate: "确认并生成",
2971
3738
  generating: "正在提交…",
2972
- paidNotice: "生成是计费操作;每次点击只提交一次,不会自动重试。",
2973
3739
  designDisabled: "Design 已关闭,可在 Modellix 设置中重新开启。",
2974
3740
  keyRequired: "请先在 Modellix 设置中配置 API Key。",
2975
3741
  noModels: "当前没有可用模型,请稍后刷新。",
@@ -2983,9 +3749,17 @@ const zh = {
2983
3749
  designNoticeCatalogUnavailable: "Modellix Design 模型目录暂时不可用,请稍后刷新。",
2984
3750
  designNoticeCredentialReloaded: "API Key 已更改,Design 模型和参数已重新加载。",
2985
3751
  resultsTitle: "结果",
2986
- runningTitle: "进行中",
2987
- succeededTitle: "已完成",
2988
- diagnosticsTitle: "诊断",
3752
+ resultsCount: "{count} 条",
3753
+ resultViewLabel: "结果视图",
3754
+ previewTab: "预览",
3755
+ jsonTab: "JSON",
3756
+ noPreview: "当前还没有可预览的媒体结果。",
3757
+ resultNotFound: "未找到对应的生成任务。",
3758
+ resourceNavigation: "媒体结果导航",
3759
+ previousResource: "上一项",
3760
+ nextResource: "下一项",
3761
+ resourcePosition: "第 {current} / {total} 项",
3762
+ addUrlToChat: "添加 URL 到对话框",
2989
3763
  noResults: "还没有生成记录。确认参数并生成后,结果会显示在这里。",
2990
3764
  running: "生成中",
2991
3765
  succeeded: "已完成",
@@ -2993,10 +3767,10 @@ const zh = {
2993
3767
  canceled: "已取消",
2994
3768
  unknown: "提交结果未知",
2995
3769
  expired: "结果已过期",
2996
- openImage: "打开原图",
3770
+ openImage: "放大图片",
2997
3771
  imageViewerTitle: "生成图片",
2998
3772
  close: "关闭",
2999
- download: "下载结果",
3773
+ download: "下载",
3000
3774
  expiresAt: "有效期至 {time}",
3001
3775
  invalidJson: "请输入有效的 JSON。",
3002
3776
  invalidParameter: "此参数不符合当前模型的约束。",
@@ -3007,9 +3781,9 @@ const zh = {
3007
3781
  generatedAudioPreview: "生成音频结果预览",
3008
3782
  jobModel: "模型:{model}",
3009
3783
  jobCreated: "创建于 {time}",
3010
- diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复计费生成提交。",
3784
+ diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复生成提交。",
3011
3785
  diagnosticCredentialChanged: "此任务属于更早的 API Key,无法继续刷新。",
3012
- diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,避免重复计费。",
3786
+ diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,不要重复提交。",
3013
3787
  diagnosticGenerationFailed: "生成任务未完成。",
3014
3788
  diagnosticResultUnavailable: "生成已完成,但没有可用的结果资源。",
3015
3789
  diagnosticCredentialRejected: "刷新任务时 Modellix 拒绝了当前 API Key。",
@@ -3028,7 +3802,6 @@ const zh = {
3028
3802
  const en = {
3029
3803
  dateLocale: "en-US",
3030
3804
  nav: "Modellix",
3031
- designTab: "Design",
3032
3805
  onboardingTitle: "Connect Modellix",
3033
3806
  onboardingDescription: "Enter one Modellix API Key to enable Design, LLM models, and Web Tools.",
3034
3807
  keyLabel: "Modellix API Key",
@@ -3045,7 +3818,7 @@ const en = {
3045
3818
  serviceLlm: "LLM models",
3046
3819
  serviceLlmDescription: "Quickly switch between Modellix models in Harness.",
3047
3820
  serviceWeb: "Web Tools",
3048
- serviceWebDescription: "Provide Modellix for native web_search and web_fetch.",
3821
+ serviceWebDescription: "Provide automatic Modellix Web Search and Web Fetch tools.",
3049
3822
  settingsTitle: "Modellix",
3050
3823
  settingsDescription: "One API Key for Design, LLM, and Web Tools.",
3051
3824
  credentialTitle: "API Key",
@@ -3088,13 +3861,13 @@ const en = {
3088
3861
  errorGeneric: "The operation could not be completed. Try again.",
3089
3862
  errorConflict: "Settings changed elsewhere. Refresh and try again.",
3090
3863
  errorKeyInvalid: "This API Key is invalid. Check it and enter it again.",
3091
- errorBilling: "Billing is unavailable. Check the Modellix balance or billing status.",
3864
+ errorBilling: "This request cannot be completed. Check the account status in the Modellix console.",
3092
3865
  errorRateLimited: "Too many requests. Wait before retrying manually.",
3093
3866
  errorOffline: "Modellix cannot be reached. Check the network and try again.",
3094
3867
  errorTimeout: "Modellix timed out. Retry manually later.",
3095
3868
  errorServer: "Modellix is temporarily unavailable. Retry manually later.",
3096
3869
  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.",
3870
+ errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting again.",
3098
3871
  errorAssetExpired: "This result resource has expired and is no longer available.",
3099
3872
  errorDesignSchema: "The model parameter definition changed or could not be parsed. Refresh the model and try again.",
3100
3873
  designTitle: "Modellix Design",
@@ -3114,11 +3887,11 @@ const en = {
3114
3887
  promptPlaceholder: "Describe the image, video motion, or sound…",
3115
3888
  parametersTitle: "Parameters",
3116
3889
  advancedParameters: "Advanced parameters ({count})",
3890
+ advancedEditor: "Advanced parameter editor",
3117
3891
  required: "Required",
3118
3892
  optional: "Optional",
3119
3893
  requiredMissing: "Complete every required parameter before generating.",
3120
3894
  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
3895
  assistantPlaceholder: "For example: make it 16:9, eight seconds, and more cinematic",
3123
3896
  propose: "Propose parameter changes",
3124
3897
  proposing: "Analyzing…",
@@ -3130,7 +3903,6 @@ const en = {
3130
3903
  conflicts: "Resolve conflicts before applying",
3131
3904
  generate: "Confirm and generate",
3132
3905
  generating: "Submitting…",
3133
- paidNotice: "Generation is billed. Each click submits once and is never retried automatically.",
3134
3906
  designDisabled: "Design is disabled. Enable it in Modellix settings.",
3135
3907
  keyRequired: "Configure a Modellix API Key in settings first.",
3136
3908
  noModels: "No model is currently available. Refresh and try again.",
@@ -3144,9 +3916,17 @@ const en = {
3144
3916
  designNoticeCatalogUnavailable: "The Modellix Design model catalog is temporarily unavailable. Refresh it later.",
3145
3917
  designNoticeCredentialReloaded: "The API Key changed, so the Design model and parameters were reloaded.",
3146
3918
  resultsTitle: "Results",
3147
- runningTitle: "Running",
3148
- succeededTitle: "Succeeded",
3149
- diagnosticsTitle: "Diagnostics",
3919
+ resultsCount: "{count} item(s)",
3920
+ resultViewLabel: "Result view",
3921
+ previewTab: "Preview",
3922
+ jsonTab: "JSON",
3923
+ noPreview: "No media preview is available yet.",
3924
+ resultNotFound: "The requested generation task was not found.",
3925
+ resourceNavigation: "Media result navigation",
3926
+ previousResource: "Previous",
3927
+ nextResource: "Next",
3928
+ resourcePosition: "{current} of {total}",
3929
+ addUrlToChat: "Add URL to chat",
3150
3930
  noResults: "No generations yet. Confirm parameters and generate to see results here.",
3151
3931
  running: "Running",
3152
3932
  succeeded: "Succeeded",
@@ -3154,10 +3934,10 @@ const en = {
3154
3934
  canceled: "Canceled",
3155
3935
  unknown: "Submission outcome unknown",
3156
3936
  expired: "Result expired",
3157
- openImage: "Open full image",
3937
+ openImage: "Enlarge image",
3158
3938
  imageViewerTitle: "Generated image",
3159
3939
  close: "Close",
3160
- download: "Download result",
3940
+ download: "Download",
3161
3941
  expiresAt: "Available until {time}",
3162
3942
  invalidJson: "Enter valid JSON.",
3163
3943
  invalidParameter: "This value does not satisfy the current model constraints.",
@@ -3168,9 +3948,9 @@ const en = {
3168
3948
  generatedAudioPreview: "Generated audio result preview",
3169
3949
  jobModel: "Model: {model}",
3170
3950
  jobCreated: "Created {time}",
3171
- diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a billed generation submission automatically.",
3951
+ diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a generation submission automatically.",
3172
3952
  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.",
3953
+ diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting again.",
3174
3954
  diagnosticGenerationFailed: "The generation did not complete.",
3175
3955
  diagnosticResultUnavailable: "The generation completed without a usable result resource.",
3176
3956
  diagnosticCredentialRejected: "Modellix rejected the current API Key while refreshing this task.",
@@ -3527,7 +4307,7 @@ function llmHealthText(health, t) {
3527
4307
  //#endregion
3528
4308
  //#region src/client/styles.ts
3529
4309
  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}
4310
+ .mdlx-settings,.mdlx-design,.mdlx-design-drawer,.mdlx-tool-result,.mdlx-modal-content{font-family:var(--dsw-font-family,inherit);color:var(--dsw-alias-label-primary);box-sizing:border-box}.mdlx-settings *,.mdlx-design *,.mdlx-design-drawer *,.mdlx-tool-result *,.mdlx-modal-content *{box-sizing:border-box}
3531
4311
  .mdlx-settings{width:min(100%,760px);min-width:0;display:grid;gap:12px;padding:4px}
3532
4312
  .mdlx-heading{display:grid;gap:4px;min-width:0;margin:0 0 8px}.mdlx-heading h2,.mdlx-heading h3,.mdlx-heading p{margin:0;overflow-wrap:anywhere}
3533
4313
  .mdlx-heading h2{font-size:20px;line-height:28px;font-weight:500}.mdlx-heading h3{font-size:15px;line-height:22px;font-weight:600}
@@ -3536,7 +4316,7 @@ const MODELLIX_CLIENT_CSS = String.raw`
3536
4316
  .mdlx-card-head,.mdlx-status-row,.mdlx-actions,.mdlx-result-head{display:flex;align-items:center;gap:12px;min-width:0;justify-content:space-between;flex-wrap:wrap}
3537
4317
  .mdlx-card-head>.mdlx-heading{margin-bottom:0}
3538
4318
  .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}
4319
+ .mdlx-actions{justify-content:flex-end}.mdlx-actions>*{min-width:0;max-width:100%}.mdlx-actions-start{justify-content:flex-start}.mdlx-grow{flex:1 1 auto}.mdlx-settings button,.mdlx-design button,.mdlx-design-drawer button,.mdlx-tool-result button,.mdlx-modal-content button{height:auto;min-height:36px}
3540
4320
  .mdlx-service-list{display:grid;gap:8px}.mdlx-switch-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;padding:12px;border-radius:12px;border:1px solid var(--dsw-alias-border-l2)}
3541
4321
  .mdlx-switch-copy{display:grid;gap:2px;min-width:0}.mdlx-switch-copy strong{font-size:14px;font-weight:500;overflow-wrap:anywhere}.mdlx-switch-copy span{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;overflow-wrap:anywhere}
3542
4322
  .mdlx-switch-target{display:inline-flex;width:24px;height:24px;align-items:center;justify-content:center;justify-self:center}.mdlx-switch{width:24px;height:24px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}.mdlx-switch:disabled{cursor:not-allowed}
@@ -3553,25 +4333,28 @@ const MODELLIX_CLIENT_CSS = String.raw`
3553
4333
  .mdlx-link-button{appearance:none;border:0;padding:0;background:transparent;font:inherit;cursor:pointer}
3554
4334
  .mdlx-modal{box-sizing:border-box;width:min(600px,calc(100vw - 48px));max-height:calc(100dvh - 48px);padding:0!important;gap:0!important;border-radius:24px!important;box-shadow:var(--dsw-shadow-lv3)!important;overflow:auto;overscroll-behavior:contain}.mdlx-modal-confirm{width:min(380px,calc(100vw - 48px))}.mdlx-modal-content{width:100%;min-width:0;padding:28px;display:grid;gap:20px;outline:none}.mdlx-modal-content>.mdlx-heading{margin-bottom:0}.mdlx-modal-title{font-size:20px;line-height:28px;font-weight:500;margin:0;overflow-wrap:anywhere}.mdlx-modal-description{font-size:14px;line-height:22px;color:var(--dsw-alias-label-secondary);margin:0;overflow-wrap:anywhere}
3555
4335
  .mdlx-design{container-type:inline-size;height:100%;min-height:0;box-sizing:border-box;padding:16px;padding-bottom:max(16px,env(safe-area-inset-bottom));scroll-padding-block-end:max(24px,env(safe-area-inset-bottom));overflow:auto;background:var(--dsw-alias-bg-base)}
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}
4336
+ .mdlx-design-shell{width:100%;min-height:100%;display:grid;grid-template-columns:minmax(0,1fr);align-content:start;gap:12px}.mdlx-design-pane{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:16px;display:flex;flex-direction:column;gap:16px;overflow-wrap:anywhere}
4337
+ .mdlx-design-launcher{height:32px!important;min-height:32px!important;padding:6px 12px!important;border-radius:18px!important;font-size:13px!important;font-weight:400!important;line-height:20px!important}.mdlx-design-launcher .mdlx-launcher-compact{display:none}.mdlx-design-drawer-viewport{position:fixed;inset:0 0 0 auto;width:360px;max-width:100vw;overflow:hidden;pointer-events:none}.mdlx-design-drawer{position:absolute;inset:0 0 0 auto;width:360px;min-width:360px;display:grid;grid-template-rows:auto minmax(0,1fr);border-inline-start:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);pointer-events:auto;transform:translateX(0);opacity:1;transition:transform 180ms ease-out,opacity 180ms ease-out}@starting-style{.mdlx-design-drawer{transform:translateX(100%);opacity:.72}}.mdlx-design-drawer-viewport[data-closing="true"] .mdlx-design-drawer{transform:translateX(100%);opacity:.72;pointer-events:none}.mdlx-drawer-header{position:relative;display:grid;gap:0;padding:16px 60px 16px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.mdlx-drawer-header>.mdlx-heading{margin:0}.mdlx-drawer-close{position:absolute!important;inset-block-start:12px;inset-inline-end:12px;width:36px!important;min-width:36px!important;height:36px!important;min-height:36px!important;padding:0!important;border-radius:50%!important;justify-content:center}.mdlx-drawer-header-reveal{display:grid;grid-template-rows:0fr;opacity:0;margin-top:0;transition:grid-template-rows 160ms ease,opacity 120ms ease,margin-top 160ms ease}.mdlx-drawer-header-reveal[hidden]{display:none!important}.mdlx-drawer-header-reveal>div{min-height:0;overflow:hidden}.mdlx-drawer-header:hover .mdlx-drawer-header-reveal,.mdlx-drawer-header:focus-within .mdlx-drawer-header-reveal,.mdlx-drawer-header[data-editor-open="true"] .mdlx-drawer-header-reveal{grid-template-rows:1fr;opacity:1;margin-top:12px}.mdlx-drawer-editor-toggle{justify-self:start}.mdlx-drawer-body{min-height:0;overflow:hidden}.mdlx-drawer-body>.mdlx-design{padding:12px}.mdlx-design-editor{display:grid;gap:12px}.mdlx-design-editor[hidden]{display:none}.mdlx-results-summary{cursor:pointer;min-height:40px;display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:15px;font-weight:600;overflow-wrap:anywhere}.mdlx-results-disclosure{display:block}.mdlx-results-summary{margin:-4px 0;padding:4px 0}.mdlx-results-body{padding-top:12px}
3557
4338
  .mdlx-design-scroll{display:grid;gap:16px;min-height:0}.mdlx-parameter-list{display:grid;gap:12px}.mdlx-parameter{display:grid;gap:6px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}
3558
4339
  .mdlx-advanced{display:grid;gap:12px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}.mdlx-advanced>summary{cursor:pointer;font-size:14px;font-weight:500;min-height:24px;overflow-wrap:anywhere}
3559
4340
  .mdlx-proposal{display:grid;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:12px;background:var(--dsw-alias-bg-layer-2)}
3560
4341
  .mdlx-generate-block{display:grid;gap:8px;min-width:0}.mdlx-generate-block>p{margin:0}
3561
4342
  .mdlx-change-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.mdlx-change{display:grid;grid-template-columns:minmax(100px,.4fr) minmax(0,1fr);gap:8px;min-width:0;font-size:13px}.mdlx-change>*{min-width:0;overflow-wrap:anywhere}.mdlx-code{font-family:var(--ds-font-family-code,monospace);font-size:12px;overflow-wrap:anywhere}
3562
4343
  .mdlx-result-section{display:grid;gap:10px}.mdlx-result-section h3{margin:0;font-size:14px;font-weight:600}.mdlx-result-list{display:grid;gap:12px;margin:0;padding:0;list-style:none}
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}
4344
+ .mdlx-result-card{display:block;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}.mdlx-result-disclosure{display:block}.mdlx-result-card-summary{position:relative;display:grid;gap:8px;min-height:72px;padding:12px 44px 12px 12px;cursor:pointer;list-style:none}.mdlx-result-card-summary::-webkit-details-marker{display:none}.mdlx-result-card-summary::after{content:"";position:absolute;inset-block-start:18px;inset-inline-end:18px;width:8px;height:8px;border-inline-end:2px solid currentColor;border-block-end:2px solid currentColor;transform:rotate(-45deg);transition:transform 160ms ease}.mdlx-result-disclosure[open]>.mdlx-result-card-summary::after{transform:rotate(45deg)}.mdlx-result-card-body{display:grid;gap:12px;padding:0 12px 12px}
4345
+ .mdlx-result-tabs{display:flex;align-items:center;gap:4px;border-bottom:1px solid var(--dsw-alias-border-l2)}.mdlx-result-tab{appearance:none;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;padding:8px 12px;cursor:pointer}.mdlx-result-tab[aria-selected="true"]{border-bottom-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary);font-weight:600}.mdlx-result-panel{display:grid;gap:12px;min-width:0}.mdlx-result-json{max-height:320px;margin:0;padding:12px;border-radius:12px;background:var(--dsw-alias-markdown-code-block,var(--dsw-alias-bg-layer-3));font-family:var(--ds-font-family-code,monospace);font-size:12px;line-height:18px;white-space:pre-wrap;overflow:auto;overflow-wrap:anywhere}.mdlx-resource-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.mdlx-resource{display:grid;gap:8px;min-width:0}.mdlx-resource-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}.mdlx-media{display:block;width:100%;max-height:420px;object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}.mdlx-image-preview-button{appearance:none;width:100%;padding:0;border:0;border-radius:12px;background:transparent;cursor:zoom-in}.mdlx-image-preview-button>.mdlx-media{pointer-events:none}
4346
+ .mdlx-media-audio{min-height:48px}.mdlx-empty{display:grid;place-items:center;min-height:160px;text-align:center;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;padding:24px}.mdlx-empty-compact{min-height:96px}.mdlx-tool-result{display:grid;gap:12px;margin:4px 0 4px 4px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}
3566
4347
  .mdlx-image-modal{width:min(1000px,calc(100vw - 48px))}.mdlx-image-full{display:block;width:100%;max-height:calc(100dvh - 220px);object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
3567
4348
  .mdlx-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
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}
4349
+ .mdlx-input{height:auto;min-height:40px}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible,.mdlx-result-tab:focus-visible,.mdlx-image-preview-button:focus-visible,.mdlx-drawer-editor-toggle:focus-visible,.mdlx-results-summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.mdlx-result-card-summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
4350
+ @media (min-width:1280px){.mdlx-design-drawer{box-shadow:none}}
3569
4351
  @media (max-width:768px){.mdlx-design{padding:12px;padding-bottom:max(12px,env(safe-area-inset-bottom))}.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
3570
4352
  @container (max-width:992px){.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
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}}
4353
+ @media (max-width:560px){.mdlx-design-launcher{width:40px;min-width:40px;padding-inline:0!important}.mdlx-design-launcher .mdlx-launcher-label{display:none}.mdlx-design-launcher .mdlx-launcher-compact{display:inline}.mdlx-modal{width:calc(100vw - 48px)}.mdlx-modal-content{padding:24px}.mdlx-input-row,.mdlx-model-tools{grid-template-columns:minmax(0,1fr)}.mdlx-input-row>button,.mdlx-model-tools button,.mdlx-actions button{width:100%;min-width:0;justify-self:stretch}.mdlx-actions{display:grid;grid-template-columns:minmax(0,1fr)}.mdlx-switch-row{grid-template-columns:minmax(0,1fr) 48px}.mdlx-change{grid-template-columns:minmax(0,1fr)}.mdlx-design-drawer-viewport,.mdlx-design-drawer{width:100vw;min-width:0}.mdlx-drawer-header{padding:12px 56px 12px 12px}.mdlx-drawer-close{inset-block-start:8px;inset-inline-end:8px}.mdlx-resource-nav{display:grid;grid-template-columns:1fr}.mdlx-resource-nav>*{width:100%;text-align:center}}
4354
+ @media (max-width:360px){.mdlx-design-launcher{position:fixed!important;inset-block-start:92px;inset-inline-end:12px;z-index:10;width:48px;min-width:48px;height:48px;min-height:48px!important;padding:0!important;border-radius:50%!important}}
4355
+ @media (pointer:coarse){.mdlx-design-launcher{height:48px!important;min-height:48px!important}.mdlx-drawer-header-reveal{grid-template-rows:1fr;opacity:1;margin-top:12px}.mdlx-switch-row,.mdlx-switch-target,.mdlx-safe-link{min-width:48px;min-height:48px}.mdlx-settings button,.mdlx-design button,.mdlx-design-drawer button,.mdlx-tool-result button,.mdlx-modal-content button{min-height:48px}.mdlx-input,.mdlx-select,.mdlx-native-input,.mdlx-advanced>summary{min-height:48px}}
4356
+ @media (prefers-reduced-motion:reduce){.mdlx-design-drawer-viewport,.mdlx-design-drawer,.mdlx-settings *,.mdlx-design *,.mdlx-design-drawer *,.mdlx-tool-result *,.mdlx-modal-content *{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
4357
+ @media (forced-colors:active){.mdlx-modal,.mdlx-design-drawer{border:1px solid CanvasText;box-shadow:none!important}.mdlx-card,.mdlx-switch-row,.mdlx-design-pane,.mdlx-result-card,.mdlx-tool-result,.mdlx-proposal,.mdlx-error,.mdlx-info{forced-color-adjust:auto;border:1px solid CanvasText}.mdlx-input,.mdlx-textarea,.mdlx-select,.mdlx-native-input{border-color:CanvasText}.mdlx-safe-link{color:LinkText}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible,.mdlx-result-tab:focus-visible,.mdlx-image-preview-button:focus-visible,.mdlx-drawer-editor-toggle:focus-visible,.mdlx-results-summary:focus-visible,.mdlx-result-card-summary:focus-visible{outline-color:Highlight}}
3575
4358
  `;
3576
4359
  function installModellixClientStyles(target = typeof document === "undefined" ? void 0 : document) {
3577
4360
  if (target === void 0) return () => void 0;
@@ -3589,6 +4372,11 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3589
4372
  id: "modellix.credential-recovery",
3590
4373
  order: 10
3591
4374
  },
4375
+ {
4376
+ name: "shell.overlay",
4377
+ id: "modellix.design-drawer",
4378
+ order: 20
4379
+ },
3592
4380
  {
3593
4381
  name: "settings.onboarding",
3594
4382
  id: "modellix.onboarding",
@@ -3600,9 +4388,19 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3600
4388
  order: 30
3601
4389
  },
3602
4390
  {
3603
- name: "conversation.view",
3604
- id: "modellix.design",
4391
+ name: "conversation.session.header.utilities",
4392
+ id: "modellix.design-launcher",
3605
4393
  order: 20
4394
+ },
4395
+ {
4396
+ name: "tool.call.toolview",
4397
+ key: "modellix_media_generate",
4398
+ priority: 20
4399
+ },
4400
+ {
4401
+ name: "tool.call.toolview",
4402
+ key: "modellix_media_get_result",
4403
+ priority: 20
3606
4404
  }
3607
4405
  ]);
3608
4406
  //#endregion
@@ -3610,7 +4408,8 @@ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3610
4408
  const inject = [
3611
4409
  "slots",
3612
4410
  "locale",
3613
- "connection"
4411
+ "connection",
4412
+ "layout"
3614
4413
  ];
3615
4414
  function apply(ctx) {
3616
4415
  ctx.effect(() => ctx.locale.register(MODELLIX_LOCALE_NAMESPACE, {
@@ -3621,6 +4420,7 @@ function apply(ctx) {
3621
4420
  const t = ctx.locale.bind(MODELLIX_LOCALE_NAMESPACE);
3622
4421
  const rpc = new ModellixRpcClient(ctx.connection.rpc);
3623
4422
  const settingsController = new SettingsController(rpc);
4423
+ const designDrawer = new DesignDrawerController(rpc, ctx.layout);
3624
4424
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
3625
4425
  name: "shell.overlay",
3626
4426
  id: "modellix.credential-recovery",
@@ -3628,6 +4428,16 @@ function apply(ctx) {
3628
4428
  locale: MODELLIX_LOCALE_NAMESPACE,
3629
4429
  inject: () => ({ controller: settingsController })
3630
4430
  }, CredentialRecoveryOverlay));
4431
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
4432
+ name: "shell.overlay",
4433
+ id: "modellix.design-drawer",
4434
+ order: 20,
4435
+ locale: MODELLIX_LOCALE_NAMESPACE,
4436
+ inject: () => ({
4437
+ drawer: designDrawer,
4438
+ settingsController
4439
+ })
4440
+ }, ModellixDesignDrawer));
3631
4441
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3632
4442
  name: "settings.onboarding",
3633
4443
  id: "modellix.onboarding",
@@ -3643,17 +4453,27 @@ function apply(ctx) {
3643
4453
  locale: MODELLIX_LOCALE_NAMESPACE,
3644
4454
  inject: () => ({ controller: settingsController })
3645
4455
  }, ModellixSettingsSection));
3646
- ctx.slots.inject("conversation.view", () => ctx.slots.register({
3647
- name: "conversation.view",
3648
- id: "modellix.design",
4456
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
4457
+ name: "conversation.session.header.utilities",
4458
+ id: "modellix.design-launcher",
3649
4459
  order: 20,
3650
- label: () => t("designTab"),
3651
4460
  locale: MODELLIX_LOCALE_NAMESPACE,
3652
- inject: (sessionId) => ({
3653
- controller: new DesignController(rpc, sessionId),
3654
- settingsController
3655
- })
3656
- }, ModellixDesignView));
4461
+ inject: () => ({ drawer: designDrawer })
4462
+ }, ModellixDesignLauncher));
4463
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4464
+ name: "tool.call.toolview",
4465
+ key: "modellix_media_generate",
4466
+ priority: 20,
4467
+ locale: MODELLIX_LOCALE_NAMESPACE,
4468
+ inject: () => ({ drawer: designDrawer })
4469
+ }, ModellixMediaToolView));
4470
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
4471
+ name: "tool.call.toolview",
4472
+ key: "modellix_media_get_result",
4473
+ priority: 20,
4474
+ locale: MODELLIX_LOCALE_NAMESPACE,
4475
+ inject: () => ({ drawer: designDrawer })
4476
+ }, ModellixMediaToolView));
3657
4477
  }
3658
4478
  //#endregion
3659
4479
  exports.MODELLIX_CLIENT_SLOTS = MODELLIX_CLIENT_SLOTS;