@matamt-dbg/dsh-studio 0.0.0-stage → 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.
package/client.js ADDED
@@ -0,0 +1,2111 @@
1
+ window.__ModuleLoader__.load({id: '@matamt-dbg/dsh-studio', factory: (require) => { const module = { exports: {} }; const exports = module.exports;
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/shell.tsx
32
+ var shell_exports = {};
33
+ __export(shell_exports, {
34
+ apply: () => apply,
35
+ inject: () => inject
36
+ });
37
+ module.exports = __toCommonJS(shell_exports);
38
+
39
+ // src/config-overrides.ts
40
+ var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
41
+ function validateConfigOverrides(value) {
42
+ if (!object(value)) throw new Error("Config overrides must be an object");
43
+ const visit = (v, depth = 0) => {
44
+ if (depth > 32) throw new Error("Config nesting exceeds 32");
45
+ if (typeof v === "number" && !Number.isFinite(v)) throw new Error("Invalid Config number");
46
+ if (v !== null && !["object", "string", "number", "boolean"].includes(typeof v)) throw new Error("Config must be JSON");
47
+ if (Array.isArray(v)) v.forEach((child) => visit(child, depth + 1));
48
+ else if (object(v)) for (const [key, child] of Object.entries(v)) {
49
+ if (["__proto__", "prototype", "constructor"].includes(key)) throw new Error("Unsafe Config key");
50
+ visit(child, depth + 1);
51
+ }
52
+ };
53
+ for (const patch of Object.values(value)) {
54
+ if (!object(patch)) throw new Error("Plugin override must be an object");
55
+ visit(patch);
56
+ }
57
+ }
58
+
59
+ // src/provider-fields.ts
60
+ var field = (key, type, value, minimum) => ({ key, type, ...value === void 0 ? {} : { value }, ...minimum === void 0 ? {} : { minimum } });
61
+ var providerFields = {
62
+ "@deepseek-ai/dsh-compaction-basic": [field("thresholdRatio", "number", 0.8, 0), field("headroomTokens", "number", 65536, 1), field("retainRatio", "number", 0.16, 0), field("retainTokens", "number", void 0, 0), field("summarizationProvider", "string", ""), field("summarizationModel", "string", ""), field("maxTokens", "number", void 0, 1), field("compactionRetries", "number", 1, 0), field("maxOverflowRetries", "number", 1, 0), field("auto", "boolean", true)],
63
+ "@deepseek-ai/dsh-compaction-tool-result-pruner": [field("thresholdChars", "number", 8192, 1), field("headChars", "number", 4096, 0), field("tailChars", "number", 1024, 0)],
64
+ "@deepseek-ai/dsh-agent-tool-presentation": [{ key: "mode", type: "string", options: ["native", "ptc"] }],
65
+ "@deepseek-ai/dsh-persona": [field("prefix", "string"), field("suffix", "string", ""), field("includeRuntimeContext", "boolean", true)],
66
+ "@deepseek-ai/dsh-agent-instructions": [field("maxBytes", "number", 65536), field("maxSourceBytes", "number", void 0, 1), field("projectRootMarkers", "string[]"), field("instructionFileCandidates", "string[]"), field("localInstructionFileCandidates", "string[]")],
67
+ "@deepseek-ai/dsh-time-context": [field("timeZone", "string"), field("refreshIntervalMs", "number", void 0, 0)],
68
+ "@deepseek-ai/dsh-skill-filesystem": [field("providerName", "string", "filesystem"), field("includeDefaultRoots", "boolean", true), field("dshHome", "string"), field("agentsHome", "string"), field("customSkillDirs", "string[]", []), field("bundledSkillDir", "string"), field("watch", "boolean", true), field("watchUsePolling", "boolean", false), field("watchStabilityThresholdMs", "number", 200, 0), field("watchPollIntervalMs", "number", 100, 1), field("watchMaxProjects", "number", 128, 1), field("watchFollowSymlinks", "boolean", true)]
69
+ };
70
+
71
+ // src/execution-defaults.ts
72
+ var record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
73
+ var names = /* @__PURE__ */ new Set(["timeoutMs", "timeout_ms"]);
74
+ function validateExecutionDefaults(value) {
75
+ if (!record(value) || Object.keys(value).some((key) => !["defaults", "batch"].includes(key)) || !record(value.defaults)) throw new Error("Invalid execution defaults");
76
+ if (value.batch !== void 0 && (!record(value.batch) || Object.keys(value.batch).some((key) => !["tool", "parameter", "maxItems"].includes(key)) || value.batch.tool !== "read_batch" || typeof value.batch.parameter !== "string" || !value.batch.parameter || ["__proto__", "prototype", "constructor"].includes(value.batch.parameter) || !Number.isSafeInteger(value.batch.maxItems) || value.batch.maxItems < 1)) throw new Error("Invalid batch limit");
77
+ for (const [tool, fields] of Object.entries(value.defaults)) {
78
+ if (!tool || ["__proto__", "prototype", "constructor"].includes(tool) || !record(fields)) throw new Error("Invalid execution defaults");
79
+ for (const [field2, number] of Object.entries(fields)) if (!names.has(field2) || typeof number !== "number" || !Number.isFinite(number) || number <= 0) throw new Error(`Invalid execution default: ${tool}.${field2}`);
80
+ }
81
+ }
82
+
83
+ // src/prompt-composition.ts
84
+ function parsePreviewRequest(value) {
85
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid preview request");
86
+ const row = value;
87
+ const strings = (input) => Array.isArray(input) && input.every((item) => typeof item === "string");
88
+ if (typeof row.preset !== "string" || !strings(row.providers) || !strings(row.tools) || typeof row.prompt !== "string" || typeof row.model !== "string") throw new Error("Invalid preview request");
89
+ const record2 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
90
+ if (row.persona !== void 0 && typeof row.persona !== "string") throw new Error("Invalid persona");
91
+ if (row.sections !== void 0 && (!record2(row.sections) || Object.values(row.sections).some((v) => typeof v !== "string"))) throw new Error("Invalid section overrides");
92
+ if (row.toolOverrides !== void 0 && (!record2(row.toolOverrides) || Object.values(row.toolOverrides).some((v) => !record2(v) || Object.keys(v).some((k) => !["description", "parameters"].includes(k)) || v.description !== void 0 && typeof v.description !== "string" || v.parameters !== void 0 && (!record2(v.parameters) || Object.values(v.parameters).some((p) => typeof p !== "string"))))) throw new Error("Invalid tool overrides");
93
+ if (row.providerConfigs !== void 0 && (!record2(row.providerConfigs) || Object.values(row.providerConfigs).some((v) => !record2(v)))) throw new Error("Invalid provider configs");
94
+ if (row.configOverrides !== void 0) validateConfigOverrides(row.configOverrides);
95
+ if (row.execution !== void 0) validateExecutionDefaults(row.execution);
96
+ return {
97
+ ...row.configOverrides !== void 0 ? { configOverrides: row.configOverrides } : {},
98
+ ...row.execution !== void 0 ? { execution: row.execution } : {},
99
+ preset: row.preset,
100
+ providers: [...row.providers],
101
+ tools: [...row.tools],
102
+ prompt: row.prompt,
103
+ model: row.model,
104
+ ...row.persona !== void 0 ? { persona: row.persona } : {},
105
+ ...row.sections !== void 0 ? { sections: row.sections } : {},
106
+ ...row.toolOverrides !== void 0 ? { toolOverrides: row.toolOverrides } : {},
107
+ ...row.providerConfigs !== void 0 ? { providerConfigs: row.providerConfigs } : {}
108
+ };
109
+ }
110
+
111
+ // src/ui.tsx
112
+ var import_react = __toESM(require("react"), 1);
113
+
114
+ // src/ui-styles.ts
115
+ var studioStyles = `/* Only this adapter references host tokens. Studio never installs a global theme. */
116
+ .studio-ui {
117
+ --studio-bg: var(--dsw-alias-bg-base);
118
+ --studio-surface: var(--dsw-alias-bg-layer-1);
119
+ --studio-inset: var(--dsw-alias-bg-layer-2);
120
+ --studio-ink: var(--dsw-alias-label-primary);
121
+ --studio-muted: var(--dsw-alias-label-secondary);
122
+ --studio-subtle: var(--dsw-alias-label-tertiary);
123
+ --studio-line: var(--dsw-alias-border-l2);
124
+ --studio-control-line: var(--dsw-alias-border-l3);
125
+ --studio-accent: var(--dsw-alias-brand-primary);
126
+ --studio-hover: var(--dsw-alias-interactive-bg-hover);
127
+ --studio-pressed: var(--dsw-alias-interactive-bg-active);
128
+ --studio-primary-fill: var(--dsw-alias-button-primary-fill);
129
+ --studio-primary-hover: var(--dsw-alias-button-primary-hover);
130
+ --studio-on-primary: var(--dsw-alias-label-primary-foreground);
131
+ --studio-focus: var(--dsw-focus-ring-color);
132
+ --studio-error: var(--dsw-alias-state-error-primary);
133
+ --studio-success: var(--dsw-alias-state-success-primary);
134
+ --studio-warn: var(--dsw-alias-state-warn-primary);
135
+ --studio-info: var(--dsw-alias-state-business-primary);
136
+ --studio-neutral-fill: var(--dsw-alias-bg-module-platform);
137
+ --studio-radius: 12px;
138
+ --studio-control-radius: var(--dsw-radius-md);
139
+ --studio-small-radius: var(--dsw-radius-sm);
140
+ --studio-gap: 16px;
141
+ color: var(--studio-ink);
142
+ font: inherit;
143
+ font-size: 13px;
144
+ line-height: 1.6;
145
+ min-width: 0;
146
+ overflow-wrap: anywhere;
147
+ }
148
+ .studio-ui *, .studio-ui *::before, .studio-ui *::after { box-sizing: border-box; }
149
+ .studio-ui .studio-tool-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 12px; }
150
+ .studio-ui .studio-tool-card { display: flex; flex-direction: column; gap: 8px; padding: 14px; border: 1px solid var(--studio-line); border-radius: var(--studio-radius); cursor: pointer; min-width: 0; }
151
+ .studio-ui .studio-tool-card[data-selected="true"] { background: var(--studio-hover); border-color: var(--studio-ink); }
152
+ .studio-ui .studio-card-excerpt { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; font-weight: 400; color: var(--studio-muted); }
153
+ .studio-ui .studio-tool-card strong { overflow-wrap: anywhere; }
154
+ .studio-ui .studio-detail-dialog { width: min(760px, calc(100vw - 32px)); max-height: calc(100dvh - 48px); overflow: auto; padding: 0; color: var(--studio-ink); background: var(--studio-bg); border: 1px solid var(--studio-line); border-radius: var(--studio-radius); }
155
+ .studio-ui .studio-detail-dialog::backdrop { background: var(--studio-muted); opacity: 0.45; }
156
+ .studio-ui .studio-selection-detail { padding: 16px; margin-top: 12px; border: 1px solid var(--studio-line); border-radius: var(--studio-radius); }
157
+ .studio-ui .studio-schema-tool { margin-top: 12px; padding: 10px; border: 1px solid var(--studio-line); border-radius: var(--studio-small-radius); }
158
+ .studio-ui .studio-schema-tool summary { cursor: pointer; overflow-wrap: anywhere; }
159
+ .studio-ui .studio-schema-fields { margin: 8px 0; }
160
+ .studio-ui .studio-schema-fields > div { display: grid; grid-template-columns: minmax(70px, 120px) minmax(0, 1fr); gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--studio-line); }
161
+ .studio-ui .studio-schema-fields dt { color: var(--studio-muted); overflow-wrap: anywhere; }
162
+ .studio-ui .studio-schema-fields dd { margin: 0; min-width: 0; }
163
+ .studio-ui .studio-schema-list { padding-inline-start: 20px; }
164
+ .studio-ui .studio-schema-meta { color: var(--studio-muted); font-size: 12px; }
165
+ .studio-ui .studio-row button[aria-pressed="true"] { background: var(--studio-hover); border-color: var(--studio-ink); }
166
+ .studio-ui .studio-tool-description { white-space: pre-wrap; overflow-wrap: anywhere; }
167
+ .studio-ui .studio-tool-card strong { font-weight: 500; }
168
+ .studio-ui .studio-comparison { margin-top: 24px; }
169
+ .studio-ui .studio-comparison-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: var(--studio-gap); align-items: start; }
170
+ .studio-ui .studio-comparison-grid > section { min-width: 0; }
171
+ .studio-ui .studio-comparison h4 { font-size: 14px; font-weight: 500; margin: 8px 0; }
172
+ .studio-ui .studio-comparison-panel { border: 1px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); padding: 6px; overflow: hidden; }
173
+ .studio-ui .studio-comparison-native-row { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto 12px; gap: 12px; padding: 12px; align-items: center; border-radius: var(--studio-radius); }
174
+ .studio-ui .studio-comparison-native-row:first-child { background: var(--studio-hover); }
175
+ .studio-ui .studio-comparison-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--studio-success); align-self: start; margin-top: 8px; }
176
+ .studio-ui .studio-comparison-label > div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
177
+ .studio-ui .studio-comparison-secondary, .studio-ui .studio-comparison-metrics, .studio-ui .studio-comparison-chevron { color: var(--studio-muted); }
178
+ .studio-ui .studio-comparison-metrics { text-align: right; font-size: 12px; white-space: nowrap; }
179
+ .studio-ui .studio-comparison-chevron { font-size: 20px; }
180
+ .studio-ui .studio-comparison-panel .studio-catalog-row { padding: 10px; }
181
+ .studio-ui .studio-comparison-panel .studio-catalog-row:first-child { border-top: 0; }
182
+ .studio-ui .studio-comparison-snapshot { background: var(--studio-inset); border-radius: var(--studio-small-radius); padding: 8px; margin-top: 8px; }
183
+ .studio-ui .studio-comparison-navigation { color: var(--studio-muted); }
184
+ .studio-ui.studio-catalog { width: min(336px, calc(100vw - 32px)); max-height: 65vh; overflow: auto; padding: 8px; }
185
+ .studio-ui .studio-catalog-row { padding: 8px 0; border-top: 1px solid var(--studio-line); }
186
+ .studio-ui .studio-catalog-row p { margin: 4px 0; }
187
+ .studio-ui .studio-catalog-meta { color: var(--studio-muted); font-size: 12px; }
188
+ .studio-ui .studio-catalog-branch .studio-catalog-branch { margin-left: 12px; padding-left: 8px; border-left: 1px solid var(--studio-line); }
189
+ .studio-ui .studio-catalog-task, .studio-ui .studio-catalog-row pre { white-space: pre-wrap; overflow-wrap: anywhere; }
190
+ .studio-ui .studio-catalog-expand { color: var(--studio-ink); background: transparent; border: 0; border-radius: var(--studio-small-radius); padding: 4px 8px; font: inherit; cursor: pointer; }
191
+ .studio-ui .studio-catalog-expand:hover { background: var(--studio-hover); }
192
+ .studio-ui .studio-catalog-expand:focus-visible { outline: 2px solid var(--studio-focus); }
193
+ .studio-ui.studio-catalog-launch fieldset { border: 0; padding: 0; margin: 0 0 12px; }
194
+ .studio-ui.studio-catalog-launch textarea { min-height: 100px; }
195
+
196
+ .studio-ui h1, .studio-ui h2, .studio-ui h3, .studio-ui h4, .studio-ui strong, .studio-ui legend { font-weight: 500; }
197
+ .studio-ui h1 { font-size: 28px; line-height: 1.3; margin: 8px 0 12px; }
198
+ .studio-ui h2 { font-size: 18px; line-height: 1.4; margin: 0 0 12px; }
199
+ .studio-ui h3 { font-size: 14px; line-height: 1.5; margin: 16px 0 8px; }
200
+ .studio-ui h4 { font-size: 13px; line-height: 1.5; margin: 0; }
201
+ .studio-ui p { margin: 8px 0 16px; }
202
+ .studio-ui .studio-muted, .studio-ui dt { color: var(--studio-muted); }
203
+ .studio-ui .studio-hint { font-size: 12px; margin: 0; }
204
+ .studio-ui .studio-grow { flex: 1 1 auto; min-width: 0; }
205
+ .studio-ui .studio-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
206
+
207
+ /* Page frame: navigation and content scroll independently, so selecting a section never moves the navigation. */
208
+ .studio-ui.studio-shell { height: 100%; overflow: hidden; container: studio-shell / inline-size; }
209
+ .studio-ui .studio-page { height: 100%; background: var(--studio-bg); }
210
+ .studio-ui .studio-services { display: flex; flex-direction: column; height: 100%; min-height: 0; }
211
+ .studio-ui .studio-service-nav { display: flex; flex: 0 0 auto; gap: 8px; padding: 12px 16px; overflow-x: auto; border-bottom: .5px solid var(--studio-line); }
212
+ .studio-ui .studio-service-nav .studio-nav-item { white-space: nowrap; flex-shrink: 0; }
213
+ .studio-ui .studio-service-panel { flex: 1; min-height: 0; overflow-y: auto; }
214
+ .studio-ui .studio-service-panel[hidden] { display: none; }
215
+ .studio-ui .studio-service-panel > .studio-ui { max-width: 960px; margin: 0 auto; padding: 24px clamp(16px, 3vw, 40px) 48px; }
216
+ .studio-ui .studio-layout { display: grid; grid-template-columns: 208px minmax(0, 1fr); height: 100%; }
217
+ .studio-ui nav.studio-nav { display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px; margin: 0; padding: 24px 12px; border-inline-end: .5px solid var(--studio-line); overflow-y: auto; }
218
+ .studio-ui .studio-nav-head { display: grid; gap: 2px; padding: 0 10px 8px; }
219
+ .studio-ui .studio-nav-title { font-size: 14px; }
220
+ .studio-ui .studio-nav-subtitle { color: var(--studio-subtle); font-size: 12px; }
221
+ .studio-ui .studio-nav-group, .studio-ui .studio-list-group { display: flex; align-items: center; gap: 6px; padding: 16px 10px 4px; color: var(--studio-subtle); font-size: 12px; }
222
+ .studio-ui .studio-nav-badge { padding: 0 6px; border: .5px solid var(--studio-control-line); border-radius: 999px; font-size: 11px; line-height: 16px; }
223
+ .studio-ui :is(.studio-nav-item, .studio-list-item) { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 32px; padding: 5px 10px; border: 0; border-radius: var(--studio-control-radius); background: none; color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
224
+ .studio-ui :is(.studio-nav-item, .studio-list-item):hover { background: var(--studio-hover); }
225
+ .studio-ui :is(.studio-nav-item, .studio-list-item)[aria-current] { background: var(--studio-hover); font-weight: 500; }
226
+ .studio-ui .studio-scroller { min-width: 0; overflow-y: auto; scrollbar-gutter: stable; container: studio-main / inline-size; }
227
+ .studio-ui .studio-content { max-width: 960px; margin: 0 auto; padding: 24px clamp(16px, 3vw, 40px) 48px; }
228
+ .studio-ui .studio-feature-stack { display: grid; gap: 24px; }
229
+ .studio-ui .studio-page-stack { display: grid; gap: var(--studio-gap); min-width: 0; }
230
+ .studio-ui .studio-page-stack > p, .studio-ui .studio-detail > p { margin: 0; }
231
+ .studio-ui .studio-page-head h2 { margin: 0 0 4px; }
232
+ .studio-ui .studio-page-head p { margin: 0; }
233
+ .studio-ui .studio-page-head + * { margin-top: 0; }
234
+ .studio-ui section > .studio-page-head { margin-bottom: var(--studio-gap); }
235
+ .studio-ui .studio-page-head-actions { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--studio-gap); }
236
+ .studio-ui .studio-status-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 0; }
237
+ .studio-ui .studio-note { margin: 0; padding: 6px 12px; border-radius: var(--studio-control-radius); background: var(--studio-hover); color: var(--studio-muted); font-size: 12px; }
238
+ .studio-ui .studio-section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
239
+ .studio-ui .studio-section-head :is(h3, h4, p) { margin: 0; }
240
+ .studio-ui .studio-section-head p { margin-top: 2px; }
241
+ .studio-ui .studio-section-head-field { align-items: flex-end; }
242
+ .studio-ui .studio-section-head-field > label { margin: 0; }
243
+ .studio-ui .studio-panel-title { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; }
244
+ .studio-ui h3.studio-section-title { margin: 0; font-size: 18px; }
245
+ .studio-ui .studio-block-section { display: grid; gap: 8px; }
246
+ .studio-ui .studio-block-section > :is(h3, p) { margin: 0; }
247
+
248
+ /* Buttons follow the DSH outline and primary variants. */
249
+ .studio-ui .studio-button { display: inline-flex; align-items: center; justify-content: center; gap: 4px; height: 32px; padding: 0 12px; margin: 2px 4px 2px 0; border: .5px solid var(--studio-control-line); border-radius: var(--studio-control-radius); background: transparent; color: var(--studio-ink); font: inherit; font-weight: 500; line-height: 20px; white-space: nowrap; cursor: pointer; }
250
+ .studio-ui .studio-button:hover:not(:disabled) { background: var(--studio-hover); }
251
+ .studio-ui .studio-button:active:not(:disabled) { background: var(--studio-pressed); }
252
+ .studio-ui .studio-button.studio-primary { border-color: transparent; background: var(--studio-primary-fill); color: var(--studio-on-primary); }
253
+ .studio-ui .studio-button.studio-primary:hover:not(:disabled) { background: var(--studio-primary-hover); }
254
+ .studio-ui .studio-button.studio-small { height: 28px; padding: 0 10px; font-size: 12px; line-height: 18px; border-radius: var(--studio-small-radius); }
255
+ .studio-ui [role=switch][aria-checked=true] { border-color: var(--studio-ink); }
256
+ .studio-ui .studio-inline-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
257
+ .studio-ui .studio-inline-actions .studio-button, .studio-ui.studio-modal-footer .studio-button { margin: 0; }
258
+ .studio-ui.studio-modal-footer { display: flex; justify-content: flex-end; gap: 8px; }
259
+ .studio-ui :is(button, input, textarea, select):focus-visible { outline: 2px solid var(--studio-focus, var(--studio-accent)); outline-offset: 2px; }
260
+ .studio-ui :is(button, input, textarea, select):disabled { cursor: not-allowed; opacity: .4; }
261
+
262
+ /* Tabs follow the Plugins page: tertiary labels with a primary underline. */
263
+ .studio-ui .studio-tabs { position: sticky; top: 0; z-index: 2; display: flex; align-items: flex-end; gap: 22px; margin: 0; padding-top: 8px; border-bottom: .5px solid var(--studio-line); background: var(--studio-bg); overflow-x: auto; scrollbar-width: none; }
264
+ .studio-ui .studio-tab { position: relative; display: inline-flex; align-items: center; gap: 6px; padding: 7px 1px 9px; border: 0; background: transparent; color: var(--studio-subtle); font: inherit; line-height: 20px; white-space: nowrap; cursor: pointer; }
265
+ .studio-ui .studio-tab:hover, .studio-ui .studio-tab[aria-selected=true] { color: var(--studio-ink); }
266
+ .studio-ui .studio-tab[aria-selected=true]::after { position: absolute; right: 0; bottom: 0; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: var(--studio-ink); content: ''; }
267
+ .studio-ui .studio-view-switch { max-width: 320px; }
268
+ .studio-ui .studio-tab-panel { min-height: calc(100vh - 48px); }
269
+
270
+ /* Forms. Field rules skip checkbox labels and inputs so host primitives such as Checkbox keep their own layout. */
271
+ .studio-ui fieldset { min-width: 0; margin: 16px 0; padding: 16px; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); }
272
+ .studio-ui legend { padding: 0 8px; font-size: 14px; line-height: 1.5; }
273
+ .studio-ui .studio-role-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: var(--studio-gap); }
274
+ .studio-ui label:not(:has(> input[type=checkbox])) { display: grid; gap: 6px; margin: 12px 0; }
275
+ .studio-ui label:not([class]):has(> input[type=checkbox]) { display: flex; align-items: center; gap: 8px; margin: 8px 0; }
276
+ /* A preset field label wraps its control inline; the fieldset ancestor outranks the grid label rule above. */
277
+ .studio-ui fieldset label.studio-inline-label { display: block; }
278
+ .studio-ui input:not([type=checkbox]), .studio-ui textarea, .studio-ui select { width: 100%; min-width: 0; padding: 6px 10px; border: .5px solid var(--studio-control-line); border-radius: var(--studio-control-radius); background: var(--studio-bg); color: var(--studio-ink); font: inherit; }
279
+ .studio-ui input:not([type=checkbox]), .studio-ui select { min-height: 32px; }
280
+ /* Native select arrows hug the edge; draw an inset chevron in the text color instead. */
281
+ .studio-ui select:not([multiple]) { appearance: none; -webkit-appearance: none; padding-inline-end: 34px; background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%); background-position: right 17px top 50%, right 12px top 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; text-overflow: ellipsis; }
282
+ .studio-ui textarea { min-height: 144px; resize: vertical; }
283
+ .studio-ui .studio-field { display: grid; gap: 6px; margin: 12px 0; }
284
+ .studio-ui .studio-field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 0 var(--studio-gap); }
285
+
286
+ /* Prompt assembly map and native multi-select transfer lists. */
287
+ .studio-ui .studio-prompt-workbench { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 20px; align-items: start; text-align: start; }
288
+ .studio-ui ol.studio-steps { margin: 0; padding-inline-start: 20px; }
289
+ .studio-ui .studio-page-stack > pre { margin: 0; }
290
+ .studio-ui .studio-page-stack > :is(.studio-field, label) { margin: 0; }
291
+ .studio-ui ol.studio-steps li { margin-bottom: 4px; }
292
+ .studio-ui ol.studio-prompt-stages { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
293
+ .studio-ui .studio-prompt-document { max-height: 680px; overflow-y: auto; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
294
+ .studio-ui .studio-prompt-section { display: grid; grid-template-columns: minmax(130px, 1fr) minmax(0, 3fr); gap: 18px; padding: 18px; border-bottom: .5px solid var(--studio-line); border-inline-start: 3px solid transparent; scroll-margin: 8px; }
295
+ .studio-ui .studio-prompt-section h3 { overflow-wrap: anywhere; }
296
+ .studio-ui .studio-prompt-section > p { margin: 0; min-width: 0; overflow-wrap: anywhere; }
297
+ .studio-ui .studio-prompt-tools { padding: 18px; }
298
+ @media (max-width: 600px) { .studio-ui .studio-prompt-section { grid-template-columns: 1fr; gap: 8px; } }
299
+ .studio-ui .studio-prompt-section[data-selected=true] { border-inline-start-color: var(--studio-ink); background: var(--studio-hover); }
300
+ .studio-ui .studio-prompt-section h3 { margin: 0; font-size: 14px; font-weight: 500; }
301
+ .studio-ui .studio-prompt-section textarea, .studio-ui .studio-prompt-section .studio-history-content { margin-top: 12px; }
302
+ @media (max-width: 800px) {
303
+ .studio-ui .studio-prompt-workbench { grid-template-columns: minmax(0, 1fr); }
304
+ .studio-ui ol.studio-steps { margin: 0; padding-inline-start: 20px; }
305
+ .studio-ui .studio-page-stack > pre { margin: 0; }
306
+ .studio-ui .studio-page-stack > :is(.studio-field, label) { margin: 0; }
307
+ .studio-ui ol.studio-steps li { margin-bottom: 4px; }
308
+ .studio-ui ol.studio-prompt-stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
309
+ }
310
+ .studio-ui .studio-prompt-stages .studio-pick { width: 100%; height: 100%; display: grid; gap: 6px; text-align: start; }
311
+ .studio-ui .studio-prompt-stages [aria-current=step] { border-color: var(--studio-ink); background: var(--studio-hover); }
312
+ .studio-ui .studio-tool-transfer { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 16px; }
313
+ .studio-ui .studio-transfer-column { min-width: 0; border: 1px solid var(--studio-line); border-radius: var(--studio-radius); }
314
+ .studio-ui .studio-transfer-items { display: grid; gap: 10px; max-height: 460px; overflow-y: auto; padding: 8px; }
315
+ .studio-ui .studio-transfer-actions { display: flex; flex-direction: column; justify-content: center; gap: 10px; }
316
+ .studio-ui .studio-prompt-stages { display: grid; gap: 6px; }
317
+ .studio-ui .studio-tool-transfer select { min-height: 260px; }
318
+ .studio-ui .studio-tool-transfer option { padding: 6px; }
319
+ .studio-ui .studio-transfer-actions { display: flex; flex-direction: column; justify-content: center; gap: 8px; }
320
+ @media (max-width: 640px) {
321
+ .studio-ui .studio-tool-transfer { grid-template-columns: minmax(0, 1fr); }
322
+ .studio-ui .studio-transfer-actions { flex-direction: row; }
323
+ }
324
+
325
+ /* Existing Studio surfaces. */
326
+ .studio-ui .studio-card, .studio-ui article { padding: 20px; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); min-width: 0; }
327
+ .studio-ui article + article { margin-top: var(--studio-gap); }
328
+ .studio-ui .studio-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--studio-gap); }
329
+ .studio-ui .studio-actions, .studio-ui nav { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--studio-gap); }
330
+ .studio-ui dl { display: grid; grid-template-columns: minmax(80px, 1fr) minmax(0, 3fr); gap: 8px 16px; margin: 16px 0; }
331
+ .studio-ui dd { margin: 0; min-width: 0; }
332
+ .studio-ui [role=alert] { margin: 0; padding: 8px 12px; border-inline-start: 2px solid var(--studio-error); color: var(--studio-error); }
333
+ .studio-ui [role=status] { color: var(--studio-muted); }
334
+ .studio-ui pre { max-width: 100%; overflow: auto; padding: 12px; border-radius: 8px; background: var(--studio-inset); font-size: 12px; line-height: 1.6; }
335
+ .studio-ui .studio-history-content { white-space: pre-wrap; overflow-wrap: anywhere; }
336
+ .studio-ui ol, .studio-ui ul { padding-inline-start: 20px; }
337
+ .studio-ui li { margin-bottom: 8px; }
338
+ .studio-ui .studio-agents { padding: 20px; }
339
+ .studio-ui .studio-timer { padding: 20px; border-radius: var(--studio-radius); background: var(--studio-inset); }
340
+ .studio-ui .studio-timer strong { font-size: 28px; line-height: 1.4; font-variant-numeric: tabular-nums; }
341
+ .studio-ui .studio-timer span { font-size: 13px; line-height: 1.6; }
342
+
343
+ /* Design mock: cards, lists, and tables. */
344
+ .studio-ui .studio-inline-state { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
345
+ .studio-ui .studio-notice-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
346
+ .studio-ui .studio-cards { display: grid; gap: 8px; }
347
+ .studio-ui .studio-component { min-width: 0; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
348
+ .studio-ui .studio-component-flat { padding: 4px 16px 16px; }
349
+ .studio-ui .studio-component-head { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px; padding: 12px 16px; border: 0; border-radius: var(--studio-radius); background: none; color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
350
+ .studio-ui .studio-component-head[aria-expanded=true] { border-radius: var(--studio-radius) var(--studio-radius) 0 0; }
351
+ .studio-ui .studio-component-head:hover { background: var(--studio-hover); }
352
+ .studio-ui .studio-component-title { flex: 0 0 auto; font-size: 14px; font-weight: 500; }
353
+ .studio-ui .studio-component-summary { flex: 1 1 auto; min-width: 0; overflow: hidden; color: var(--studio-muted); text-overflow: ellipsis; white-space: nowrap; }
354
+ .studio-ui .studio-chevron { flex: 0 0 auto; color: var(--studio-muted); }
355
+ .studio-ui .studio-chevron[data-open=true] { transform: rotate(180deg); }
356
+ .studio-ui .studio-component-body { padding: 4px 16px 16px; border-top: .5px solid var(--studio-line); }
357
+ .studio-ui .studio-reason { margin: 12px 0; padding: 8px 12px; border-radius: var(--studio-control-radius); background: var(--studio-hover); color: var(--studio-muted); }
358
+ .studio-ui .studio-reason-warning { border-inline-start: 2px solid var(--studio-subtle); color: var(--studio-ink); }
359
+ .studio-ui .studio-effect { margin-top: 16px; padding-top: 12px; border-top: .5px solid var(--studio-line); }
360
+ .studio-ui .studio-effect span { color: var(--studio-muted); font-size: 12px; }
361
+ .studio-ui .studio-effect p { margin: 4px 0 0; }
362
+ .studio-ui .studio-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
363
+ .studio-ui .studio-card-actions .studio-button { margin: 0; }
364
+ .studio-ui .studio-card-actions:empty { display: none; }
365
+ .studio-ui .studio-blocks { margin: 0 0 8px; padding: 0; list-style: none; }
366
+ .studio-ui .studio-block { margin: 0; padding: 12px 0; border-bottom: .5px solid var(--studio-line); }
367
+ .studio-ui .studio-block-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; }
368
+ .studio-ui .studio-block-name { flex: 1 1 200px; margin: 0; }
369
+ .studio-ui .studio-block textarea { min-height: 72px; }
370
+ .studio-ui .studio-tool-groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); gap: 8px var(--studio-gap); }
371
+ .studio-ui .studio-tool-groups fieldset { display: grid; align-content: start; gap: 8px; margin: 8px 0 0; padding: 8px 12px 12px; }
372
+ .studio-ui .studio-tool-groups legend { color: var(--studio-muted); font-size: 13px; }
373
+ .studio-ui .studio-targets { display: grid; gap: 8px; margin: 12px 0; }
374
+ .studio-ui .studio-target { padding: 8px 12px; border: .5px solid var(--studio-line); border-radius: var(--studio-control-radius); }
375
+ .studio-ui .studio-target-body { padding-inline-start: 26px; }
376
+ .studio-ui .studio-target .studio-field { margin: 8px 0; }
377
+ .studio-ui .studio-tile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); gap: 8px; }
378
+ .studio-ui .studio-tile { display: grid; gap: 2px; padding: 12px 14px; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
379
+ .studio-ui .studio-tile:hover { background: var(--studio-hover); }
380
+ .studio-ui .studio-tile-title { display: flex; align-items: center; gap: 6px; font-weight: 500; }
381
+ .studio-ui .studio-plain-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
382
+ .studio-ui .studio-plain-list > li { margin: 0; }
383
+ .studio-ui .studio-list-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: .5px solid var(--studio-line); }
384
+ .studio-ui .studio-system-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); gap: 8px; margin: 0; padding: 0; list-style: none; }
385
+ .studio-ui .studio-system-grid > li { margin: 0; }
386
+ .studio-ui .studio-system { display: grid; gap: 4px; padding: 16px; }
387
+ .studio-ui .studio-system > p { margin: 0; }
388
+ .studio-ui .studio-facts { grid-template-columns: minmax(96px, 140px) minmax(0, 1fr); gap: 6px 12px; margin: 8px 0 0; }
389
+ .studio-ui .studio-master-detail { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; align-items: start; }
390
+ .studio-ui nav.studio-list { position: sticky; top: 0; display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px; margin: 0; }
391
+ .studio-ui .studio-list-group:first-child { padding-top: 0; }
392
+ .studio-ui .studio-list-text { display: grid; min-width: 0; }
393
+ .studio-ui .studio-list-text .studio-muted { font-size: 12px; font-weight: 400; }
394
+ .studio-ui .studio-detail { display: grid; gap: 12px; min-width: 0; }
395
+ .studio-ui .studio-detail > label { margin: 0; }
396
+ .studio-ui .studio-matrix-scroll { overflow-x: auto; }
397
+ .studio-ui .studio-matrix { width: 100%; border-collapse: collapse; }
398
+ .studio-ui .studio-matrix :is(th, td) { padding: 6px; border-bottom: .5px solid var(--studio-line); font-weight: 500; text-align: start; vertical-align: middle; }
399
+ .studio-ui .studio-matrix thead th { color: var(--studio-muted); font-size: 12px; }
400
+ .studio-ui .studio-matrix-cell { display: grid; align-content: center; width: 100%; min-height: 44px; padding: 6px 10px; border: 0; border-radius: var(--studio-control-radius); background: none; color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
401
+ .studio-ui .studio-matrix-cell:hover { background: var(--studio-hover); }
402
+ .studio-ui .studio-matrix-cell .studio-muted { font-size: 12px; }
403
+ .studio-ui .studio-checklist { margin: 0 0 12px; padding: 0; list-style: none; }
404
+ .studio-ui .studio-checklist li { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
405
+ .studio-ui .studio-simulate { margin: 16px 0 0; padding-top: 12px; border-top: .5px dashed var(--studio-line); }
406
+ .studio-ui .studio-pick-list { display: grid; gap: 6px; margin: 0 0 12px; padding: 0; list-style: none; }
407
+ .studio-ui .studio-pick-list > li { margin: 0; }
408
+ .studio-ui .studio-pick { display: grid; gap: 2px; width: 100%; padding: 10px 12px; border: .5px solid var(--studio-control-line); border-radius: var(--studio-control-radius); background: transparent; color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
409
+ .studio-ui .studio-pick:hover { background: var(--studio-hover); }
410
+ .studio-ui .studio-pick[aria-checked=true] { border-color: var(--studio-ink); background: var(--studio-hover); }
411
+ .studio-ui .studio-pick-title { display: flex; align-items: center; gap: 6px; }
412
+
413
+ /* Shared form rhythm: stacks own their spacing so paragraphs and fields do not add stray margins. */
414
+ .studio-ui .studio-stack { display: grid; gap: 12px; min-width: 0; }
415
+ .studio-ui .studio-stack > :is(p, label, .studio-field, .studio-note, fieldset) { margin: 0; }
416
+ .studio-ui .studio-form-section { display: grid; gap: 12px; min-width: 0; }
417
+ .studio-ui .studio-form-section > :is(p, label, .studio-field, .studio-note) { margin: 0; }
418
+ .studio-ui .studio-field .studio-field-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 8px; min-height: 28px; }
419
+ .studio-ui .studio-field .studio-field-head > label { display: block; margin: 0; }
420
+ .studio-ui .studio-field .studio-field-head .studio-button { margin: 0; }
421
+ .studio-ui .studio-field > .studio-hint { margin-top: -2px; }
422
+ .studio-ui .studio-count { margin-inline-start: 6px; color: var(--studio-subtle); font-size: 12px; font-weight: 400; }
423
+ .studio-ui .studio-button[aria-pressed="true"] { background: var(--studio-hover); border-color: var(--studio-ink); }
424
+
425
+ /* Status badges mirror the DSH Tag capsule: tinted fill, matching text, fixed geometry. */
426
+ .studio-ui .studio-badge { display: inline-flex; align-items: center; flex: 0 0 auto; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 500; line-height: 17px; white-space: nowrap; background: var(--studio-neutral-fill); color: var(--studio-muted); }
427
+ .studio-ui .studio-badge[data-tone=success] { background: color-mix(in srgb, var(--studio-success) 10%, transparent); color: var(--studio-success); }
428
+ .studio-ui .studio-badge[data-tone=warning] { background: color-mix(in srgb, var(--studio-warn) 12%, transparent); color: var(--studio-warn); }
429
+ .studio-ui .studio-badge[data-tone=info] { background: color-mix(in srgb, var(--studio-info) 10%, transparent); color: var(--studio-info); }
430
+ .studio-ui .studio-badge[data-tone=outline] { background: transparent; border: .5px solid var(--studio-control-line); color: var(--studio-subtle); }
431
+
432
+ /* Editor tabs and panels. */
433
+ .studio-ui .studio-editor-tabs { margin-top: 4px; }
434
+ .studio-ui .studio-tab-body { display: grid; align-content: start; gap: 24px; padding-top: 20px; min-width: 0; min-height: 70vh; }
435
+ .studio-ui .studio-tab-body[hidden] { display: none; }
436
+ .studio-ui .studio-tab-body > :is(p, .studio-note) { margin: 0; }
437
+ .studio-ui .studio-tab-body .studio-page-head h2 { font-size: 15px; }
438
+
439
+ /* Preset and role cards. */
440
+ .studio-ui .studio-tool-cards > article + article, .studio-ui .studio-transfer-items > article + article { margin-top: 0; }
441
+ .studio-ui article.studio-preset-card, .studio-ui article.studio-role-card { display: flex; flex-direction: column; gap: 6px; padding: 16px; }
442
+ .studio-ui .studio-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
443
+ .studio-ui .studio-card-head :is(h3, strong) { margin: 0; font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
444
+ .studio-ui .studio-card-head > :first-child { margin-inline-end: auto; }
445
+ .studio-ui .studio-card-text { margin: 0; color: var(--studio-muted); }
446
+ .studio-ui .studio-card-meta { margin: 0; color: var(--studio-subtle); font-size: 12px; }
447
+ .studio-ui .studio-card-footer { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: auto; padding-top: 10px; }
448
+ .studio-ui .studio-card-footer .studio-button { margin: 0; }
449
+ .studio-ui .studio-card-note { margin-top: auto; padding-top: 10px; }
450
+ .studio-ui .studio-card-footer .studio-card-footer-end { margin-inline-start: auto; }
451
+ .studio-ui .studio-tool-cards.studio-card-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
452
+ .studio-ui .studio-preset-warnings { display: flex; flex-direction: column; gap: 6px; margin: 0 0 12px; padding: 0; list-style: none; }
453
+ .studio-ui .studio-preset-warnings > li { display: flex; align-items: baseline; gap: 8px; padding: 6px 12px; border-radius: var(--studio-control-radius); background: var(--studio-hover); color: var(--studio-muted); font-size: 12px; }
454
+
455
+ /* Selection columns: a quiet header row and compact cards. */
456
+ .studio-ui .studio-transfer-column { display: flex; flex-direction: column; background: var(--studio-surface); }
457
+ .studio-ui .studio-transfer-column > .studio-section-head { padding: 10px 14px; border-bottom: .5px solid var(--studio-line); }
458
+ .studio-ui .studio-transfer-column > .studio-section-head .studio-panel-title { font-size: 13px; }
459
+ .studio-ui .studio-transfer-items { padding: 10px; gap: 8px; }
460
+ .studio-ui .studio-transfer-items > p { margin: 4px; }
461
+ .studio-ui .studio-transfer-items > .studio-card { display: grid; gap: 6px; padding: 12px 14px; background: var(--studio-bg); }
462
+ .studio-ui .studio-transfer-items > .studio-card > p { margin: 0; }
463
+ .studio-ui .studio-transfer-items > .studio-card .studio-panel-title { font-size: 13px; overflow-wrap: anywhere; }
464
+ .studio-ui .studio-transfer-items > .studio-card .studio-section-head p { font-size: 12px; }
465
+ .studio-ui .studio-selection-detail { display: grid; gap: 12px; margin-top: 4px; }
466
+ .studio-ui .studio-selection-detail > :is(p, label, .studio-field, .studio-note, section) { margin: 0; }
467
+ .studio-ui .studio-selection-detail .studio-section-head { margin-top: 8px; }
468
+
469
+ /* Config form: root object has no frame; nested objects use a quiet rule; each field reads label \u2192 state \u2192 input. */
470
+ .studio-ui fieldset.studio-config-root { margin: 4px 0 0; padding: 0; border: 0; }
471
+ .studio-ui fieldset.studio-config-root fieldset { margin: 8px 0; padding: 4px 0 4px 12px; border: 0; border-inline-start: .5px solid var(--studio-control-line); border-radius: 0; }
472
+ .studio-ui fieldset.studio-config-root legend { padding: 0; color: var(--studio-muted); font-size: 12px; }
473
+ .studio-ui .studio-config-field { margin: 10px 0; }
474
+ .studio-ui .studio-config-list { display: grid; }
475
+ .studio-ui .studio-config-list .studio-list-row { padding: 4px 0; }
476
+ .studio-ui .studio-config-list .studio-list-row:last-child { border-bottom: 0; }
477
+ .studio-ui .studio-config-list .studio-list-row input { flex: 1; min-width: 0; }
478
+ .studio-ui .studio-config-list .studio-list-row .studio-config-item { flex: 1; min-width: 0; margin: 0; padding: 0 0 0 2px; border: 0; }
479
+ .studio-ui .studio-config-key { flex: 0 0 auto; min-width: 0; overflow-wrap: anywhere; color: var(--studio-muted); }
480
+ .studio-ui .studio-config-state { color: var(--studio-subtle); font-size: 12px; margin-inline-start: auto; }
481
+ .studio-ui .studio-config-field[data-overridden=true] .studio-config-state { color: var(--studio-info); }
482
+
483
+ /* Collapsible advanced block, styled like a component card. */
484
+ .studio-ui details.studio-disclosure { border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
485
+ .studio-ui details.studio-disclosure > summary { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-radius: var(--studio-radius); cursor: pointer; list-style: none; font-weight: 500; }
486
+ .studio-ui details.studio-disclosure > summary::-webkit-details-marker { display: none; }
487
+ .studio-ui details.studio-disclosure > summary::after { content: ''; width: 6px; height: 6px; margin-inline-start: auto; border-right: 1.3px solid var(--studio-muted); border-bottom: 1.3px solid var(--studio-muted); transform: rotate(45deg); }
488
+ .studio-ui details.studio-disclosure[open] > summary::after { transform: rotate(-135deg); }
489
+ .studio-ui details.studio-disclosure > summary:hover { background: var(--studio-hover); }
490
+ .studio-ui details.studio-disclosure > summary:focus-visible { outline: 2px solid var(--studio-focus); outline-offset: 2px; }
491
+ .studio-ui .studio-disclosure-summary { display: grid; gap: 0; }
492
+ .studio-ui .studio-disclosure-summary .studio-muted { font-size: 12px; font-weight: 400; }
493
+ .studio-ui .studio-disclosure-body { display: grid; gap: 12px; padding: 4px 16px 16px; border-top: .5px solid var(--studio-line); }
494
+ .studio-ui .studio-disclosure-body > :is(p, ol, pre, label, .studio-field, .studio-note) { margin: 0; }
495
+ .studio-ui .studio-disclosure-body ol { padding-top: 12px; }
496
+ .studio-ui .studio-disclosure-body li { margin-bottom: 4px; }
497
+
498
+ /* Dialog header and body spacing. */
499
+ .studio-ui .studio-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
500
+ .studio-ui .studio-dialog-head h2, .studio-ui .studio-dialog-head h3 { margin: 0; }
501
+ .studio-ui .studio-dialog-actions { justify-content: flex-end; }
502
+ .studio-ui .studio-selection-detail > h3 { margin: 0; font-size: 15px; }
503
+
504
+ /* Unified selection card: title row, optional mono subtitle, two-line excerpt, actions bottom-right. */
505
+ .studio-ui .studio-transfer-items { max-height: 520px; }
506
+ .studio-ui .studio-transfer-items > .studio-transfer-card { display: flex; flex-direction: column; gap: 4px; min-height: 0; padding: 12px 14px; }
507
+ .studio-ui .studio-transfer-card-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
508
+ .studio-ui .studio-transfer-card-head strong { flex: 1 1 auto; min-width: 0; overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
509
+ .studio-ui .studio-transfer-card-subtitle { overflow: hidden; color: var(--studio-subtle); text-overflow: ellipsis; white-space: nowrap; }
510
+ .studio-ui .studio-transfer-card .studio-card-excerpt { margin: 0; font-size: 12px; line-height: 1.55; }
511
+ .studio-ui .studio-transfer-card-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: auto; padding-top: 6px; }
512
+ .studio-ui .studio-transfer-card-actions .studio-button { margin: 0; }
513
+ .studio-ui .studio-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
514
+
515
+ /* Split button: main action joined to a caret menu. */
516
+ .studio-ui .studio-split { position: relative; display: inline-flex; }
517
+ .studio-ui .studio-split .studio-button { margin: 0; }
518
+ .studio-ui .studio-split[data-menu=true] > .studio-button:first-child { border-start-end-radius: 0; border-end-end-radius: 0; }
519
+ .studio-ui .studio-split .studio-split-caret { width: 26px; padding: 0; margin-inline-start: -.5px; border-start-start-radius: 0; border-end-start-radius: 0; }
520
+ .studio-ui .studio-split .studio-button.studio-primary.studio-split-caret { border-inline-start: .5px solid var(--studio-on-primary); }
521
+ .studio-ui .studio-split-menu { position: absolute; top: calc(100% + 4px); right: 0; z-index: 5; display: grid; min-width: 140px; padding: 4px; border: .5px solid var(--studio-line); border-radius: var(--studio-control-radius); background: var(--studio-bg); }
522
+ .studio-ui .studio-split-menu button { padding: 6px 10px; border: 0; border-radius: var(--studio-small-radius); background: none; color: var(--studio-ink); font: inherit; font-size: 12px; text-align: start; white-space: nowrap; cursor: pointer; }
523
+ .studio-ui .studio-split-menu button:hover { background: var(--studio-hover); }
524
+
525
+ /* Detail dialog: header, scrolling body, optional footer. */
526
+ .studio-ui .studio-detail-dialog { width: min(680px, calc(100vw - 48px)); max-height: calc(100dvh - 64px); overflow: hidden; }
527
+ .studio-ui .studio-detail-dialog[open] { display: flex; }
528
+ .studio-ui .studio-dialog { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; width: 100%; }
529
+ .studio-ui .studio-dialog > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px 14px; border-bottom: .5px solid var(--studio-line); }
530
+ .studio-ui .studio-dialog > header h2 { margin: 0; font-size: 15px; overflow-wrap: anywhere; }
531
+ .studio-ui .studio-dialog > header p { margin: 2px 0 0; font-size: 12px; }
532
+ .studio-ui .studio-dialog > header .studio-button { margin: 0; flex: 0 0 auto; }
533
+ .studio-ui .studio-dialog .studio-dialog-body { flex: 1 1 auto; min-height: 0; margin: 0; border: 0; border-radius: 0; padding: 16px 20px 20px; overflow-y: auto; }
534
+ .studio-ui .studio-dialog > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 20px; border-top: .5px solid var(--studio-line); }
535
+ .studio-ui .studio-dialog > footer .studio-button { margin: 0; }
536
+ .studio-ui .studio-dialog > footer .studio-button:first-child:not(:last-child) { margin-inline-end: auto; }
537
+ .studio-ui .studio-dialog-body .studio-field-grid .studio-field { margin: 0 0 12px; }
538
+ .studio-ui .studio-dialog-body fieldset.studio-config-root { margin: 0; }
539
+ .studio-ui .studio-dialog-body > .studio-button, .studio-ui .studio-dialog-body section > .studio-button { justify-self: start; margin: 0; }
540
+ .studio-ui .studio-dialog-body section { display: grid; gap: 8px; }
541
+ .studio-ui .studio-dialog-body section > .studio-field { margin: 0; }
542
+
543
+ /* Item picker: one list, rows stay in place when toggled; filters show selected or modified rows. */
544
+ .studio-ui .studio-picker { display: grid; gap: 10px; min-width: 0; }
545
+ .studio-ui .studio-picker-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
546
+ .studio-ui .studio-picker-bar input[type=search] { flex: 1 1 240px; width: auto; }
547
+ .studio-ui .studio-picker-filters { display: inline-flex; flex: 0 0 auto; padding: 2px; border-radius: var(--studio-control-radius); background: var(--studio-neutral-fill); }
548
+ .studio-ui .studio-picker-filter { display: inline-flex; align-items: center; height: 28px; padding: 0 10px; border: 0; border-radius: var(--studio-small-radius); background: none; color: var(--studio-muted); font: inherit; font-size: 12px; cursor: pointer; }
549
+ .studio-ui .studio-picker-filter[aria-checked=true] { background: var(--studio-bg); color: var(--studio-ink); font-weight: 500; }
550
+ .studio-ui .studio-picker-filter .studio-count { margin-inline-start: 4px; }
551
+ .studio-ui ul.studio-picker-list { display: grid; max-height: 480px; margin: 0; padding: 0; overflow-y: auto; list-style: none; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
552
+ .studio-ui .studio-picker-row { display: flex; align-items: center; gap: 8px; margin: 0; padding: 0 12px 0 0; border-bottom: .5px solid var(--studio-line); }
553
+ .studio-ui .studio-picker-row:last-child { border-bottom: 0; }
554
+ .studio-ui .studio-picker-row[data-selected=true] { background: var(--studio-hover); }
555
+ .studio-ui .studio-picker-row > .studio-button { margin: 0; flex: 0 0 auto; }
556
+ .studio-ui .studio-picker-toggle { display: flex; flex: 1 1 auto; align-items: flex-start; gap: 12px; min-width: 0; padding: 10px 4px 10px 14px; border: 0; background: none; color: var(--studio-ink); font: inherit; text-align: start; cursor: pointer; }
557
+ .studio-ui .studio-picker-toggle:focus-visible { outline-offset: -2px; }
558
+ .studio-ui .studio-picker-check { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 18px; height: 18px; margin-top: 1px; border: 1px solid var(--studio-control-line); border-radius: 5px; color: transparent; }
559
+ .studio-ui .studio-picker-toggle[aria-checked=true] .studio-picker-check { border-color: var(--studio-primary-fill); background: var(--studio-primary-fill); color: var(--studio-on-primary); }
560
+ .studio-ui .studio-picker-text { display: grid; gap: 2px; min-width: 0; }
561
+ .studio-ui .studio-picker-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; font-weight: 500; overflow-wrap: anywhere; }
562
+ .studio-ui .studio-picker-subtitle { color: var(--studio-subtle); font-weight: 400; }
563
+ .studio-ui .studio-picker-description { display: -webkit-box; overflow: hidden; color: var(--studio-muted); font-size: 12px; line-height: 1.5; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
564
+ .studio-ui .studio-picker-empty { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0; padding: 14px; color: var(--studio-muted); }
565
+ .studio-ui .studio-picker-empty .studio-button { margin: 0; }
566
+
567
+ /* Prompt preview: sections read as a document on the left, tool schemas sit in a right rail. */
568
+ .studio-ui .studio-detail-dialog.studio-dialog-compact { width: min(440px, calc(100vw - 48px)); }
569
+ .studio-ui .studio-dialog-compact .studio-dialog > header { border-bottom: 0; padding-bottom: 0; }
570
+ .studio-ui .studio-dialog-compact .studio-dialog .studio-dialog-body { padding-top: 10px; padding-bottom: 4px; }
571
+ .studio-ui .studio-dialog-compact .studio-dialog > footer { border-top: 0; padding-bottom: 18px; }
572
+ .studio-ui .studio-dialog-compact .studio-dialog > footer .studio-button:first-child:not(:last-child) { margin-inline-end: 0; }
573
+ /* Preset identity: one card, short name beside a wider description, quiet labels. */
574
+ .studio-ui .studio-identity { display: grid; grid-template-columns: minmax(200px, 2fr) minmax(0, 3fr); gap: 12px var(--studio-gap); padding: 14px 16px; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
575
+ .studio-ui .studio-identity > label.studio-field { margin: 0; gap: 4px; }
576
+ .studio-ui .studio-identity > label.studio-field > span { color: var(--studio-subtle); font-size: 12px; }
577
+ .studio-ui .studio-identity input:not([type=checkbox]) { min-height: 36px; background: var(--studio-bg); }
578
+ .studio-ui .studio-identity .studio-identity-name input { font-size: 15px; font-weight: 500; }
579
+ @media (max-width: 720px) { .studio-ui .studio-identity { grid-template-columns: minmax(0, 1fr); } }
580
+ .studio-ui .studio-detail-dialog.studio-dialog-wide { width: min(1180px, calc(100vw - 48px)); height: calc(100dvh - 64px); }
581
+ .studio-ui .studio-dialog-wide .studio-dialog-body { display: flex; flex-direction: column; padding-bottom: 16px; }
582
+ .studio-ui .studio-preview { display: grid; flex: 1 1 auto; grid-template-columns: minmax(0, 1fr) 300px; gap: 16px; min-height: 0; }
583
+ .studio-ui .studio-preview .studio-prompt-document { max-height: none; min-height: 0; overflow-y: auto; border: .5px solid var(--studio-line); background: var(--studio-bg); }
584
+ .studio-ui .studio-preview .studio-prompt-section { display: block; padding: 14px 18px; border-inline-start: 0; }
585
+ .studio-ui .studio-preview .studio-prompt-section h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; color: var(--studio-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; font-weight: 400; }
586
+ .studio-ui .studio-prompt-index { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--studio-neutral-fill); color: var(--studio-muted); font-family: inherit; font-size: 11px; }
587
+ .studio-ui .studio-preview .studio-prompt-section > p { font-size: 13px; line-height: 1.65; }
588
+ .studio-ui .studio-preview .studio-prompt-tools { min-height: 0; overflow-y: auto; padding: 12px; border: .5px solid var(--studio-line); border-radius: var(--studio-radius); background: var(--studio-surface); }
589
+ .studio-ui .studio-preview .studio-prompt-tools h3 { margin: 0 0 8px; font-size: 13px; }
590
+ .studio-ui .studio-preview .studio-schema-tool > summary { display: flex; align-items: center; gap: 8px; list-style: none; }
591
+ .studio-ui .studio-preview .studio-schema-tool > summary::-webkit-details-marker { display: none; }
592
+ .studio-ui .studio-preview .studio-schema-tool > summary::before { content: ''; width: 5px; height: 5px; border-right: 1.3px solid var(--studio-muted); border-bottom: 1.3px solid var(--studio-muted); transform: rotate(-45deg); }
593
+ .studio-ui .studio-preview .studio-schema-tool[open] > summary::before { transform: rotate(45deg); }
594
+ .studio-ui .studio-preview .studio-schema-tool { margin-top: 6px; padding: 8px 10px; background: var(--studio-bg); }
595
+ .studio-ui .studio-preview .studio-schema-tool summary { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
596
+ .studio-ui .studio-preview .studio-schema-tool p { font-size: 12px; }
597
+ .studio-ui .studio-preview .studio-schema-fields > div { grid-template-columns: minmax(60px, 90px) minmax(0, 1fr); font-size: 12px; }
598
+ @container studio-shell (max-width: 900px) { .studio-ui .studio-preview { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 2fr) minmax(0, 1fr); } }
599
+
600
+ /* Width rules follow the Studio surface and its content column, which the host sidebar and the Studio navigation narrow. */
601
+ @container studio-shell (max-width: 760px) {
602
+ .studio-ui .studio-page { overflow-y: auto; }
603
+ .studio-ui .studio-layout { grid-template-columns: 1fr; height: auto; }
604
+ .studio-ui nav.studio-nav { flex-direction: row; overflow-x: auto; padding: 8px 12px; border-inline-end: 0; border-bottom: .5px solid var(--studio-line); }
605
+ .studio-ui .studio-nav-title, .studio-ui .studio-nav-group { display: none; }
606
+ .studio-ui .studio-nav-item { flex: 0 0 auto; }
607
+ .studio-ui .studio-scroller { overflow: visible; }
608
+ }
609
+ @container studio-main (max-width: 560px) {
610
+ .studio-ui .studio-tool-cards.studio-card-pair { grid-template-columns: minmax(0, 1fr); }
611
+ }
612
+ @container studio-main (max-width: 640px) {
613
+ .studio-ui .studio-master-detail { grid-template-columns: 1fr; }
614
+ .studio-ui nav.studio-list { position: static; flex-direction: row; flex-wrap: wrap; }
615
+ .studio-ui .studio-list-group { display: none; }
616
+ .studio-ui .studio-component-summary { display: none; }
617
+ .studio-ui .studio-facts { grid-template-columns: 1fr; gap: 4px; }
618
+ }
619
+ @media (max-width: 480px) {
620
+ .studio-ui .studio-card, .studio-ui article, .studio-ui .studio-agents { padding: 12px; }
621
+ .studio-ui dl { grid-template-columns: 1fr; gap: 4px; }
622
+ .studio-ui dd { margin-bottom: 8px; }
623
+ }
624
+ `;
625
+
626
+ // src/ui.tsx
627
+ var import_jsx_runtime = require("react/jsx-runtime");
628
+ function StudioSurface({ children, className = "" }) {
629
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `studio-ui ${className}`, children: [
630
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: studioStyles }),
631
+ children
632
+ ] });
633
+ }
634
+ function Button({ primary = false, small = false, className = "", type = "button", ...props }) {
635
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...props, type, className: `studio-button ${primary ? "studio-primary" : ""} ${small ? "studio-small" : ""} ${className}` });
636
+ }
637
+ function ChangeIndicator({ locale }) {
638
+ const labels = { ko: "\uBCC0\uACBD\uB428", en: "Modified", zh: "\u5DF2\u4FEE\u6539" };
639
+ const text = labels[locale.startsWith("ko") ? "ko" : locale.startsWith("zh") ? "zh" : "en"];
640
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "studio-schema-meta", "data-studio-modified": "true", children: text });
641
+ }
642
+ function PageHead({ title, description, actions, children }) {
643
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: `studio-page-head ${actions ? "studio-page-head-actions" : ""}`, children: [
644
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
645
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }),
646
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "studio-muted", children: description }),
647
+ children
648
+ ] }),
649
+ actions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "studio-inline-actions", children: actions })
650
+ ] });
651
+ }
652
+ function SectionHead({ title, description, actions, field: field2 = false }) {
653
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `studio-section-head ${field2 ? "studio-section-head-field" : ""}`, children: [
654
+ description ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
655
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "studio-panel-title", children: title }),
656
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "studio-muted", children: description })
657
+ ] }) : typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "studio-panel-title", children: title }) : title,
658
+ actions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "studio-inline-actions", children: actions })
659
+ ] });
660
+ }
661
+ function Note({ children }) {
662
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "studio-note", role: "note", children });
663
+ }
664
+ function Notice({ children, action }) {
665
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "alert", className: "studio-notice-row", children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children }),
667
+ action
668
+ ] });
669
+ }
670
+ function Badge({ tone = "neutral", children }) {
671
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "studio-badge", "data-tone": tone, children });
672
+ }
673
+ function SplitButton({ label, onClick, primary = false, disabled = false, actions = [], menuLabel }) {
674
+ const [open, setOpen] = import_react.default.useState(false);
675
+ const root = import_react.default.useRef(null);
676
+ const menuId = import_react.default.useId();
677
+ import_react.default.useEffect(() => {
678
+ if (!open) return;
679
+ const close = (event) => {
680
+ if (!root.current?.contains(event.target)) setOpen(false);
681
+ };
682
+ const escape = (event) => {
683
+ if (event.key === "Escape") setOpen(false);
684
+ };
685
+ document.addEventListener("pointerdown", close);
686
+ document.addEventListener("keydown", escape);
687
+ return () => {
688
+ document.removeEventListener("pointerdown", close);
689
+ document.removeEventListener("keydown", escape);
690
+ };
691
+ }, [open]);
692
+ const menu = actions.length > 0;
693
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { ref: root, className: "studio-split", "data-menu": menu, children: [
694
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { small: true, primary, disabled, onClick, children: label }),
695
+ menu && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { small: true, primary, className: "studio-split-caret", disabled, "aria-label": menuLabel, "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((value) => !value), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
696
+ menu && open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { id: menuId, role: "menu", className: "studio-split-menu", children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "menuitem", disabled: action.disabled, onClick: () => {
697
+ setOpen(false);
698
+ action.onSelect();
699
+ }, children: action.label }, action.label)) })
700
+ ] });
701
+ }
702
+ function DetailDialog({ open, title, subtitle, onClose, closeLabel, footer, wide = false, compact = false, children }) {
703
+ const ref = import_react.default.useRef(null);
704
+ const opener = import_react.default.useRef(null);
705
+ const titleId = import_react.default.useId();
706
+ import_react.default.useEffect(() => {
707
+ const node = ref.current;
708
+ if (!node) return;
709
+ if (open && !node.open) {
710
+ opener.current = document.activeElement;
711
+ if (typeof node.showModal === "function") node.showModal();
712
+ else node.setAttribute("open", "");
713
+ } else if (!open && node.open) {
714
+ if (typeof node.close === "function") node.close();
715
+ else node.removeAttribute("open");
716
+ opener.current?.focus?.();
717
+ }
718
+ }, [open]);
719
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dialog", { ref, className: `studio-detail-dialog ${wide ? "studio-dialog-wide" : compact ? "studio-dialog-compact" : ""}`, "aria-labelledby": titleId, onCancel: (event) => {
720
+ event.preventDefault();
721
+ onClose();
722
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "studio-dialog", children: [
723
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: titleId, children: title }),
726
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "studio-muted", children: subtitle })
727
+ ] }),
728
+ closeLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { small: true, onClick: onClose, children: closeLabel })
729
+ ] }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "studio-dialog-body studio-selection-detail", children }),
731
+ footer && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("footer", { children: footer })
732
+ ] }) });
733
+ }
734
+
735
+ // src/role-editor.tsx
736
+ var import_react2 = __toESM(require("react"), 1);
737
+ var import_jsx_runtime2 = require("react/jsx-runtime");
738
+ function RoleEditor({ locale, roles, onChange, loadModels, defaultPersona = "" }) {
739
+ const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
740
+ const [selected, setSelected] = import_react2.default.useState();
741
+ const [catalog, setCatalog] = import_react2.default.useState();
742
+ const [error, setError] = import_react2.default.useState("");
743
+ const [loading, setLoading] = import_react2.default.useState(false);
744
+ const refresh = import_react2.default.useCallback(async () => {
745
+ if (!loadModels) return;
746
+ setLoading(true);
747
+ setError("");
748
+ try {
749
+ setCatalog(await loadModels());
750
+ } catch (e) {
751
+ setError(String(e));
752
+ } finally {
753
+ setLoading(false);
754
+ }
755
+ }, [loadModels]);
756
+ import_react2.default.useEffect(() => {
757
+ void refresh();
758
+ }, [refresh]);
759
+ const models = (catalog?.groups ?? []).flatMap((group) => group.models.map((model2) => ({ ...model2, value: `${group.id}/${model2.id}`, label: `${group.name} \xB7 ${model2.name}` })));
760
+ const role = selected === void 0 ? void 0 : roles[selected];
761
+ const model = models.find((model2) => model2.value === role?.model);
762
+ const update2 = (patch) => onChange(roles.map((role2, index) => index === selected ? { ...role2, ...patch } : role2));
763
+ const field2 = (label, content) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "studio-field", children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
765
+ content
766
+ ] });
767
+ const close = () => setSelected(void 0);
768
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionHead, { title: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { className: "studio-panel-title", children: [
770
+ t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8", "Subagents", "\u5B50\u4EE3\u7406"),
771
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-count", children: roles.length })
772
+ ] }), description: t("\uBC14\uAFBC \uB0B4\uC6A9\uC740 \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uACE0 DSH\uC5D0 \uB2E4\uC2DC \uBC18\uC601\uD574\uC57C \uC801\uC6A9\uB429\uB2C8\uB2E4.", "Save the preset and reapply it to DSH after changes.", "\u4FEE\u6539\u540E\u9700\u4FDD\u5B58\u9884\u8BBE\u5E76\u91CD\u65B0\u5E94\u7528\u5230 DSH\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, disabled: loading || !loadModels, onClick: () => void refresh(), children: loading ? t("\uBAA8\uB378 \uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading models\u2026", "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u2026") : t("\uBAA8\uB378 \uBAA9\uB85D \uC0C8\uB85C\uACE0\uCE68", "Refresh models", "\u5237\u65B0\u6A21\u578B\u5217\u8868") }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, onClick: () => {
775
+ setSelected(roles.length);
776
+ onChange([...roles, { name: "", role: "", model: "inherit", reasoning: "inherit", spawn: "fresh", persona: "" }]);
777
+ }, children: t("\uC5ED\uD560 \uCD94\uAC00", "Add role", "\u6DFB\u52A0\u89D2\u8272") })
778
+ ] }) }),
779
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Note, { children: error }),
780
+ catalog?.failures.map((failure) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Note, { children: [
781
+ failure.name,
782
+ ": ",
783
+ failure.message
784
+ ] }, failure.name)),
785
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Note, { children: t(
786
+ "DSH\uC758 \uAE30\uBCF8 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uB3C4\uAD6C(subagent, subagent_fork, list_subagent_models)\uB97C subagent_role \uD558\uB098\uB85C \uD1B5\uD569\uD574 \uC81C\uACF5\uD569\uB2C8\uB2E4. \uAE30\uBCF8 \uB3C4\uAD6C\uB294 \uC790\uB3D9\uC73C\uB85C \uBE60\uC9C0\uACE0, subagent_role\uC758 \uC124\uBA85\uACFC \uC778\uC790\uB294 \uD504\uB86C\uD504\uD2B8\xB7\uB3C4\uAD6C \uD0ED\uC758 \uB3C4\uAD6C \uBAA9\uB85D\uC5D0\uC11C \uD655\uC778\uD558\uACE0 \uC218\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
787
+ "Studio provides the DSH default subagent tools (subagent, subagent_fork, list_subagent_models) as a single subagent_role tool. The default tools are removed automatically; review or edit subagent_role in the tool list on the Prompts & tools tab.",
788
+ "\u5C06 DSH \u9ED8\u8BA4\u5B50\u4EE3\u7406\u5DE5\u5177\uFF08subagent\u3001subagent_fork\u3001list_subagent_models\uFF09\u5408\u5E76\u4E3A\u4E00\u4E2A subagent_role \u5DE5\u5177\u63D0\u4F9B\u3002\u9ED8\u8BA4\u5DE5\u5177\u4F1A\u88AB\u81EA\u52A8\u79FB\u9664\uFF1B\u53EF\u5728\u201C\u63D0\u793A\u8BCD\u4E0E\u5DE5\u5177\u201D\u9875\u7684\u5DE5\u5177\u5217\u8868\u4E2D\u67E5\u770B\u6216\u7F16\u8F91 subagent_role\u3002"
789
+ ) }),
790
+ !roles.length && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-muted", role: "status", children: t("\uC815\uC758\uB41C \uC5ED\uD560\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 \uC0C1\uD0DC\uC5D0\uC11C\uB294 subagent_role\uC774 \uBD80\uBAA8\uC640 \uAC19\uC740 \uBAA8\uB378\xB7\uCD94\uB860\xB7\uC9C0\uCE68\uC73C\uB85C \uC0C8 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.", "No roles defined. subagent_role then starts a new subagent with the same model, reasoning and instructions as the parent.", "\u5C1A\u672A\u5B9A\u4E49\u89D2\u8272\u3002\u6B64\u65F6 subagent_role \u4F1A\u4EE5\u4E0E\u7236\u4EE3\u7406\u76F8\u540C\u7684\u6A21\u578B\u3001\u63A8\u7406\u548C\u6307\u4EE4\u542F\u52A8\u65B0\u7684\u5B50\u4EE3\u7406\u3002") }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "studio-tool-cards studio-card-pair", children: roles.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "studio-role-card", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-card-head", children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: item.name || t("\uC774\uB984 \uC5C6\uC74C", "Untitled", "\u672A\u547D\u540D") }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { tone: "outline", children: item.spawn === "fork" ? t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30", "Continue parent", "\u7EE7\u627F\u7236\u5BF9\u8BDD") : t("\uC0C8 \uB300\uD654", "New conversation", "\u65B0\u5BF9\u8BDD") })
795
+ ] }),
796
+ item.role && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-card-text studio-mono", children: item.role }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "studio-card-meta", children: [
798
+ item.model === "inherit" ? t("\uBD80\uBAA8 \uBAA8\uB378 \uC0AC\uC6A9", "Inherit parent model", "\u7EE7\u627F\u7236\u7EA7\u6A21\u578B") : models.find((model2) => model2.value === item.model)?.label ?? item.model,
799
+ " \xB7 ",
800
+ t("\uCD94\uB860", "Reasoning", "\u63A8\u7406"),
801
+ " ",
802
+ item.reasoning === "inherit" ? t("\uAE30\uBCF8", "inherit", "\u9ED8\u8BA4") : item.reasoning
803
+ ] }),
804
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "studio-card-footer", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, "aria-haspopup": "dialog", onClick: () => setSelected(index), children: t("\uD3B8\uC9D1", "Edit", "\u7F16\u8F91") }) })
805
+ ] }, index)) }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
807
+ DetailDialog,
808
+ {
809
+ open: role !== void 0,
810
+ title: role?.name || t("\uC0C8 \uC5ED\uD560", "New role", "\u65B0\u89D2\u8272"),
811
+ subtitle: t("\uC5ED\uD560 \uD3B8\uC9D1", "Edit role", "\u7F16\u8F91\u89D2\u8272"),
812
+ onClose: close,
813
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
814
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, onClick: () => {
815
+ onChange(roles.filter((_, index) => index !== selected));
816
+ close();
817
+ }, children: t("\uC5ED\uD560 \uC0AD\uC81C", "Remove role", "\u79FB\u9664\u89D2\u8272") }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, primary: true, onClick: close, children: t("\uC644\uB8CC", "Done", "\u5B8C\u6210") })
819
+ ] }),
820
+ children: role && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-field-grid", children: [
822
+ field2(t("\uC774\uB984", "Name", "\u540D\u79F0"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: role.name, onChange: (e) => update2({ name: e.target.value }) })),
823
+ field2(t("\uC5ED\uD560 ID", "Role", "\u89D2\u8272"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "studio-mono", value: role.role, onChange: (e) => update2({ role: e.target.value }) }))
824
+ ] }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-field-grid", children: [
826
+ field2(t("\uBAA8\uB378", "Model", "\u6A21\u578B"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.model, onChange: (e) => update2({ model: e.target.value, reasoning: "inherit" }), children: [
827
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "inherit", children: t("\uBD80\uBAA8 \uBAA8\uB378 \uC0AC\uC6A9", "Inherit parent model", "\u7EE7\u627F\u7236\u7EA7\u6A21\u578B") }),
828
+ role.model !== "inherit" && !model && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: role.model, disabled: true, children: [
829
+ role.model,
830
+ " \xB7 ",
831
+ t("\uBAA9\uB85D\uC5D0 \uC5C6\uC74C \xB7 \uBC18\uC601\uD560 \uB54C \uD655\uC778", "Not confirmed in catalog \xB7 checked on apply", "\u76EE\u5F55\u4E2D\u672A\u786E\u8BA4 \xB7 \u5E94\u7528\u65F6\u9A8C\u8BC1")
832
+ ] }),
833
+ models.map((model2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: model2.value, children: model2.label }, model2.value))
834
+ ] })),
835
+ field2(t("\uCD94\uB860 \uC218\uC900", "Reasoning", "\u63A8\u7406\u7EA7\u522B"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.reasoning, disabled: !model, onChange: (e) => update2({ reasoning: e.target.value }), children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "inherit", children: role.model === "inherit" ? t("\uBD80\uBAA8 \uC124\uC815 \uC0AC\uC6A9", "Inherit parent setting", "\u7EE7\u627F\u7236\u7EA7\u8BBE\u7F6E") : t("\uBAA8\uB378 \uAE30\uBCF8\uAC12", "Model default", "\u6A21\u578B\u9ED8\u8BA4\u503C") }),
837
+ role.reasoning !== "inherit" && !model?.reasoning?.efforts.some((e) => e.id === role.reasoning) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: role.reasoning, disabled: true, children: [
838
+ role.reasoning,
839
+ " \xB7 ",
840
+ t("\uC9C0\uC6D0 \uC5EC\uBD80 \uD655\uC778 \uD544\uC694", "Check support", "\u9700\u786E\u8BA4\u652F\u6301")
841
+ ] }),
842
+ model?.reasoning?.efforts.map((e) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: e.id, children: e.name }, e.id))
843
+ ] })),
844
+ field2(t("\uC2DC\uC791 \uBC29\uC2DD", "Spawn mode", "\u751F\u6210\u65B9\u5F0F"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.spawn, onChange: (e) => update2({ spawn: e.target.value }), children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "fresh", children: t("\uC0C8 \uB300\uD654 (Fresh)", "New conversation (Fresh)", "\u65B0\u5BF9\u8BDD\uFF08Fresh\uFF09") }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "fork", children: t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30 (Fork)", "Continue parent (Fork)", "\u7EE7\u627F\u7236\u5BF9\u8BDD\uFF08Fork\uFF09") })
847
+ ] }))
848
+ ] }),
849
+ field2(t("\uC5ED\uD560 \uC9C0\uCE68 (persona)", "User instructions (persona)", "\u7528\u6237\u6307\u4EE4\uFF08persona\uFF09"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { rows: 6, value: role.persona, placeholder: defaultPersona, onChange: (e) => update2({ persona: e.target.value }) })),
850
+ !role.persona && defaultPersona && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-muted studio-hint", children: t("\uD750\uB9B0 \uAE00\uC528\uB294 \uD504\uB9AC\uC14B\uC758 \uAE30\uBCF8 persona\uC785\uB2C8\uB2E4. \uBE44\uC6CC \uB450\uBA74 \uC774 \uB0B4\uC6A9\uC774 \uADF8\uB300\uB85C \uC4F0\uC785\uB2C8\uB2E4.", "The dimmed text is the preset default persona; it is used while this field is empty.", "\u6D45\u8272\u6587\u5B57\u662F\u9884\u8BBE\u7684\u9ED8\u8BA4 persona\uFF0C\u7559\u7A7A\u65F6\u4F7F\u7528\u8BE5\u5185\u5BB9\u3002") })
851
+ ] })
852
+ }
853
+ )
854
+ ] });
855
+ }
856
+
857
+ // src/optimization-prototype.tsx
858
+ var import_react9 = __toESM(require("react"), 1);
859
+
860
+ // src/master-extensions-editor.tsx
861
+ var import_react3 = __toESM(require("react"), 1);
862
+ var import_jsx_runtime3 = require("react/jsx-runtime");
863
+ function MasterExtensionsEditor({ locale, actions }) {
864
+ const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
865
+ const [text, setText] = import_react3.default.useState("[]");
866
+ const [error, setError] = import_react3.default.useState("");
867
+ const [busy, setBusy] = import_react3.default.useState(false);
868
+ const [loaded, setLoaded] = import_react3.default.useState(false);
869
+ import_react3.default.useEffect(() => {
870
+ let active = true;
871
+ if (actions) actions.read().then((value) => {
872
+ if (active) {
873
+ setText(JSON.stringify(value, null, 2));
874
+ setLoaded(true);
875
+ }
876
+ }, (e) => {
877
+ if (active) setError(String(e));
878
+ });
879
+ return () => {
880
+ active = false;
881
+ };
882
+ }, [actions]);
883
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-page-stack", children: [
884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageHead, { title: t("\uB3C4\uAD6C \uC81C\uACF5\uC790", "Tool providers", "\u5DE5\u5177\u63D0\u4F9B\u8005"), description: t("\uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778\uC758 \uB3C4\uAD6C\uB97C Studio \uB3C4\uAD6C \uBAA9\uB85D\uC5D0 \uB4F1\uB85D\uD569\uB2C8\uB2E4. \uB4F1\uB85D\uD55C \uB3C4\uAD6C\uB294 \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Register installed plugin tools in the Studio tool list so presets can select them.", "\u5C06\u5DF2\u5B89\u88C5\u63D2\u4EF6\u7684\u5DE5\u5177\u6CE8\u518C\u5230 Studio \u5DE5\u5177\u5217\u8868\uFF0C\u4F9B\u9884\u8BBE\u9009\u62E9") }),
885
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t("\uB4F1\uB85D\uD558\uBA74 \uD50C\uB7EC\uADF8\uC778 \uCF54\uB4DC\uAC00 \uC2E4\uD589\uB418\uBBC0\uB85C \uC2E0\uB8B0\uD558\uB294 \uD50C\uB7EC\uADF8\uC778\uB9CC \uCD94\uAC00\uD558\uC138\uC694. \uD328\uD0A4\uC9C0\uB97C \uC124\uCE58\uD558\uC9C0\uB294 \uC54A\uC73C\uBA70, \uC124\uC815\uC740 \uC774 \uCEF4\uD4E8\uD130\uC5D0 \uD3C9\uBB38\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4.", "Register installed provider compositions as JSON. This does not install packages or generate tool code. Registration executes plugin code: add trusted providers only. Settings are stored locally as plaintext. Applied presets are not automatically changed.", "\u4EE5 JSON \u6CE8\u518C\u5DF2\u5B89\u88C5\u7684\u63D0\u4F9B\u8005\u914D\u7F6E\u3002\u4E0D\u5B89\u88C5\u8F6F\u4EF6\u5305\u6216\u751F\u6210\u5DE5\u5177\u4EE3\u7801\u3002\u6CE8\u518C\u4F1A\u6267\u884C\u63D2\u4EF6\u4EE3\u7801\uFF0C\u8BF7\u4EC5\u6DFB\u52A0\u53EF\u4FE1\u63D0\u4F9B\u8005\u3002\u8BBE\u7F6E\u4EE5\u660E\u6587\u4FDD\u5B58\u5728\u672C\u5730\u3002\u5DF2\u5E94\u7528\u7684\u9884\u8BBE\u4E0D\u4F1A\u81EA\u52A8\u6539\u53D8\u3002") }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("ol", { className: "studio-steps", children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uD50C\uB7EC\uADF8\uC778\uC774 \uD604\uC7AC DSH \uD504\uB85C\uD544\uC5D0 \uC124\uCE58\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.", "Ensure the plugin is installed in this DSH profile. Packages are not installed automatically.", "\u786E\u8BA4\u63D2\u4EF6\u5DF2\u5B89\u88C5\u5728\u5F53\u524D DSH \u914D\u7F6E\u4E2D\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002") }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uC544\uB798 \uBC30\uC5F4\uC5D0 id(\uACE0\uC720\uD55C \uC18C\uBB38\uC790 \uC2DD\uBCC4\uC790), name(\uD328\uD0A4\uC9C0 \uC774\uB984), config(\uD50C\uB7EC\uADF8\uC778 \uC124\uC815)\uB97C \uC785\uB825\uD569\uB2C8\uB2E4.", "Add an entry with id (unique lowercase identifier), name (exact package name), and config (public plugin settings).", "\u5728\u6570\u7EC4\u4E2D\u586B\u5199 id\uFF08\u552F\u4E00\u5C0F\u5199\u6807\u8BC6\u7B26\uFF09\u3001name\uFF08\u51C6\u786E\u7684\u8F6F\u4EF6\u5305\u540D\uFF09\u548C config\uFF08\u63D2\u4EF6\u516C\u5F00\u8BBE\u7F6E\uFF09\u3002") }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uD655\uC778 \uD6C4 \uB4F1\uB85D\uC744 \uB204\uB974\uBA74 \uB3C4\uAD6C \uBAA9\uB85D\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4. \uD544\uC694\uD55C \uB3C4\uAD6C\uB97C \uACE8\uB77C \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uC138\uC694.", "Validate and update master to refresh the tool list, then select the tools and save your preset.", "\u9A8C\u8BC1\u5E76\u66F4\u65B0\u4E3B\u9884\u8BBE\u540E\uFF0C\u9009\u62E9\u6240\u9700\u5DE5\u5177\u5E76\u4FDD\u5B58\u9884\u8BBE\u3002") })
890
+ ] }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted", children: t("\uC608\uC2DC: \uC124\uCE58\uB41C subagent \uD50C\uB7EC\uADF8\uC778\uC744 \uB2E4\uB978 \uB3C4\uAD6C \uC774\uB984\uC73C\uB85C \uB4F1\uB85D", "Example: register the installed subagent provider under a distinct tool name", "\u793A\u4F8B\uFF1A\u4F7F\u7528\u4E0D\u540C\u5DE5\u5177\u540D\u6CE8\u518C\u5DF2\u5B89\u88C5\u7684 subagent \u63D0\u4F9B\u8005") }),
892
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { children: JSON.stringify([{ id: "extra-agent", name: "@deepseek-ai/dsh-tool-subagent", config: { provider: "spawn", toolName: "studio_extra_agent" } }], null, 2) }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t("\uBAA9\uB85D \uC804\uCCB4\uAC00 \uC0C8 \uB0B4\uC6A9\uC73C\uB85C \uBC14\uB01D\uB2C8\uB2E4. \uAE30\uC874 \uD56D\uBAA9\uC744 \uC720\uC9C0\uD558\uB824\uBA74 \uBC30\uC5F4\uC5D0 \uB0A8\uACA8 \uB450\uACE0, \uBAA8\uB450 \uC9C0\uC6B0\uB824\uBA74 []\uB97C \uB4F1\uB85D\uD558\uC138\uC694. \uB3C4\uAD6C \uC774\uB984\uC774 \uACB9\uCE58\uC9C0 \uC54A\uAC8C \uD558\uC138\uC694.", "Saving replaces the entire extension list. Keep existing entries to retain them; [] removes all added providers only. Avoid existing tool names. Missing plugin service dependencies can prevent activation.", "\u4FDD\u5B58\u4F1A\u66FF\u6362\u6574\u4E2A\u9644\u52A0\u63D0\u4F9B\u8005\u5217\u8868\u3002\u4FDD\u7559\u5DF2\u6709\u6761\u76EE\u4EE5\u514D\u5220\u9664\uFF1B\u4FDD\u5B58 [] \u4EC5\u79FB\u9664\u5168\u90E8\u9644\u52A0\u63D0\u4F9B\u8005\u3002\u907F\u514D\u5DE5\u5177\u91CD\u540D\uFF1B\u7F3A\u5C11\u4F9D\u8D56\u670D\u52A1\u53EF\u80FD\u5BFC\u81F4\u6FC0\u6D3B\u5931\u8D25\u3002") }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "studio-field", children: [
895
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("\uB4F1\uB85D \uBAA9\uB85D (JSON)", "Provider composition", "\u63D0\u4F9B\u8005\u914D\u7F6E") }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("textarea", { rows: 6, className: "studio-mono", value: text, disabled: !loaded || busy, onChange: (event) => setText(event.target.value) })
897
+ ] }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "studio-inline-actions", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { disabled: !loaded || busy, onClick: async () => {
899
+ if (!actions) return;
900
+ setBusy(true);
901
+ setError("");
902
+ try {
903
+ await actions.save(text);
904
+ } catch (e) {
905
+ setError(String(e));
906
+ } finally {
907
+ setBusy(false);
908
+ }
909
+ }, children: t("\uD655\uC778 \uD6C4 \uB4F1\uB85D", "Validate and update master", "\u9A8C\u8BC1\u5E76\u66F4\u65B0\u4E3B\u9884\u8BBE") }) }),
910
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", children: error })
911
+ ] });
912
+ }
913
+
914
+ // src/config-editor.tsx
915
+ var import_react5 = __toESM(require("react"), 1);
916
+
917
+ // src/change-indicator.tsx
918
+ function hasChangedOverride(base, override) {
919
+ if (override === void 0) return false;
920
+ if (override !== null && typeof override === "object" && !Array.isArray(override)) {
921
+ const source = base !== null && typeof base === "object" ? base : {};
922
+ return Object.entries(override).some(([key, value]) => hasChangedOverride(source[key], value));
923
+ }
924
+ return JSON.stringify(base) !== JSON.stringify(override);
925
+ }
926
+
927
+ // src/item-picker.tsx
928
+ var import_react4 = __toESM(require("react"), 1);
929
+ var import_jsx_runtime4 = require("react/jsx-runtime");
930
+ function ItemPicker({ label, items, selected, onChange, labels, renderDetails, isModified, hasDetails }) {
931
+ const [query, setQuery] = import_react4.default.useState("");
932
+ const [filter, setFilter] = import_react4.default.useState("all");
933
+ const [detail, setDetail] = import_react4.default.useState();
934
+ const chosen = new Set(selected);
935
+ const modified = (name) => chosen.has(name) && (isModified?.(name) ?? false);
936
+ const text = query.trim().toLowerCase();
937
+ const visible = items.filter((item) => (filter === "all" || (filter === "selected" ? chosen.has(item.name) : modified(item.name))) && (!text || `${item.name}
938
+ ${item.title ?? ""}
939
+ ${item.description}
940
+ ${item.searchText ?? ""}`.toLowerCase().includes(text)));
941
+ const counts = { all: items.length, selected: items.filter((item) => chosen.has(item.name)).length, modified: items.filter((item) => modified(item.name)).length };
942
+ const toggle = (name) => onChange(chosen.has(name) ? selected.filter((value) => value !== name) : [...selected, name]);
943
+ const open = items.find((item) => item.name === detail && chosen.has(item.name));
944
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "studio-picker", children: [
945
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "studio-picker-bar", children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "search", "aria-label": labels.search, placeholder: labels.searchHint, value: query, onChange: (event) => setQuery(event.target.value) }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "studio-picker-filters", role: "radiogroup", "aria-label": labels.filter, children: ["all", "selected", "modified"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", role: "radio", "aria-checked": filter === value, className: "studio-picker-filter", onClick: () => setFilter(value), children: [
948
+ labels[value],
949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-count", children: counts[value] })
950
+ ] }, value)) })
951
+ ] }),
952
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ul", { className: "studio-picker-list", "aria-label": label, children: [
953
+ visible.map((item) => {
954
+ const on = chosen.has(item.name);
955
+ const blocked = !on && item.disabledReason !== void 0;
956
+ const details = on && renderDetails !== void 0 && (hasDetails?.(item.name) ?? true);
957
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "studio-picker-row", "data-selected": on, children: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", role: "switch", "aria-checked": on, disabled: blocked, className: "studio-picker-toggle", "aria-label": `${item.title ?? item.name}`, onClick: () => toggle(item.name), children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-check", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M3.5 8.5l3 3 6-7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "studio-picker-text", children: [
961
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "studio-picker-title", children: [
962
+ item.title ?? item.name,
963
+ item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-subtitle studio-mono", children: item.subtitle })
964
+ ] }),
965
+ (item.disabledReason && !on ? item.disabledReason : item.description) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-description", children: item.disabledReason && !on ? item.disabledReason : item.description })
966
+ ] })
967
+ ] }),
968
+ modified(item.name) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-badge", "data-tone": "info", "data-studio-modified": "true", children: labels.modifiedTag }),
969
+ details && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { small: true, "aria-label": `${labels.settings}: ${item.name}`, "aria-haspopup": "dialog", onClick: () => setDetail(item.name), children: labels.settings })
970
+ ] }, item.name);
971
+ }),
972
+ !visible.length && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "studio-picker-empty", role: "status", children: [
973
+ text ? labels.noResults : labels.empty,
974
+ (text || filter !== "all") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { small: true, onClick: () => {
975
+ setQuery("");
976
+ setFilter("all");
977
+ }, children: labels.clear })
978
+ ] })
979
+ ] }),
980
+ renderDetails && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DetailDialog, { open: open !== void 0, title: open?.title ?? open?.name, subtitle: open?.subtitle, closeLabel: labels.done, onClose: () => setDetail(void 0), children: open && renderDetails(open.name) })
981
+ ] });
982
+ }
983
+
984
+ // src/config-editor.tsx
985
+ var import_jsx_runtime5 = require("react/jsx-runtime");
986
+ var copy = {
987
+ en: {
988
+ list: "Configs",
989
+ reset: "Reset override",
990
+ unavailable: "Schema unavailable; editing is disabled.",
991
+ inherited: "Base value (not overridden)",
992
+ overridden: "Override",
993
+ unset: "Not set",
994
+ unsupported: "This schema type is not yet supported by the form. Existing values are preserved; remove the Config to reset them.",
995
+ json: "Config",
996
+ invalid: "Enter valid JSON.",
997
+ object: "Enter a JSON object.",
998
+ number: "Enter a valid number.",
999
+ picker: { filter: "Show", search: "Search execution settings", searchHint: "Search plugin names, fields or descriptions", all: "All", selected: "On", modified: "Modified", settings: "Edit", done: "Done", empty: "No Configs", noResults: "No matching results.", modifiedTag: "Modified", clear: "Show all" },
1000
+ items: { add: "Add", remove: "Remove", draft: "New item", key: "New key" }
1001
+ },
1002
+ ko: {
1003
+ list: "Config",
1004
+ reset: "\uAE30\uBCF8\uAC12\uC73C\uB85C",
1005
+ unavailable: "\uC2A4\uD0A4\uB9C8\uB97C \uC77D\uC744 \uC218 \uC5C6\uC5B4 \uD3B8\uC9D1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
1006
+ inherited: "\uAE30\uBCF8\uAC12",
1007
+ overridden: "\uBCC0\uACBD\uB428",
1008
+ unset: "\uBBF8\uC124\uC815",
1009
+ unsupported: "\uC774 \uD615\uC2DD\uC740 \uC544\uC9C1 \uD3B8\uC9D1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uAE30\uC874 \uAC12\uC740 \uC720\uC9C0\uB418\uBA70, Config\uB97C \uB044\uBA74 \uCD08\uAE30\uD654\uB429\uB2C8\uB2E4.",
1010
+ json: "Config",
1011
+ invalid: "\uC62C\uBC14\uB978 JSON\uC744 \uC785\uB825\uD558\uC138\uC694.",
1012
+ object: "JSON \uAC1D\uCCB4\uB97C \uC785\uB825\uD558\uC138\uC694.",
1013
+ number: "\uC62C\uBC14\uB978 \uC22B\uC790\uB97C \uC785\uB825\uD558\uC138\uC694.",
1014
+ picker: { filter: "\uBCF4\uAE30", search: "\uC124\uC815 \uAC80\uC0C9", searchHint: "\uD50C\uB7EC\uADF8\uC778 \uC774\uB984, \uD544\uB4DC, \uC124\uBA85\uC73C\uB85C \uAC80\uC0C9", all: "\uC804\uCCB4", selected: "\uCF1C\uC9D0", modified: "\uBCC0\uACBD\uB428", settings: "\uC124\uC815", done: "\uC644\uB8CC", empty: "\uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", noResults: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", modifiedTag: "\uBCC0\uACBD\uB428", clear: "\uC804\uCCB4 \uBCF4\uAE30" },
1015
+ items: { add: "\uCD94\uAC00", remove: "\uC0AD\uC81C", draft: "\uC0C8 \uD56D\uBAA9", key: "\uC0C8 \uD0A4" }
1016
+ },
1017
+ zh: {
1018
+ list: "Config",
1019
+ reset: "\u91CD\u7F6E\u8986\u76D6",
1020
+ unavailable: "\u67B6\u6784\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u7F16\u8F91",
1021
+ inherited: "\u57FA\u7840\u503C\uFF08\u672A\u8986\u76D6\uFF09",
1022
+ overridden: "\u8986\u76D6",
1023
+ unset: "\u672A\u8BBE\u7F6E",
1024
+ unsupported: "\u8868\u5355\u6682\u4E0D\u652F\u6301\u6B64\u8BBE\u7F6E\u7C7B\u578B\u3002\u4FDD\u7559\u73B0\u6709\u503C\uFF1B\u5173\u95ED Config \u53EF\u91CD\u7F6E\u3002",
1025
+ json: "Config",
1026
+ invalid: "\u8BF7\u8F93\u5165\u6709\u6548\u7684 JSON",
1027
+ object: "\u8BF7\u8F93\u5165 JSON \u5BF9\u8C61",
1028
+ number: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57",
1029
+ picker: { filter: "\u663E\u793A", search: "\u641C\u7D22\u6267\u884C\u8BBE\u7F6E", searchHint: "\u6309\u63D2\u4EF6\u540D\u79F0\u3001\u5B57\u6BB5\u6216\u63CF\u8FF0\u641C\u7D22", all: "\u5168\u90E8", selected: "\u5DF2\u5F00\u542F", modified: "\u5DF2\u4FEE\u6539", settings: "\u8BBE\u7F6E", done: "\u5B8C\u6210", empty: "\u6CA1\u6709 Config", noResults: "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\u3002", modifiedTag: "\u5DF2\u4FEE\u6539", clear: "\u663E\u793A\u5168\u90E8" },
1030
+ items: { add: "\u6DFB\u52A0", remove: "\u5220\u9664", draft: "\u65B0\u9879", key: "\u65B0\u952E" }
1031
+ }
1032
+ };
1033
+ var own = (value, key) => isObject(value) && Object.hasOwn(value, key);
1034
+ function isObject(value) {
1035
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1036
+ }
1037
+ function at(value, path) {
1038
+ return path.reduce((node, key) => own(node, key) ? node[key] : void 0, value);
1039
+ }
1040
+ function has(value, path) {
1041
+ return path.length === 0 || own(at(value, path.slice(0, -1)), path[path.length - 1]);
1042
+ }
1043
+ function update(value, path, next, reset = false) {
1044
+ if (!path.length) return next === void 0 ? {} : next;
1045
+ const [key, ...rest] = path;
1046
+ const result = { ...value };
1047
+ if (rest.length) {
1048
+ const child = update(isObject(value[key]) ? value[key] : {}, rest, next, reset);
1049
+ if (reset && !Object.keys(child).length) delete result[key];
1050
+ else Object.defineProperty(result, key, { value: child, enumerable: true, writable: true, configurable: true });
1051
+ } else if (reset) delete result[key];
1052
+ else Object.defineProperty(result, key, { value: next, enumerable: true, writable: true, configurable: true });
1053
+ return result;
1054
+ }
1055
+ function merged(base, override) {
1056
+ if (!isObject(base) || !isObject(override)) return override === void 0 ? base : override;
1057
+ return Object.fromEntries([.../* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => [key, own(override, key) ? merged(base[key], override[key]) : base[key]]));
1058
+ }
1059
+ function resolve(schema, root) {
1060
+ const seen = /* @__PURE__ */ new Set();
1061
+ while (typeof schema.$ref === "string") {
1062
+ const ref = schema.$ref;
1063
+ if (!ref.startsWith("#/") || seen.has(ref)) return schema;
1064
+ seen.add(ref);
1065
+ const target = at(root, ref.slice(2).split("/").map((part) => part.replace(/~1/g, "/").replace(/~0/g, "~")));
1066
+ if (!isObject(target)) return schema;
1067
+ const { $ref: _, ...siblings } = schema;
1068
+ schema = { ...target, ...siblings };
1069
+ }
1070
+ return schema;
1071
+ }
1072
+ function pathLabel(path) {
1073
+ return path.reduce((text, segment) => /^\d+$/.test(segment) ? `${text}[${segment}]` : `${text}.${segment}`);
1074
+ }
1075
+ var loaderBranch = (branch) => isObject(branch) && branch.$ref === "#/$defs/loaderExpression";
1076
+ function unwrapExpression(raw, root) {
1077
+ const schema = resolve(raw, root);
1078
+ if (!Array.isArray(schema.anyOf)) return schema;
1079
+ const literal = schema.anyOf.filter((branch) => !loaderBranch(branch));
1080
+ if (literal.length !== 1 || literal.length === schema.anyOf.length || !isObject(literal[0])) return schema;
1081
+ const { anyOf: _, ...metadata } = schema;
1082
+ return { ...resolve(literal[0], root), ...metadata };
1083
+ }
1084
+ var scalarTypes = ["string", "number", "integer", "boolean"];
1085
+ function scalarType(schema, root) {
1086
+ const resolved = unwrapExpression(schema, root);
1087
+ const types = (Array.isArray(resolved.type) ? resolved.type : [resolved.type]).filter((type) => type !== "null");
1088
+ return types.length === 1 && typeof types[0] === "string" ? types[0] : void 0;
1089
+ }
1090
+ function renderable(schema, root) {
1091
+ if (schema.const !== void 0 || Array.isArray(schema.enum)) return schema;
1092
+ const type = scalarType(schema, root);
1093
+ if (type !== void 0 && scalarTypes.includes(type)) return { ...schema, type };
1094
+ if (type === "object" && !own(schema, "patternProperties") && (isObject(schema.properties) || isObject(schema.additionalProperties))) return { ...schema, type };
1095
+ if (type === "array" && isObject(schema.items)) return { ...schema, type };
1096
+ return void 0;
1097
+ }
1098
+ function unionBranches(raw, root, depth = 0) {
1099
+ if (depth > 8) return void 0;
1100
+ const schema = unwrapExpression(raw, root);
1101
+ if (Array.isArray(schema.anyOf)) {
1102
+ const branches = [];
1103
+ for (const branch2 of schema.anyOf) {
1104
+ const nested = unionBranches(branch2, root, depth + 1);
1105
+ if (nested === void 0) return void 0;
1106
+ branches.push(...nested);
1107
+ }
1108
+ return branches;
1109
+ }
1110
+ const types = Array.isArray(schema.type) ? schema.type : schema.type === void 0 ? [] : [schema.type];
1111
+ if (schema.type !== void 0 && !types.some((type) => type !== "null")) return [];
1112
+ const branch = renderable(schema, root);
1113
+ return branch === void 0 ? void 0 : [branch];
1114
+ }
1115
+ function matchesChoice(choice, value) {
1116
+ if (choice.const !== void 0) return Object.is(choice.const, value);
1117
+ if (Array.isArray(choice.enum)) return choice.enum.some((item) => Object.is(item, value));
1118
+ if (choice.type === "object") return isObject(value);
1119
+ if (choice.type === "array") return Array.isArray(value);
1120
+ if (choice.type === "integer") return typeof value === "number" && Number.isInteger(value);
1121
+ if (choice.type === "number") return typeof value === "number";
1122
+ if (choice.type === "boolean") return typeof value === "boolean";
1123
+ return choice.type === "string" && typeof value === "string";
1124
+ }
1125
+ function choiceLabel(choice) {
1126
+ if (choice.const !== void 0) return typeof choice.const === "string" ? choice.const : JSON.stringify(choice.const);
1127
+ if (Array.isArray(choice.enum)) return choice.enum.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join(", ");
1128
+ if (typeof choice.title === "string") return choice.title;
1129
+ return String(choice.type ?? "value");
1130
+ }
1131
+ function seedChoice(choice, fallback) {
1132
+ if (matchesChoice(choice, fallback)) return fallback;
1133
+ if (matchesChoice(choice, choice.default)) return choice.default;
1134
+ if (choice.type === "object") return {};
1135
+ if (choice.type === "array") return [];
1136
+ if (choice.type === "number" || choice.type === "integer") return typeof choice.minimum === "number" ? choice.minimum : 0;
1137
+ return choice.type === "boolean" ? false : "";
1138
+ }
1139
+ function emptyValue(raw, root) {
1140
+ const schema = unwrapExpression(raw, root);
1141
+ if (Array.isArray(schema.anyOf)) {
1142
+ const branches = unionBranches(schema, root);
1143
+ const first = branches?.[0];
1144
+ if (first !== void 0) return emptyValue(first, root);
1145
+ }
1146
+ if (schema.const !== void 0) return schema.const;
1147
+ if (Array.isArray(schema.enum) && schema.enum.length) return schema.enum[0];
1148
+ if (schema.default !== void 0 && schema.default !== null) return JSON.parse(JSON.stringify(schema.default));
1149
+ if (schema.type === "object") return {};
1150
+ if (schema.type === "array") return [];
1151
+ if (schema.type === "boolean") return false;
1152
+ if (schema.type === "number" || schema.type === "integer") return typeof schema.minimum === "number" ? schema.minimum : 0;
1153
+ return "";
1154
+ }
1155
+ function Field({ schema: raw, root, path, base, override, change, labels, entryId, depth = 0, locked = false, bare = false, labelPath }) {
1156
+ const keys = labelPath ?? path;
1157
+ const inherited = merged(at(base, path), at(override, path));
1158
+ const expression = isObject(inherited) && own(inherited, "__jsExpr");
1159
+ let schema = expression ? resolve(raw, root) : unwrapExpression(raw, root);
1160
+ const alternatives = expression ? void 0 : unionBranches(schema, root);
1161
+ if (alternatives?.length === 1) {
1162
+ const { anyOf: _, ...metadata } = schema;
1163
+ schema = { ...metadata, ...alternatives[0] };
1164
+ }
1165
+ if (inherited !== null && Array.isArray(schema.type)) {
1166
+ const types = schema.type.filter((type) => type !== "null");
1167
+ if (types.length === 1) schema = { ...schema, type: types[0] };
1168
+ }
1169
+ const disabled = locked || schema.readOnly === true;
1170
+ const overridden = path.length > 0 && has(override, path);
1171
+ const current = inherited === void 0 ? schema.default : inherited;
1172
+ const label = `${entryId}: ${keys.length ? pathLabel(keys) : labels.json}`;
1173
+ const id = import_react5.default.useId();
1174
+ const [draft, setDraft] = import_react5.default.useState("");
1175
+ const [chosen, setChosen] = import_react5.default.useState();
1176
+ const union = alternatives !== void 0 && alternatives.length > 1 ? alternatives : void 0;
1177
+ const properties = isObject(schema.properties) ? schema.properties : void 0;
1178
+ const values = !properties && !own(schema, "patternProperties") && isObject(schema.additionalProperties) ? schema.additionalProperties : void 0;
1179
+ const items = schema.type === "array" && isObject(schema.items) ? schema.items : void 0;
1180
+ const reset = overridden && !bare && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { small: true, disabled, "aria-label": `${labels.reset}: ${label}`, onClick: () => change(path, void 0, true), children: labels.reset });
1181
+ if (properties) return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("fieldset", { disabled, className: bare ? "studio-config-item" : path.length ? void 0 : "studio-config-root", children: [
1182
+ !bare && keys.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("legend", { children: typeof schema.title === "string" ? schema.title : keys[keys.length - 1] }),
1183
+ !bare && typeof schema.description === "string" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "studio-muted", children: schema.description }),
1184
+ reset,
1185
+ Object.entries(properties).map(([key, child]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { schema: isObject(child) ? child : { readOnly: child === false }, root, path: [...path, key], labelPath: [...keys, key], base, override, change, labels, entryId, depth: depth + 1, locked: disabled }, key))
1186
+ ] });
1187
+ const scalarControl = (target, value, ariaLabel, controlId, onValue) => {
1188
+ const choices = target.const !== void 0 ? [target.const] : Array.isArray(target.enum) ? target.enum : void 0;
1189
+ if (choices) {
1190
+ const selected = choices.findIndex((item) => JSON.stringify(item) === JSON.stringify(value));
1191
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { id: controlId, "aria-label": ariaLabel, value: String(selected), disabled, onChange: (event) => onValue(choices[Number(event.target.value)]), children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "-1", disabled: true, children: value === void 0 ? labels.unset : JSON.stringify(value) }),
1193
+ choices.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: index, children: typeof item === "string" ? item : JSON.stringify(item) }, index))
1194
+ ] });
1195
+ }
1196
+ if (target.type === "boolean") {
1197
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { id: controlId, "aria-label": ariaLabel, value: typeof value === "boolean" ? String(value) : "", disabled, onChange: (event) => onValue(event.target.value === "true"), children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "", disabled: true, children: labels.unset }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "true", children: "true" }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "false", children: "false" })
1201
+ ] });
1202
+ }
1203
+ if (target.type === "number" || target.type === "integer") {
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { id: controlId, "aria-label": ariaLabel, type: "number", disabled, value: typeof value === "number" ? value : "", step: target.type === "integer" ? 1 : "any", min: typeof target.minimum === "number" ? target.minimum : void 0, max: typeof target.maximum === "number" ? target.maximum : void 0, onChange: (event) => {
1205
+ const number = event.target.valueAsNumber;
1206
+ if (Number.isFinite(number) && (target.type !== "integer" || Number.isInteger(number))) {
1207
+ event.target.setCustomValidity("");
1208
+ onValue(number);
1209
+ } else event.target.setCustomValidity(labels.number);
1210
+ } });
1211
+ }
1212
+ if (target.type === "string") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { id: controlId, "aria-label": ariaLabel, type: target.format === "password" || target.writeOnly === true ? "password" : "text", disabled, value: typeof value === "string" ? value : "", onChange: (event) => onValue(event.target.value) });
1213
+ return void 0;
1214
+ };
1215
+ const unionControl = (options) => {
1216
+ const optionLabels = options.map(choiceLabel).map((text, index, all) => all.indexOf(text) === index ? text : `${text} ${index + 1}`);
1217
+ const implicit = options.findIndex((choice) => matchesChoice(choice, current));
1218
+ const selected = chosen !== void 0 && chosen < options.length ? chosen : implicit;
1219
+ const branch = selected >= 0 ? options[selected] : void 0;
1220
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1221
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { id, "aria-label": label, disabled, value: String(selected), onChange: (event) => {
1222
+ const index = Number(event.target.value);
1223
+ const option = options[index];
1224
+ if (option === void 0) return;
1225
+ setChosen(index);
1226
+ if (option.const !== void 0) change(path, option.const);
1227
+ else if (Array.isArray(option.enum)) {
1228
+ if (option.enum.length) change(path, option.enum[0]);
1229
+ } else if (!matchesChoice(option, current)) change(path, seedChoice(option, schema.default));
1230
+ }, children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "-1", disabled: true, children: current === void 0 ? labels.unset : JSON.stringify(current) }),
1232
+ options.map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: index, children: optionLabels[index] }, index))
1233
+ ] }),
1234
+ branch === void 0 || branch.const !== void 0 || Array.isArray(branch.enum) ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { bare: true, schema: branch, root, path, labelPath: [...keys, "value"], base, override, change, labels, entryId, depth: depth + 1, locked: disabled })
1235
+ ] });
1236
+ };
1237
+ const listControl = (itemSchema) => {
1238
+ const entries = Array.isArray(current) ? current : [];
1239
+ const write = (next) => change(path, next);
1240
+ const edit = (index) => (itemPath, next, resetLeaf) => {
1241
+ const source = entries[index];
1242
+ const edited = itemPath.length ? update(isObject(source) ? source : {}, itemPath, next, resetLeaf) : next;
1243
+ write(entries.map((value, at2) => at2 === index ? edited : value));
1244
+ };
1245
+ const typed = scalarType(itemSchema, root) === "string";
1246
+ const commit = () => {
1247
+ const text = draft.trim();
1248
+ if (!text) return;
1249
+ write([...entries, text]);
1250
+ setDraft("");
1251
+ };
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-config-list", children: [
1253
+ entries.map((value, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-list-row", children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { bare: true, schema: itemSchema, root, path: [], labelPath: [...keys, String(index)], base: value, override: value, change: edit(index), labels, entryId, depth: depth + 1, locked: disabled }),
1255
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { small: true, disabled, "aria-label": `${labels.items.remove}: ${label}[${index}]`, onClick: () => write(entries.filter((_, at2) => at2 !== index)), children: labels.items.remove })
1256
+ ] }, index)),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-list-row", children: [
1258
+ typed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { id, "aria-label": `${labels.items.draft}: ${label}`, placeholder: labels.items.draft, disabled, value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: (event) => {
1259
+ if (event.key === "Enter") {
1260
+ event.preventDefault();
1261
+ commit();
1262
+ }
1263
+ } }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { id, className: "studio-muted studio-hint", children: choiceLabel(itemSchema) }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { small: true, disabled: disabled || typed && !draft.trim(), "aria-label": `${labels.items.add}: ${label}`, onClick: () => typed ? commit() : write([...entries, emptyValue(itemSchema, root)]), children: labels.items.add })
1265
+ ] })
1266
+ ] });
1267
+ };
1268
+ const dictControl = (valueSchema) => {
1269
+ const entries = isObject(current) ? current : {};
1270
+ const write = (next) => change(path, next);
1271
+ const edit = (key) => (valuePath, next, resetLeaf) => {
1272
+ const source = entries[key];
1273
+ const edited = valuePath.length ? update(isObject(source) ? source : {}, valuePath, next, resetLeaf) : next;
1274
+ write({ ...entries, [key]: edited });
1275
+ };
1276
+ const commit = () => {
1277
+ const key = draft.trim();
1278
+ if (!key || own(entries, key)) return;
1279
+ write({ ...entries, [key]: emptyValue(valueSchema, root) });
1280
+ setDraft("");
1281
+ };
1282
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-config-list", children: [
1283
+ Object.entries(entries).map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-list-row", children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "studio-mono studio-config-key", children: key }),
1285
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { bare: true, schema: valueSchema, root, path: [], labelPath: [...keys, key], base: value, override: value, change: edit(key), labels, entryId, depth: depth + 1, locked: disabled }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { small: true, disabled, "aria-label": `${labels.items.remove}: ${label}.${key}`, onClick: () => {
1287
+ const next = { ...entries };
1288
+ delete next[key];
1289
+ write(next);
1290
+ }, children: labels.items.remove })
1291
+ ] }, key)),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-list-row", children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { id, "aria-label": `${labels.items.key}: ${label}`, placeholder: labels.items.key, disabled, value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: (event) => {
1294
+ if (event.key === "Enter") {
1295
+ event.preventDefault();
1296
+ commit();
1297
+ }
1298
+ } }),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { small: true, disabled: disabled || !draft.trim(), "aria-label": `${labels.items.add}: ${label}`, onClick: commit, children: labels.items.add })
1300
+ ] })
1301
+ ] });
1302
+ };
1303
+ let input;
1304
+ if (expression) input = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "status", "aria-label": label, children: labels.unsupported });
1305
+ else if (union) input = unionControl(union);
1306
+ else if (values) input = dictControl(values);
1307
+ else if (items) input = listControl(items);
1308
+ else input = (depth > 16 ? void 0 : scalarControl(schema, current, label, id, (next) => change(path, next))) ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "status", "aria-label": label, children: labels.unsupported });
1309
+ if (bare) return input;
1310
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-field studio-config-field", "data-overridden": overridden, children: [
1311
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "studio-field-head", children: [
1312
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: id, className: "studio-mono", children: typeof schema.title === "string" ? schema.title : keys[keys.length - 1] ?? labels.json }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "studio-config-state", children: overridden ? labels.overridden : labels.inherited }),
1314
+ reset
1315
+ ] }),
1316
+ typeof schema.description === "string" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "studio-muted studio-hint", children: schema.description }),
1317
+ input
1318
+ ] });
1319
+ }
1320
+ function comparisonBase(raw, root, base, depth = 0) {
1321
+ if (!raw || !root || depth > 16) return base;
1322
+ let schema = resolve(raw, root);
1323
+ if (Array.isArray(schema.anyOf)) {
1324
+ const literal = schema.anyOf.filter((branch) => !isObject(branch) || branch.$ref !== "#/$defs/loaderExpression");
1325
+ if (literal.length === 1 && literal.length < schema.anyOf.length && isObject(literal[0])) schema = resolve(literal[0], root);
1326
+ }
1327
+ const value = base === void 0 ? schema.default : base;
1328
+ if (isObject(schema.properties) && (value === void 0 || isObject(value))) {
1329
+ return { ...isObject(value) ? value : {}, ...Object.fromEntries(Object.entries(schema.properties).filter(([, child]) => isObject(child)).map(([key, child]) => [key, comparisonBase(child, root, isObject(value) ? value[key] : void 0, depth + 1)])) };
1330
+ }
1331
+ return value;
1332
+ }
1333
+ function ConfigEditor({ entries, value, onChange, locale }) {
1334
+ const labels = copy[locale.startsWith("ko") ? "ko" : locale.startsWith("zh") ? "zh" : "en"];
1335
+ const items = [
1336
+ ...entries.map((entry) => ({
1337
+ name: entry.id,
1338
+ title: entry.name,
1339
+ subtitle: entry.id,
1340
+ description: "",
1341
+ searchText: JSON.stringify(entry.schema ?? {}),
1342
+ ...entry.error || !entry.schema ? { disabledReason: entry.error || labels.unavailable } : {}
1343
+ })),
1344
+ ...Object.keys(value).filter((id) => !entries.some((entry) => entry.id === id)).map((id) => ({ name: id, title: id, description: labels.unavailable }))
1345
+ ];
1346
+ const selected = items.filter((item) => own(value, item.name)).map((item) => item.name);
1347
+ const change = (names2) => onChange(Object.fromEntries([
1348
+ ...Object.entries(value).filter(([id]) => names2.includes(id)),
1349
+ ...names2.filter((id) => !own(value, id)).map((id) => [id, {}])
1350
+ ]));
1351
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "studio-stack", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1352
+ ItemPicker,
1353
+ {
1354
+ label: labels.list,
1355
+ items,
1356
+ selected,
1357
+ onChange: change,
1358
+ labels: labels.picker,
1359
+ hasDetails: (id) => entries.some((entry) => entry.id === id && entry.schema && !entry.error),
1360
+ isModified: (id) => {
1361
+ const entry = entries.find((item) => item.id === id);
1362
+ return !!entry && own(value, id) && hasChangedOverride(comparisonBase(entry.schema, entry.schema, entry.config), value[id]);
1363
+ },
1364
+ renderDetails: (id) => {
1365
+ const entry = entries.find((item) => item.id === id);
1366
+ return entry?.schema && own(value, id) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { schema: entry.schema, root: entry.schema, path: [], base: entry.config, override: value[id], entryId: id, labels, change: (path, next, reset) => onChange({ ...value, [id]: update(value[id], path, next, reset) }) }) : null;
1367
+ }
1368
+ }
1369
+ ) });
1370
+ }
1371
+
1372
+ // src/preset-manager.tsx
1373
+ var import_react6 = __toESM(require("react"), 1);
1374
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1375
+ var message = (error) => error instanceof Error ? error.message : String(error);
1376
+ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1377
+ const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1378
+ const [load, setLoad] = import_react6.default.useState({ phase: "loading" });
1379
+ const [action, setAction] = import_react6.default.useState();
1380
+ const [deleting, setDeleting] = import_react6.default.useState();
1381
+ const [confirming, setConfirming] = import_react6.default.useState(false);
1382
+ const request = import_react6.default.useRef(0);
1383
+ const refresh = import_react6.default.useCallback(async () => {
1384
+ if (!api) return;
1385
+ const ticket = ++request.current;
1386
+ setLoad((old) => ({ phase: "loading", ...old.items ? { items: old.items } : {} }));
1387
+ try {
1388
+ const items2 = await api.list();
1389
+ if (ticket === request.current) setLoad({ phase: "ready", items: items2 });
1390
+ } catch (error) {
1391
+ if (ticket === request.current) setLoad((old) => ({ phase: "failed", error: message(error), ...old.items ? { items: old.items } : {} }));
1392
+ }
1393
+ }, [api]);
1394
+ import_react6.default.useEffect(() => {
1395
+ void refresh();
1396
+ return () => {
1397
+ request.current++;
1398
+ };
1399
+ }, [refresh, revision]);
1400
+ const busy = action !== void 0 && action.error === void 0;
1401
+ const run = async (id, kind, perform) => {
1402
+ if (busy) return;
1403
+ setAction({ id, kind });
1404
+ try {
1405
+ await perform();
1406
+ } catch (error) {
1407
+ setAction({ id, kind, error: message(error) });
1408
+ return;
1409
+ }
1410
+ setAction(void 0);
1411
+ await refresh();
1412
+ };
1413
+ const items = load.items ?? [];
1414
+ const failedName = action?.error !== void 0 ? items.find((item) => item.id === action.id)?.name ?? deleting?.name ?? action.id : "";
1415
+ const failure = action?.error === void 0 ? "" : action.kind === "apply" ? t(`${failedName}\uC744(\uB97C) \uBC18\uC601\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC774\uC804\uC5D0 \uBC18\uC601\uD55C \uAD6C\uC131\uC740 \uADF8\uB300\uB85C \uC720\uC9C0\uB429\uB2C8\uB2E4.`, `Could not apply ${failedName}. A previously applied configuration stays in effect.`, `\u65E0\u6CD5\u5E94\u7528 ${failedName}\u3002\u4E4B\u524D\u5DF2\u5E94\u7528\u7684\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8`) : action.kind === "unapply" ? t(`${failedName}\uC758 \uBC18\uC601\uC744 \uD574\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBC18\uC601 \uC0C1\uD0DC\uB294 \uADF8\uB300\uB85C\uC785\uB2C8\uB2E4.`, `Could not unapply ${failedName}. Its applied state did not change.`, `\u65E0\u6CD5\u53D6\u6D88\u5E94\u7528 ${failedName}\u3002\u5E94\u7528\u72B6\u6001\u672A\u6539\u53D8`) : t(`${failedName}\uC744(\uB97C) \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uD504\uB9AC\uC14B\uC740 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4.`, `Could not delete ${failedName}. The preset was not deleted.`, `\u65E0\u6CD5\u5220\u9664 ${failedName}\u3002\u9884\u8BBE\u672A\u88AB\u5220\u9664`);
1416
+ const pending = (item, kind) => busy && action?.id === item.id && action.kind === kind;
1417
+ const status = (item) => item.status === "applied" ? t("\uBC18\uC601\uB428", "Applied", "\u5DF2\u5E94\u7528") : item.status === "modified" ? t("\uB2E4\uC2DC \uBC18\uC601 \uD544\uC694", "Needs reapply", "\u9700\u8981\u91CD\u65B0\u5E94\u7528") : t("\uBC18\uC601 \uC548 \uB428", "Not applied", "\u672A\u5E94\u7528");
1418
+ const tone = { applied: "success", modified: "warning", draft: "neutral" };
1419
+ const warnings = [...items.reduce((groups, item) => item.applyWarning ? groups.set(item.applyWarning, [...groups.get(item.applyWarning) ?? [], item.name]) : groups, /* @__PURE__ */ new Map())];
1420
+ const retry = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: load.phase === "loading", onClick: () => void refresh(), children: t("\uB2E4\uC2DC \uC2DC\uB3C4", "Retry", "\u91CD\u8BD5") });
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PageHead, { title: t("\uD504\uB9AC\uC14B \uBAA9\uB85D", "Preset list", "\u9884\u8BBE\u5217\u8868"), description: t("\uC800\uC7A5\uD55C \uD504\uB9AC\uC14B\uC744 DSH\uC5D0 \uBC18\uC601\uD558\uBA74 \uC0C8 \uC138\uC158\uC5D0\uC11C \uC120\uD0DD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Apply saved configurations to DSH agent presets or delete them.", "\u5C06\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\u5E94\u7528\u5230 DSH \u4EE3\u7406\u9884\u8BBE\u6216\u5220\u9664\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { disabled: busy || !api || load.phase === "loading", onClick: () => void refresh(), children: load.phase === "loading" && load.items ? t("\uC0C8\uB85C\uACE0\uCE68 \uC911\u2026", "Refreshing\u2026", "\u5237\u65B0\u4E2D\u2026") : t("\uC0C8\uB85C\uACE0\uCE68", "Refresh", "\u5237\u65B0") }) }),
1423
+ !api && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Notice, { children: t("Studio \uC11C\uBC84\uC5D0 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC544 \uD504\uB9AC\uC14B\uC744 \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Presets are unavailable because the Studio Host API is not connected.", "Studio Host API \u672A\u8FDE\u63A5\uFF0C\u65E0\u6CD5\u52A0\u8F7D\u9884\u8BBE") }),
1424
+ api && load.phase === "loading" && !load.items && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "status", className: "studio-muted", children: t("\uD504\uB9AC\uC14B\uC744 \uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading presets\u2026", "\u6B63\u5728\u52A0\u8F7D\u9884\u8BBE\u2026") }),
1425
+ load.phase === "failed" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Notice, { action: retry, children: load.items ? t(`\uBAA9\uB85D\uC744 \uC0C8\uB85C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD574 \uC774\uC804 \uBAA9\uB85D\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4. (${load.error})`, `Could not reload the list. Showing the last loaded list. (${load.error})`, `\u65E0\u6CD5\u91CD\u65B0\u52A0\u8F7D\u5217\u8868\uFF0C\u6B63\u5728\u663E\u793A\u4E0A\u6B21\u52A0\u8F7D\u7684\u5217\u8868\uFF08${load.error}\uFF09`) : t(`\uD504\uB9AC\uC14B \uBAA9\uB85D\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uC5D0\uB294 \uC601\uD5A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. (${load.error})`, `Could not load presets. Saved presets were not changed. (${load.error})`, `\u65E0\u6CD5\u52A0\u8F7D\u9884\u8BBE\uFF0C\u5DF2\u4FDD\u5B58\u7684\u9884\u8BBE\u672A\u88AB\u66F4\u6539\uFF08${load.error}\uFF09`) }),
1426
+ failure && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Notice, { action: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, onClick: () => setAction(void 0), children: t("\uB2EB\uAE30", "Dismiss", "\u5173\u95ED") }), children: [
1427
+ failure,
1428
+ " (",
1429
+ action.error,
1430
+ ")"
1431
+ ] }),
1432
+ load.phase === "ready" && !items.length && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "status", className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uC0C8\uB85C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694.", "No saved presets. Create one from Create preset.", "\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u9884\u8BBE\u3002\u8BF7\u4ECE\u521B\u5EFA\u9884\u8BBE\u83DC\u5355\u5F00\u59CB\u3002") }),
1433
+ warnings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "studio-preset-warnings", "aria-label": t("\uC8FC\uC758\uC0AC\uD56D", "Notices", "\u6CE8\u610F\u4E8B\u9879"), children: warnings.map(([text, names2]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: "warning", children: t("\uC8FC\uC758", "Notice", "\u6CE8\u610F") }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1436
+ text,
1437
+ " ",
1438
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "studio-card-meta", children: [
1439
+ "(",
1440
+ t("\uD574\uB2F9", "Affects", "\u6D89\u53CA"),
1441
+ ": ",
1442
+ names2.join(", "),
1443
+ ")"
1444
+ ] })
1445
+ ] })
1446
+ ] }, text)) }),
1447
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "studio-tool-cards studio-card-pair", "aria-busy": load.phase === "loading" || busy, children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("article", { className: "studio-preset-card", children: [
1448
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "studio-card-head", children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: item.name }),
1450
+ item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: "outline", children: t("\uC77D\uAE30 \uC804\uC6A9", "Read-only", "\u53EA\u8BFB") }),
1451
+ item.template && !item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: "info", children: t("\uAE30\uBCF8 \uC81C\uACF5", "Built-in", "\u5185\u7F6E") }),
1452
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: tone[item.status], children: status(item) })
1453
+ ] }),
1454
+ item.template ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: t("Studio\uAC00 \uAD8C\uC7A5\uD558\uB294 \uAE30\uBCF8 \uAD6C\uC131\uC785\uB2C8\uB2E4. \uD3B8\uC9D1\uD558\uAC70\uB098 \uBCF5\uC0AC\uD574 \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Studio optimized default preset", "Studio \u63A8\u8350\u7684\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u53EF\u7F16\u8F91\u6216\u590D\u5236") }) : item.description && !item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: item.description }),
1455
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "studio-card-meta", children: [
1456
+ t("\uB3C4\uAD6C", "Tools", "\u5DE5\u5177"),
1457
+ " ",
1458
+ item.request.tools.length,
1459
+ " \xB7 ",
1460
+ t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC5ED\uD560", "Subagent roles", "\u5B50\u4EE3\u7406\u89D2\u8272"),
1461
+ " ",
1462
+ Array.isArray(item.agents) ? item.agents.length : 0
1463
+ ] }),
1464
+ item.readOnly ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-meta studio-card-note", children: t("DSH\uC5D0 \uC124\uCE58\uB41C \uBAA8\uB4E0 \uB3C4\uAD6C\uB97C \uB2F4\uC740 \uAE30\uC900 \uD504\uB9AC\uC14B\uC785\uB2C8\uB2E4. \uD3B8\uC9D1\uD558\uAC70\uB098 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Reference preset with every installed DSH tool. It cannot be edited or deleted.", "\u5305\u542B\u6240\u6709\u5DF2\u5B89\u88C5 DSH \u5DE5\u5177\u7684\u57FA\u51C6\u9884\u8BBE\uFF0C\u65E0\u6CD5\u7F16\u8F91\u6216\u5220\u9664") }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "studio-card-footer", children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || item.readOnly || !onEdit, onClick: () => onEdit?.(item), children: t("\uD3B8\uC9D1", "Edit", "\u7F16\u8F91") }),
1466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || !onCopy, onClick: () => onCopy?.(item), children: t("\uBCF5\uC0AC", "Create copy", "\u521B\u5EFA\u526F\u672C") }),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || !api || item.readOnly || item.nonDeletable, title: item.nonDeletable ? t("\uAE30\uBCF8 \uC81C\uACF5 \uD504\uB9AC\uC14B\uC740 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Built-in presets cannot be deleted.", "\u5185\u7F6E\u9884\u8BBE\u65E0\u6CD5\u5220\u9664") : void 0, onClick: () => {
1468
+ setDeleting(item);
1469
+ setConfirming(true);
1470
+ }, children: t("\uC0AD\uC81C", "Delete", "\u5220\u9664") }),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "studio-card-footer-end", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1472
+ SplitButton,
1473
+ {
1474
+ primary: item.status !== "applied",
1475
+ disabled: busy || !api || !!item.readOnly,
1476
+ onClick: () => void run(item.id, "apply", () => api.apply(item.id)),
1477
+ menuLabel: t("\uBC18\uC601 \uC635\uC158", "Apply options", "\u5E94\u7528\u9009\u9879"),
1478
+ label: pending(item, "apply") ? t("\uBC18\uC601 \uC911\u2026", "Applying\u2026", "\u5E94\u7528\u4E2D\u2026") : pending(item, "unapply") ? t("\uD574\uC81C \uC911\u2026", "Unapplying\u2026", "\u53D6\u6D88\u4E2D\u2026") : item.status === "draft" ? t("DSH\uC5D0 \uBC18\uC601", "Apply to DSH", "\u5E94\u7528\u5230 DSH") : t("DSH\uC5D0 \uB2E4\uC2DC \uBC18\uC601", "Reapply to DSH", "\u91CD\u65B0\u5E94\u7528\u5230 DSH"),
1479
+ actions: item.status === "draft" || !api?.unapply ? [] : [{ label: t("\uBC18\uC601 \uD574\uC81C", "Unapply from DSH", "\u53D6\u6D88\u5E94\u7528\u5230 DSH"), onSelect: () => void run(item.id, "unapply", () => api.unapply(item.id)) }]
1480
+ }
1481
+ ) })
1482
+ ] })
1483
+ ] }, item.id)) }),
1484
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1485
+ DetailDialog,
1486
+ {
1487
+ compact: true,
1488
+ open: confirming,
1489
+ onClose: () => setConfirming(false),
1490
+ title: t("\uD504\uB9AC\uC14B \uC0AD\uC81C", "Delete preset", "\u5220\u9664\u9884\u8BBE"),
1491
+ subtitle: deleting?.name,
1492
+ footer: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1493
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { disabled: busy, onClick: () => setConfirming(false), children: t("\uCDE8\uC18C", "Cancel", "\u53D6\u6D88") }),
1494
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { primary: true, disabled: busy || !deleting, onClick: () => {
1495
+ const target = deleting;
1496
+ setConfirming(false);
1497
+ void run(target.id, "delete", () => api.delete(target.id)).then(() => setDeleting(void 0));
1498
+ }, children: t("\uC0AD\uC81C \uD655\uC778", "Confirm delete", "\u786E\u8BA4\u5220\u9664") })
1499
+ ] }),
1500
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uACFC DSH\uC5D0 \uBC18\uC601\uB41C \uD504\uB9AC\uC14B\uC744 \uD568\uAED8 \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uAE30\uC874 \uB300\uD654\uB294 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4.", "Delete the saved preset and its Studio-owned DSH registration. Existing conversations are not deleted.", "\u5220\u9664\u5DF2\u4FDD\u5B58\u9884\u8BBE\u53CA Studio \u6CE8\u518C\u7684 DSH \u9884\u8BBE\uFF0C\u4E0D\u5220\u9664\u73B0\u6709\u5BF9\u8BDD\u3002") })
1501
+ }
1502
+ )
1503
+ ] });
1504
+ }
1505
+
1506
+ // src/package-name.ts
1507
+ var PACKAGE_NAME = "@matamt-dbg/dsh-studio";
1508
+ var POLICY_PLUGIN = `${PACKAGE_NAME}/policy`;
1509
+ var ROLE_PLUGIN = `${PACKAGE_NAME}/roles`;
1510
+ var BATCH_PLUGIN = `${PACKAGE_NAME}/batch`;
1511
+
1512
+ // src/delegation.ts
1513
+ var ROLE_TOOL = "subagent_role";
1514
+ var LEGACY_ROLE_TOOL = "studio_role";
1515
+ var GENERIC_DELEGATION_TOOLS = ["subagent", "subagent_fork", "list_subagent_models"];
1516
+ var WITHDRAWN_TOOLS = ["task_run"];
1517
+ function delegationTools(tools, hasRoles = false) {
1518
+ const delegates = hasRoles || tools.some((name) => name === ROLE_TOOL || name === LEGACY_ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name));
1519
+ const rest = tools.filter((name) => name !== ROLE_TOOL && name !== LEGACY_ROLE_TOOL && !GENERIC_DELEGATION_TOOLS.includes(name) && !WITHDRAWN_TOOLS.includes(name));
1520
+ return delegates ? [...rest, ROLE_TOOL] : rest;
1521
+ }
1522
+ function renameLegacyRoleKey(entries) {
1523
+ if (!entries || !Object.hasOwn(entries, LEGACY_ROLE_TOOL)) return entries;
1524
+ const { [LEGACY_ROLE_TOOL]: legacy, ...rest } = entries;
1525
+ return Object.hasOwn(rest, ROLE_TOOL) ? rest : { ...rest, [ROLE_TOOL]: legacy };
1526
+ }
1527
+
1528
+ // src/optimized-preset.ts
1529
+ var BUILTIN_TOOL_PRESETS = ["standard", "ptc", "minimal", "cordis"];
1530
+
1531
+ // src/prompt-tools-prototype.tsx
1532
+ var import_react7 = __toESM(require("react"), 1);
1533
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1534
+ var defaultProvidedTools = [];
1535
+ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProvidedChange, catalog, catalogError, onPreview, onRequestChange, initialRequest }) {
1536
+ const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1537
+ const base = initialRequest?.preset ?? "studio-master";
1538
+ const [toolOverrides, setToolOverrides] = import_react7.default.useState(renameLegacyRoleKey(initialRequest?.toolOverrides) ?? {});
1539
+ const [sections, setSections] = import_react7.default.useState(initialRequest?.sections ?? {});
1540
+ const providers = initialRequest?.providers ?? ["@deepseek-ai/dsh-persona"];
1541
+ const [providerConfigs, setProviderConfigs] = import_react7.default.useState(initialRequest?.providerConfigs ?? {});
1542
+ const [result, setResult] = import_react7.default.useState();
1543
+ const [resultKey, setResultKey] = import_react7.default.useState("");
1544
+ const [error, setError] = import_react7.default.useState("");
1545
+ const [previewOpen, setPreviewOpen] = import_react7.default.useState(false);
1546
+ const idBase = import_react7.default.useId();
1547
+ const closePreview = () => setPreviewOpen(false);
1548
+ const [busy, setBusy] = import_react7.default.useState(false);
1549
+ const presets = catalog?.presets ?? [];
1550
+ const toolSources = BUILTIN_TOOL_PRESETS.map((id) => presets.find((p) => p.id === id) ?? { id, name: id, tools: [], sections: [], error: "Preset unavailable" });
1551
+ const tools = catalog?.tools ?? [];
1552
+ const current = presets.find((preset) => preset.id === base);
1553
+ const selectedSections = new Set(tools.filter((tool) => provided.includes(tool.name)).flatMap((tool) => tool.promptSections?.map((section) => section.name) ?? []));
1554
+ const request = {
1555
+ ...initialRequest,
1556
+ preset: base,
1557
+ providers,
1558
+ tools: provided,
1559
+ prompt,
1560
+ model: initialRequest?.model ?? "testLLM",
1561
+ toolOverrides: Object.fromEntries(Object.entries(toolOverrides).filter(([name]) => provided.includes(name))),
1562
+ sections: Object.fromEntries(Object.entries(sections).filter(([name]) => selectedSections.has(name))),
1563
+ providerConfigs: Object.fromEntries(Object.entries(providerConfigs).filter(([name]) => providers.includes(name)))
1564
+ };
1565
+ const personaFields = current?.promptProviders?.find((provider) => provider.name === providers[0])?.fields ?? [];
1566
+ const requestKey = JSON.stringify(request);
1567
+ import_react7.default.useEffect(() => {
1568
+ onRequestChange?.(request);
1569
+ }, [requestKey, onRequestChange]);
1570
+ const labels = { filter: t("\uBCF4\uAE30", "Show", "\u663E\u793A"), search: t("\uB3C4\uAD6C \uAC80\uC0C9", "Search tools", "\u641C\u7D22\u5DE5\u5177"), searchHint: t("\uC774\uB984, \uC124\uBA85, \uC778\uC790\uB85C \uAC80\uC0C9", "Search names, descriptions or parameters", "\u6309\u540D\u79F0\u3001\u63CF\u8FF0\u6216\u53C2\u6570\u641C\u7D22"), all: t("\uC804\uCCB4", "All", "\u5168\u90E8"), selected: t("\uC120\uD0DD\uB428", "Selected", "\u5DF2\u9009"), modified: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), settings: t("\uC124\uC815", "Details", "\u8BE6\u60C5"), done: t("\uC644\uB8CC", "Close", "\u5173\u95ED"), empty: t("\uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", "No items", "\u65E0\u9879\u76EE"), noResults: t("\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", "No matching results.", "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\u3002"), modifiedTag: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), clear: t("\uC804\uCCB4 \uBCF4\uAE30", "Show all", "\u663E\u793A\u5168\u90E8") };
1571
+ const presetTools = (presetId) => delegationTools(presets.find((p) => p.id === presetId)?.tools.map((tool) => tool.name) ?? []);
1572
+ const togglePreset = (presetId) => {
1573
+ const names2 = presetTools(presetId);
1574
+ onProvidedChange(includesPreset(presetId) ? provided.filter((name) => !names2.includes(name)) : [.../* @__PURE__ */ new Set([...provided, ...names2])]);
1575
+ };
1576
+ const includesPreset = (presetId) => {
1577
+ const names2 = presetTools(presetId);
1578
+ return names2.length > 0 && names2.every((name) => provided.includes(name));
1579
+ };
1580
+ const changeTools = (next) => onProvidedChange(delegationTools(next));
1581
+ import_react7.default.useEffect(() => {
1582
+ if (provided.some((name) => name === LEGACY_ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name) || WITHDRAWN_TOOLS.includes(name))) onProvidedChange(delegationTools(provided));
1583
+ }, []);
1584
+ const preview = async () => {
1585
+ if (!onPreview) return;
1586
+ setPreviewOpen(true);
1587
+ setBusy(true);
1588
+ setError("");
1589
+ setResult(void 0);
1590
+ try {
1591
+ const value = await onPreview(request);
1592
+ setResult(value);
1593
+ setResultKey(requestKey);
1594
+ } catch (cause) {
1595
+ setError(String(cause));
1596
+ } finally {
1597
+ setBusy(false);
1598
+ }
1599
+ };
1600
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
1601
+ catalogError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: catalogError }) : !catalog ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: t("\uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading\u2026", "\u6B63\u5728\u52A0\u8F7D\u2026") }) : null,
1602
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-form-section", children: [
1603
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uD504\uB86C\uD504\uD2B8", "Prompt composition", "\u63D0\u793A\u8BCD\u7EC4\u88C5"), description: t("\uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uC804\uB2EC\uD560 \uC9C0\uCE68\uC744 \uD3B8\uC9D1\uD569\uB2C8\uB2E4. \uBBF8\uB9AC\uBCF4\uAE30\uB85C \uC870\uB9BD \uACB0\uACFC\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Edit Persona and Studio instructions, then preview the assembled result.", "\u7F16\u8F91 Persona \u4E0E Studio \u4E13\u7528\u6307\u4EE4\uFF0C\u5E76\u9884\u89C8\u7EC4\u88C5\u7ED3\u679C\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !onPreview || !current || busy, onClick: () => void preview(), children: busy ? t("\uC870\uB9BD \uC911\u2026", "Assembling\u2026", "\u7EC4\u88C5\u4E2D\u2026") : t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Preview prompt", "\u9884\u89C8\u63D0\u793A\u8BCD") }) }),
1604
+ ["prefix", "suffix"].map((key) => {
1605
+ const name = providers[0];
1606
+ const config = providerConfigs[name] ?? {};
1607
+ const original = personaFields.find((field2) => field2.key === key)?.value;
1608
+ const fieldId = `${idBase}-${key}`;
1609
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "studio-field", children: [
1610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "studio-field-head", children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: fieldId, children: key === "prefix" ? t("\uAE30\uBCF8 \uC9C0\uCE68 (Persona)", "Persona instructions", "Persona \u57FA\u672C\u6307\u4EE4") : t("\uB05D\uC5D0 \uBD99\uC77C \uC9C0\uCE68 (Persona)", "Persona suffix", "Persona \u540E\u7F6E\u6307\u4EE4") }),
1612
+ Object.hasOwn(config, key) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChangeIndicator, { locale }),
1613
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !Object.hasOwn(config, key), onClick: () => {
1614
+ const next = { ...config };
1615
+ delete next[key];
1616
+ setProviderConfigs({ ...providerConfigs, [name]: next });
1617
+ }, children: t("\uC6D0\uB798\uB300\uB85C", "Use original", "\u4F7F\u7528\u539F\u59CB\u503C") })
1618
+ ] }),
1619
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { id: fieldId, rows: key === "prefix" ? 6 : 3, value: typeof config[key] === "string" ? config[key] : typeof original === "string" ? original : "", onChange: (e) => setProviderConfigs({ ...providerConfigs, [name]: { ...config, [key]: e.target.value } }) })
1620
+ ] }, key);
1621
+ }),
1622
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "studio-field", children: [
1623
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: t("\uCD94\uAC00 \uC9C0\uCE68", "Additional Studio instructions", "Studio \u9644\u52A0\u6307\u4EE4") }),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { rows: 6, value: prompt, onChange: (e) => onPromptChange(e.target.value) })
1625
+ ] })
1626
+ ] }),
1627
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-form-section", children: [
1628
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), description: t("\uC5D0\uC774\uC804\uD2B8\uAC00 \uC4F8 \uB3C4\uAD6C\uB97C \uACE0\uB985\uB2C8\uB2E4. \uC124\uC815\uC5D0\uC11C \uBAA8\uB378\uC5D0 \uBCF4\uC774\uB294 \uC124\uBA85\uC744 \uBC14\uAFC0 \uC218 \uC788\uC73C\uBA70, \uB3C4\uAD6C \uB3D9\uC791\uC740 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Choose tools. Details edits model-facing descriptions only; tool behavior is unchanged.", "\u9009\u62E9\u5DE5\u5177\u3002\u8BE6\u60C5\u53EA\u7F16\u8F91\u6A21\u578B\u53EF\u89C1\u7684\u63CF\u8FF0\uFF0C\u5DE5\u5177\u884C\u4E3A\u4E0D\u53D8\u3002") }),
1629
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "studio-field", children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: t("\uAE30\uBCF8 \uD504\uB9AC\uC14B\uC5D0\uC11C \uD55C \uBC88\uC5D0 \uCD94\uAC00", "Include a built-in preset tool set", "\u5F15\u5165\u5185\u7F6E\u9884\u8BBE\u5DE5\u5177\u96C6") }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "studio-inline-actions", children: toolSources.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !!p.error, "aria-pressed": includesPreset(p.id), onClick: () => togglePreset(p.id), children: p.id === "cordis" ? "creator (cordis)" : p.id }, p.id)) })
1632
+ ] }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ItemPicker, { label: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), items: [...tools.map((tool) => ({ ...tool, searchText: JSON.stringify(tool.parameters), ...GENERIC_DELEGATION_TOOLS.includes(tool.name) ? { disabledReason: t(`${ROLE_TOOL}\uB85C \uD1B5\uD569\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784\uC740 ${ROLE_TOOL}\uC744 \uC120\uD0DD\uD558\uC138\uC694.`, `Merged into ${ROLE_TOOL}. Select ${ROLE_TOOL} for subagent delegation.`, `\u5DF2\u5408\u5E76\u5230 ${ROLE_TOOL}\u3002\u8BF7\u9009\u62E9 ${ROLE_TOOL} \u8FDB\u884C\u5B50\u4EE3\u7406\u59D4\u6D3E\u3002`) } : {} })), ...provided.filter((name) => !tools.some((tool) => tool.name === name)).map((name) => ({ name, description: t("\uD604\uC7AC \uC124\uCE58\uB41C \uB3C4\uAD6C \uBAA9\uB85D\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.", "Absent from current catalog", "\u4E0D\u5728\u5F53\u524D\u76EE\u5F55\u4E2D") }))], selected: provided, onChange: changeTools, labels, isModified: (name) => {
1634
+ const tool = tools.find((item) => item.name === name);
1635
+ const override = toolOverrides[name];
1636
+ const parameters = parameterFields(tool?.parameters);
1637
+ return !!(override?.description !== void 0 && override.description !== tool?.description || Object.entries(override?.parameters ?? {}).some(([path, text]) => text !== parameters.find((p) => p.path === path)?.description) || tool?.promptSections?.some((section) => sections[section.name] !== void 0 && sections[section.name] !== section.text));
1638
+ }, renderDetails: (name) => {
1639
+ const tool = tools.find((tool2) => tool2.name === name);
1640
+ const parameters = parameterFields(tool?.parameters);
1641
+ const override = toolOverrides[name];
1642
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1643
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uB3C4\uAD6C \uC124\uBA85", "Tool description", "\u5DE5\u5177\u63CF\u8FF0"), description: t("\uBAA8\uB378\uC774 \uBCF4\uB294 \uB3C4\uAD6C \uC124\uBA85\uC785\uB2C8\uB2E4.", "The overall tool description sent to the model.", "\u53D1\u9001\u7ED9\u6A21\u578B\u7684\u5DE5\u5177\u6574\u4F53\u63CF\u8FF0\u3002") }),
1644
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "studio-field", children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "studio-sr-only", children: t("\uB3C4\uAD6C \uC124\uBA85", "Tool description", "\u5DE5\u5177\u63CF\u8FF0") }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { value: override?.description ?? tool?.description ?? "", onChange: (e) => setToolOverrides({ ...toolOverrides, [name]: { ...override, description: e.target.value } }) })
1647
+ ] }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uC0AC\uC6A9 \uC9C0\uCE68", "Tool usage instructions", "\u5DE5\u5177\u4F7F\u7528\u6307\u4EE4"), description: t("\uC2DC\uC2A4\uD15C \uD504\uB86C\uD504\uD2B8\uC5D0 \uB4E4\uC5B4\uAC00\uB294 \uC0AC\uC6A9 \uADDC\uCE59\uC785\uB2C8\uB2E4. \uAC19\uC740 \uC139\uC158\uC744 \uC4F0\uB294 \uB3C4\uAD6C\uB294 \uC774 \uC9C0\uCE68\uC744 \uD568\uAED8 \uC501\uB2C8\uB2E4.", "Usage rules included in the system prompt. Tools referencing the same section share these instructions.", "\u5305\u542B\u5728\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u7684\u4F7F\u7528\u89C4\u5219\u3002\u5F15\u7528\u540C\u4E00\u7AE0\u8282\u7684\u5DE5\u5177\u5171\u4EAB\u8FD9\u4E9B\u6307\u4EE4\u3002") }),
1649
+ !tool?.promptSections?.length && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: t("\uD3B8\uC9D1\uD560 \uC218 \uC788\uB294 \uC0AC\uC6A9 \uC9C0\uCE68\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", "No separately editable usage instructions.", "\u6CA1\u6709\u53EF\u5355\u72EC\u7F16\u8F91\u7684\u4F7F\u7528\u6307\u4EE4\u3002") }),
1650
+ tool?.promptSections?.map((section) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { children: [
1651
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "studio-field", children: [
1652
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: section.name }),
1653
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { rows: 5, value: sections[section.name] ?? section.text, onChange: (e) => setSections({ ...sections, [section.name]: e.target.value }) })
1654
+ ] }),
1655
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !Object.hasOwn(sections, section.name), onClick: () => {
1656
+ const next = { ...sections };
1657
+ delete next[section.name];
1658
+ setSections(next);
1659
+ }, children: t("\uC6D0\uB798\uB300\uB85C", "Use original", "\u4F7F\u7528\u539F\u59CB\u503C") })
1660
+ ] }, section.name)),
1661
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uC778\uC790 \uC124\uBA85", "Parameter descriptions", "\u53C2\u6570\u63CF\u8FF0"), description: t("\uC124\uBA85 \uBB38\uAD6C\uB9CC \uBC14\uAFC0 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC774\uB984, \uD0C0\uC785, \uD544\uC218 \uC5EC\uBD80\uB294 \uADF8\uB300\uB85C\uC785\uB2C8\uB2E4.", "Edit only description text within the tool schema. Names, types, required status and constraints remain unchanged.", "\u4EC5\u7F16\u8F91\u5DE5\u5177\u5B9A\u4E49\u4E2D\u7684\u8BF4\u660E\u6587\u5B57\u3002\u540D\u79F0\u3001\u7C7B\u578B\u3001\u5FC5\u586B\u72B6\u6001\u4E0E\u7EA6\u675F\u4FDD\u6301\u4E0D\u53D8\u3002") }),
1662
+ !parameters.length && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: t("\uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC778\uC790\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", "No editable parameters.", "\u6CA1\u6709\u53EF\u7F16\u8F91\u7684\u53C2\u6570\u3002") }),
1663
+ parameters.map((parameter) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "studio-field", children: [
1664
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
1665
+ parameter.path,
1666
+ " ",
1667
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "studio-schema-meta", children: [
1668
+ parameter.type,
1669
+ " \xB7 ",
1670
+ parameter.required ? t("\uD544\uC218", "required", "\u5FC5\u586B") : t("\uC120\uD0DD", "optional", "\u53EF\u9009")
1671
+ ] })
1672
+ ] }),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { "aria-label": parameter.path, value: override?.parameters?.[parameter.path] ?? parameter.description, onChange: (e) => setToolOverrides({ ...toolOverrides, [name]: { ...override, parameters: { ...override?.parameters, [parameter.path]: e.target.value } } }) })
1674
+ ] }, parameter.path)),
1675
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !override, onClick: () => {
1676
+ const next = { ...toolOverrides };
1677
+ delete next[name];
1678
+ setToolOverrides(next);
1679
+ }, children: t("\uC124\uBA85 \uBAA8\uB450 \uC6D0\uB798\uB300\uB85C", "Use original", "\u4F7F\u7528\u539F\u59CB\u503C") })
1680
+ ] });
1681
+ } })
1682
+ ] }),
1683
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1684
+ DetailDialog,
1685
+ {
1686
+ wide: true,
1687
+ open: previewOpen,
1688
+ title: t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Prompt result preview", "\u63D0\u793A\u8BCD\u7ED3\u679C\u9884\u89C8"),
1689
+ closeLabel: t("\uB2EB\uAE30", "Close preview", "\u5173\u95ED\u9884\u89C8"),
1690
+ onClose: closePreview,
1691
+ subtitle: result ? t(`\uC139\uC158 ${result.sections.length}\uAC1C \xB7 \uB3C4\uAD6C ${result.tools.length}\uAC1C`, `${result.sections.length} sections \xB7 ${result.tools.length} tools`, `${result.sections.length} \u4E2A\u7AE0\u8282 \xB7 ${result.tools.length} \u4E2A\u5DE5\u5177`) : void 0,
1692
+ children: [
1693
+ busy && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "status", className: "studio-muted", children: t("\uC870\uB9BD \uC911\u2026", "Assembling\u2026", "\u7EC4\u88C5\u4E2D\u2026") }),
1694
+ error && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "alert", children: error }),
1695
+ result && resultKey !== requestKey && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: t("\uC124\uC815\uC774 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uBBF8\uB9AC\uBCF4\uAE30\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.", "Configuration changed. Run preview again.", "\u914D\u7F6E\u5DF2\u66F4\u6539\uFF0C\u8BF7\u91CD\u65B0\u9884\u89C8\u3002") }),
1696
+ result && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-preview", "aria-label": t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Prompt preview", "\u63D0\u793A\u8BCD\u9884\u89C8"), children: [
1697
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "studio-prompt-document", tabIndex: 0, "aria-label": t("\uC870\uB9BD \uC21C\uC11C\uC640 \uB0B4\uC6A9", "Assembly order and content", "\u7EC4\u88C5\u987A\u5E8F\u548C\u5185\u5BB9"), children: result.sections.map((section, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-prompt-section", children: [
1698
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h3", { children: [
1699
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "studio-prompt-index", children: index + 1 }),
1700
+ section.name
1701
+ ] }),
1702
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "studio-tool-description", children: section.text })
1703
+ ] }, section.name)) }),
1704
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("aside", { className: "studio-prompt-tools", children: [
1705
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h3", { children: [
1706
+ t("\uBAA8\uB378\uC5D0 \uC804\uB2EC\uB418\uB294 \uB3C4\uAD6C", "Model-facing tool schemas", "\u63D0\u4F9B\u7ED9\u6A21\u578B\u7684\u5DE5\u5177\u5B9A\u4E49"),
1707
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "studio-count", children: result.tools.length })
1708
+ ] }),
1709
+ result.tools.length ? result.tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("details", { className: "studio-schema-tool", children: [
1710
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("summary", { children: tool.name }),
1711
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "studio-tool-description", children: tool.description }),
1712
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SchemaValue, { value: tool.parameters })
1713
+ ] }, tool.name)) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "studio-muted", children: labels.empty })
1714
+ ] })
1715
+ ] })
1716
+ ]
1717
+ }
1718
+ )
1719
+ ] });
1720
+ }
1721
+ function SchemaValue({ value }) {
1722
+ if (Array.isArray(value)) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "studio-schema-list", children: value.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SchemaValue, { value: entry }) }, index)) });
1723
+ if (value !== null && typeof value === "object") return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dl", { className: "studio-schema-fields", children: Object.entries(value).map(([key, entry]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1724
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: key }),
1725
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { children: key === "properties" || key === "items" || key === "$defs" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("details", { children: [
1726
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("summary", { children: key }),
1727
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SchemaValue, { value: entry })
1728
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SchemaValue, { value: entry }) })
1729
+ ] }, key)) });
1730
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "studio-tool-description", children: String(value) });
1731
+ }
1732
+ function parameterFields(schema, prefix = "") {
1733
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return [];
1734
+ const row = schema;
1735
+ if (!row.properties || typeof row.properties !== "object" || Array.isArray(row.properties)) return [];
1736
+ return Object.entries(row.properties).flatMap(([key, value]) => {
1737
+ if (!value || typeof value !== "object" || Array.isArray(value)) return [];
1738
+ const field2 = value;
1739
+ const path = prefix ? `${prefix}.${key}` : key;
1740
+ return [{ path, type: Array.isArray(field2.type) ? field2.type.join(" | ") : typeof field2.type === "string" ? field2.type : "\u2014", required: Array.isArray(row.required) && row.required.includes(key), description: typeof field2.description === "string" ? field2.description : "" }, ...parameterFields(field2, path)];
1741
+ });
1742
+ }
1743
+
1744
+ // src/detail-layout.tsx
1745
+ var import_react8 = __toESM(require("react"), 1);
1746
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1747
+ function DetailLayout({ title, subtitle, navigation, pages, selected, onSelect }) {
1748
+ const id = import_react8.default.useId();
1749
+ const scroller = import_react8.default.useRef(null);
1750
+ import_react8.default.useLayoutEffect(() => {
1751
+ if (scroller.current) scroller.current.scrollTop = 0;
1752
+ }, [selected]);
1753
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "studio-layout", children: [
1754
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("nav", { className: "studio-nav", "aria-label": navigation, children: [
1755
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "studio-nav-head", children: [
1756
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { className: "studio-nav-title", children: title }),
1757
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "studio-nav-subtitle", children: subtitle })
1758
+ ] }),
1759
+ pages.map((page, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.default.Fragment, { children: [
1760
+ page.group && page.group !== pages[index - 1]?.group && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "studio-nav-group", children: page.group }),
1761
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1762
+ "button",
1763
+ {
1764
+ type: "button",
1765
+ className: "studio-nav-item",
1766
+ "aria-current": selected === page.id ? "page" : void 0,
1767
+ "aria-controls": `${id}-${page.id}`,
1768
+ onClick: () => onSelect(page.id),
1769
+ children: page.label
1770
+ }
1771
+ )
1772
+ ] }, page.id))
1773
+ ] }),
1774
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "studio-scroller", ref: scroller, "data-studio-scroller": "", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "studio-content", children: pages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1775
+ "section",
1776
+ {
1777
+ id: `${id}-${page.id}`,
1778
+ "aria-label": page.label,
1779
+ hidden: selected !== page.id,
1780
+ children: page.content
1781
+ },
1782
+ page.id
1783
+ )) }) })
1784
+ ] });
1785
+ }
1786
+
1787
+ // src/optimization-prototype.tsx
1788
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1789
+ var initial = { prompt: "Report evidence, uncertainty and remaining work.", tools: "read / grep / glob", spill: "2048", wait: "60000" };
1790
+ var seeds = [
1791
+ { name: "Researcher", role: "research", model: "inherit", reasoning: "inherit", spawn: "fresh", persona: "Investigate primary sources and cite the evidence." },
1792
+ { name: "Reviewer", role: "review", model: "inherit", reasoning: "inherit", spawn: "fork", persona: "Review correctness and identify concrete risks." }
1793
+ ];
1794
+ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, presetActions, extensionActions }) {
1795
+ const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1796
+ const [page, setPage] = import_react9.default.useState("list");
1797
+ const [common, setCommon] = import_react9.default.useState({ ...initial });
1798
+ const [agents, setAgents] = import_react9.default.useState(() => seeds.map((a) => ({ ...a })));
1799
+ const [editing, setEditing] = import_react9.default.useState();
1800
+ const [provided, setProvided] = import_react9.default.useState(defaultProvidedTools);
1801
+ const [tab, setTab] = import_react9.default.useState("tools");
1802
+ const [name, setName] = import_react9.default.useState("");
1803
+ const [description, setDescription] = import_react9.default.useState("");
1804
+ const [draftKey, setDraftKey] = import_react9.default.useState(0);
1805
+ const [revision, setRevision] = import_react9.default.useState(0);
1806
+ const defaults = import_react9.default.useMemo(() => Object.fromEntries((catalog?.plugins ?? []).filter((p) => p.schema && ["tool-bash", "tool-pwsh", "tool-fs", "spill-policy"].includes(p.id)).map((p) => [p.id, {}])), [catalog]);
1807
+ const [configDraft, setConfigDraft] = import_react9.default.useState();
1808
+ const configOverrides = configDraft ?? defaults;
1809
+ const [request, setRequest] = import_react9.default.useState();
1810
+ const [saving, setSaving] = import_react9.default.useState(false);
1811
+ const [saveError, setSaveError] = import_react9.default.useState("");
1812
+ const save = async () => {
1813
+ if (saving || !presetActions || !request || !name.trim()) return;
1814
+ setSaving(true);
1815
+ setSaveError("");
1816
+ try {
1817
+ await presetActions.save(JSON.stringify({ ...editing?.id ? { id: editing.id } : {}, name: name.trim(), description, request: { ...request, configOverrides }, conveniences: { spill: common.spill }, agents }));
1818
+ setRevision((n) => n + 1);
1819
+ setPage("list");
1820
+ setName("");
1821
+ setDescription("");
1822
+ setConfigDraft(void 0);
1823
+ setCommon({ ...initial });
1824
+ setProvided([]);
1825
+ setAgents(seeds.map((a) => ({ ...a })));
1826
+ setEditing(void 0);
1827
+ setTab("tools");
1828
+ setDraftKey((n) => n + 1);
1829
+ } catch (error) {
1830
+ setSaveError(error instanceof Error ? error.message : String(error));
1831
+ } finally {
1832
+ setSaving(false);
1833
+ }
1834
+ };
1835
+ const editPreset = (item, copy2 = false) => {
1836
+ setEditing(copy2 ? { ...item, id: "" } : item);
1837
+ setName(copy2 ? `${item.name} Copy` : item.name);
1838
+ setDescription(item.description);
1839
+ setCommon({ ...initial, prompt: item.request.prompt, spill: item.conveniences?.spill ?? initial.spill });
1840
+ setProvided([...item.request.tools]);
1841
+ setConfigDraft(item.request.configOverrides ?? {});
1842
+ setAgents(item.agents.map((role) => ({ ...role })));
1843
+ setRequest(item.request);
1844
+ setDraftKey((n) => n + 1);
1845
+ setPage("create");
1846
+ };
1847
+ const persona = "@deepseek-ai/dsh-persona";
1848
+ const presetPersona = request?.providerConfigs?.[persona]?.prefix;
1849
+ const masterPersona = catalog?.presets?.find((p) => p.id === (request?.preset ?? "studio-master"))?.promptProviders?.find((p) => p.name === persona)?.fields?.find((f) => f.key === "prefix")?.value;
1850
+ const defaultPersona = typeof presetPersona === "string" ? presetPersona : typeof masterPersona === "string" ? masterPersona : "";
1851
+ const agentPage = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RoleEditor, { locale, roles: agents, onChange: setAgents, loadModels: presetActions?.models, defaultPersona }, draftKey);
1852
+ const tabs = [
1853
+ { id: "tools", label: t("\uD504\uB86C\uD504\uD2B8\xB7\uB3C4\uAD6C", "Prompts & tools", "\u63D0\u793A\u8BCD\u4E0E\u5DE5\u5177"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PromptToolsPrototype, { initialRequest: editing?.request, locale, catalog, catalogError, onPreview: onPreview ? (request2) => onPreview({ ...request2, configOverrides }) : void 0, onRequestChange: setRequest, prompt: common.prompt, onPromptChange: (value) => setCommon((c) => ({ ...c, prompt: value })), provided, onProvidedChange: setProvided }, draftKey) },
1854
+ { id: "convenience", label: t("\uC2E4\uD589 \uC124\uC815", "Execution settings", "\u6267\u884C\u8BBE\u7F6E"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1855
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SectionHead, { title: t("\uC2E4\uD589 \uC124\uC815", "Execution settings", "\u6267\u884C\u8BBE\u7F6E"), description: t("\uD50C\uB7EC\uADF8\uC778 Config \uAC12\uC744 \uC774 \uD504\uB9AC\uC14B\uC5D0\uC11C\uB9CC \uB36E\uC5B4\uC501\uB2C8\uB2E4. \uCF20 Config\uC5D0\uC11C \uBC14\uAFBC \uD56D\uBAA9\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.", "Override plugin Config values for this preset only. Only the fields you change in an enabled Config are saved.", "\u4EC5\u4E3A\u6B64\u9884\u8BBE\u8986\u76D6\u63D2\u4EF6 Config \u503C\u3002\u53EA\u4FDD\u5B58\u5DF2\u542F\u7528 Config \u4E2D\u4FEE\u6539\u7684\u5B57\u6BB5\u3002") }),
1856
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfigEditor, { locale, entries: catalog?.plugins ?? [], value: configOverrides, onChange: setConfigDraft })
1857
+ ] }) },
1858
+ { id: "agents", label: t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8", "Subagent settings", "\u5B50\u4EE3\u7406\u8BBE\u7F6E"), content: agentPage }
1859
+ ];
1860
+ const tabId = import_react9.default.useId();
1861
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DetailLayout, { title: "DSH Studio", subtitle: t("\uC5D0\uC774\uC804\uD2B8 \uD504\uB9AC\uC14B \uD3B8\uC9D1\uAE30", "Agent preset editor", "\u4EE3\u7406\u9884\u8BBE\u7F16\u8F91\u5668"), navigation: t("Studio \uBA54\uB274", "Preset navigation", "\u9884\u8BBE\u5BFC\u822A"), selected: page, onSelect: (next) => {
1862
+ if (next === "create" && editing) {
1863
+ setEditing(void 0);
1864
+ setName("");
1865
+ setDescription("");
1866
+ setCommon({ ...initial });
1867
+ setProvided([]);
1868
+ setConfigDraft(void 0);
1869
+ setAgents(seeds.map((role) => ({ ...role })));
1870
+ setDraftKey((n) => n + 1);
1871
+ }
1872
+ setPage(next);
1873
+ }, pages: [
1874
+ { id: "list", group: t("\uD504\uB9AC\uC14B", "Presets", "\u9884\u8BBE"), label: t("\uD504\uB9AC\uC14B \uBAA9\uB85D", "Preset list", "\u9884\u8BBE\u5217\u8868"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PresetList, { locale, api: presetActions, revision, onEdit: (item) => editPreset(item), onCopy: (item) => editPreset(item, true) }) },
1875
+ { id: "create", group: t("\uD504\uB9AC\uC14B", "Presets", "\u9884\u8BBE"), label: editing ? t("\uD504\uB9AC\uC14B \uD3B8\uC9D1", "Edit preset", "\u7F16\u8F91\u9884\u8BBE") : t("\uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30", "Create preset", "\u521B\u5EFA\u9884\u8BBE"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "studio-page-stack", children: [
1876
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PageHead, { title: editing ? t("\uD504\uB9AC\uC14B \uD3B8\uC9D1", "Edit preset", "\u7F16\u8F91\u9884\u8BBE") : t("\uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30", "Create preset", "\u521B\u5EFA\u9884\u8BBE"), description: editing ? t("\uC800\uC7A5\uD55C \uB4A4 \uD504\uB9AC\uC14B \uBAA9\uB85D\uC5D0\uC11C \uB2E4\uC2DC \uBC18\uC601\uD574\uC57C DSH\uC5D0 \uC801\uC6A9\uB429\uB2C8\uB2E4.", "Saving updates this preset. If it was applied, reapply it from the list to update DSH.", "\u4FDD\u5B58\u4F1A\u66F4\u65B0\u6B64\u9884\u8BBE\u3002\u82E5\u5DF2\u5E94\u7528\uFF0C\u9700\u8981\u5728\u5217\u8868\u4E2D\u91CD\u65B0\u5E94\u7528\u624D\u4F1A\u66F4\u65B0 DSH") : t("\uD504\uB86C\uD504\uD2B8, \uB3C4\uAD6C, \uC2E4\uD589 \uC124\uC815, \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uAD6C\uC131\uD574 \uC0C8 \uD504\uB9AC\uC14B\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.", "Configure tools, execution settings and subagent roles, then save a new preset.", "\u914D\u7F6E\u5DE5\u5177\u3001\u6267\u884C\u8BBE\u7F6E\u548C\u5B50\u4EE3\u7406\u89D2\u8272\uFF0C\u5E76\u4FDD\u5B58\u4E3A\u65B0\u9884\u8BBE"), actions: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { primary: true, disabled: saving || !presetActions || !request || !name.trim(), onClick: () => void save(), children: saving ? t("\uC800\uC7A5 \uC911\u2026", "Saving\u2026", "\u4FDD\u5B58\u4E2D\u2026") : t("\uD504\uB9AC\uC14B \uC800\uC7A5", "Save preset", "\u4FDD\u5B58\u9884\u8BBE") }) }),
1877
+ saveError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Notice, { children: t(`\uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uD3B8\uC9D1\uD55C \uB0B4\uC6A9\uC740 \uD654\uBA74\uC5D0 \uADF8\uB300\uB85C \uC788\uC2B5\uB2C8\uB2E4. (${saveError})`, `Could not save the preset. Your edits remain on this screen. (${saveError})`, `\u65E0\u6CD5\u4FDD\u5B58\u9884\u8BBE\uFF0C\u7F16\u8F91\u5185\u5BB9\u4ECD\u4FDD\u7559\u5728\u6B64\u9875\u9762\uFF08${saveError}\uFF09`) }),
1878
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "studio-identity", children: [
1879
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "studio-field studio-identity-name", children: [
1880
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: t("\uD504\uB9AC\uC14B \uC774\uB984", "Preset name", "\u9884\u8BBE\u540D\u79F0") }),
1881
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { value: name, maxLength: 120, placeholder: t("\uC608: \uB9AC\uC11C\uCE58\uC6A9 \uC5D0\uC774\uC804\uD2B8", "e.g. Research agent", "\u4F8B\u5982\uFF1A\u7814\u7A76\u4EE3\u7406"), onChange: (e) => setName(e.target.value) })
1882
+ ] }),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "studio-field", children: [
1884
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: t("\uC124\uBA85", "Description", "\u63CF\u8FF0") }),
1885
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { value: description, placeholder: t("\uC5B4\uB5A4 \uC791\uC5C5\uC5D0 \uC4F0\uB294 \uD504\uB9AC\uC14B\uC778\uC9C0 \uD55C \uC904\uB85C \uC801\uC5B4 \uC8FC\uC138\uC694 (\uC120\uD0DD)", "One line on what this preset is for (optional)", "\u4E00\u53E5\u8BDD\u8BF4\u660E\u6B64\u9884\u8BBE\u7684\u7528\u9014\uFF08\u53EF\u9009\uFF09"), onChange: (e) => setDescription(e.target.value) })
1886
+ ] })
1887
+ ] }),
1888
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "studio-tabs studio-editor-tabs", role: "tablist", "aria-label": t("\uD504\uB9AC\uC14B \uAD6C\uC131", "Preset configuration", "\u9884\u8BBE\u914D\u7F6E"), children: tabs.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", role: "tab", className: "studio-tab", id: `${tabId}-${item.id}-tab`, "aria-controls": `${tabId}-${item.id}`, "aria-selected": tab === item.id, tabIndex: tab === item.id ? 0 : -1, onClick: () => setTab(item.id), onKeyDown: (e) => {
1889
+ const next = e.key === "ArrowRight" ? (index + 1) % tabs.length : e.key === "ArrowLeft" ? (index + tabs.length - 1) % tabs.length : e.key === "Home" ? 0 : e.key === "End" ? tabs.length - 1 : -1;
1890
+ if (next < 0) return;
1891
+ e.preventDefault();
1892
+ setTab(tabs[next].id);
1893
+ document.getElementById(`${tabId}-${tabs[next].id}-tab`)?.focus();
1894
+ }, children: item.label }, item.id)) }),
1895
+ tabs.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("section", { role: "tabpanel", className: "studio-tab-body", id: `${tabId}-${item.id}`, "aria-labelledby": `${tabId}-${item.id}-tab`, hidden: tab !== item.id, children: item.content }, item.id))
1896
+ ] }) },
1897
+ { id: "providers", group: t("\uB3C4\uAD6C", "Tools", "\u5DE5\u5177"), label: t("\uB3C4\uAD6C \uC81C\uACF5\uC790", "Tool providers", "\u5DE5\u5177\u63D0\u4F9B\u8005"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MasterExtensionsEditor, { locale, actions: extensionActions }) }
1898
+ ] });
1899
+ }
1900
+
1901
+ // src/shell.tsx
1902
+ var import_react11 = __toESM(require("react"), 1);
1903
+ var import_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
1904
+
1905
+ // src/service-layout.tsx
1906
+ var import_react10 = __toESM(require("react"), 1);
1907
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1908
+ var ko = { navigation: "Studio \uC11C\uBE44\uC2A4", empty: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC11C\uBE44\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." };
1909
+ var en = { navigation: "Studio services", empty: "No services are available." };
1910
+ var zh = { navigation: "Studio \u670D\u52A1", empty: "\u6CA1\u6709\u53EF\u7528\u7684\u670D\u52A1" };
1911
+ function ServiceLayout({ locale, services, renderSlot }) {
1912
+ const t = locale.startsWith("ko") ? ko : locale.startsWith("zh") ? zh : en;
1913
+ const [selection, setSelection] = import_react10.default.useState(null);
1914
+ const active = services.some((service) => service.id === selection) ? selection : services[0]?.id;
1915
+ import_react10.default.useEffect(() => {
1916
+ if (selection !== null && !services.some((service) => service.id === selection)) setSelection(null);
1917
+ }, [selection, services]);
1918
+ const id = import_react10.default.useId();
1919
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "studio-services", children: [
1920
+ services.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("nav", { className: "studio-service-nav", "aria-label": t.navigation, children: services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1921
+ "button",
1922
+ {
1923
+ type: "button",
1924
+ className: "studio-nav-item",
1925
+ "aria-current": service.id === active ? "page" : void 0,
1926
+ "aria-controls": `${id}-${service.id}`,
1927
+ onClick: () => setSelection(service.id),
1928
+ children: service.label
1929
+ },
1930
+ service.id
1931
+ )) }),
1932
+ !services.length && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { role: "status", className: "studio-muted", children: t.empty }),
1933
+ services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1934
+ "section",
1935
+ {
1936
+ className: "studio-service-panel",
1937
+ id: `${id}-${service.id}`,
1938
+ hidden: service.id !== active,
1939
+ children: renderSlot("local.dsh-studio.services", { active: service.id === active }, { only: service.id })
1940
+ },
1941
+ service.id
1942
+ ))
1943
+ ] });
1944
+ }
1945
+
1946
+ // src/shell.tsx
1947
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1948
+ var inject = ["slots", "locale", "remote"];
1949
+ var panel = "local-dsh-studio";
1950
+ async function apply(ctx) {
1951
+ const remote = ctx.get("remote");
1952
+ await remote.$mount({ package: PACKAGE_NAME, descriptors: [{
1953
+ id: `${PACKAGE_NAME}#studioCatalog/read`,
1954
+ service: "studioCatalog",
1955
+ namespace: "studioCatalog",
1956
+ method: "read",
1957
+ invocation: { kind: "direct" },
1958
+ parameters: [],
1959
+ result: { mode: "src-json" }
1960
+ }, {
1961
+ id: `${PACKAGE_NAME}#studioCatalog/preview`,
1962
+ service: "studioCatalog",
1963
+ namespace: "studioCatalog",
1964
+ method: "preview",
1965
+ invocation: { kind: "direct" },
1966
+ parameters: [{ name: "request", wire: "request", source: "json", codec: { mode: "strict", typeSymbol: "PreviewRequest", create: () => ({ parse: parsePreviewRequest }) } }],
1967
+ result: { mode: "src-json" }
1968
+ }, ...["listPresets", "savePreset", "applyPreset", "unapplyPreset", "roleModels", "deletePreset", "readExtensions", "saveExtensions"].map((method) => ({
1969
+ id: `${PACKAGE_NAME}#studioCatalog/${method}`,
1970
+ service: "studioCatalog",
1971
+ namespace: "studioCatalog",
1972
+ method,
1973
+ invocation: { kind: "direct" },
1974
+ parameters: ["listPresets", "readExtensions", "roleModels"].includes(method) ? [] : [{ name: ["savePreset", "saveExtensions"].includes(method) ? "payload" : "id", wire: ["savePreset", "saveExtensions"].includes(method) ? "payload" : "id", source: "json", codec: { mode: "strict", typeSymbol: "string", create: () => ({ parse: (value) => {
1975
+ if (typeof value !== "string") throw new TypeError("Expected a string");
1976
+ return value;
1977
+ } }) } }],
1978
+ result: { mode: "src-json" }
1979
+ }))] });
1980
+ ctx.effect(() => ctx.locale.addLanguage({ id: "ko", label: "\uD55C\uAD6D\uC5B4", fallback: "en" }));
1981
+ const serviceListeners = /* @__PURE__ */ new Set();
1982
+ let services = [];
1983
+ const syncServices = () => {
1984
+ services = ctx.slots.entriesOfSlot("local.dsh-studio.services").slice().sort((a, b) => (a.options.order ?? 0) - (b.options.order ?? 0)).map(({ options }) => ({
1985
+ id: options.id,
1986
+ label: (0, import_dsh_client_ui_slots.resolveSlotLabel)(options.label) ?? options.id
1987
+ }));
1988
+ serviceListeners.forEach((listener) => listener());
1989
+ };
1990
+ ctx.effect(() => ctx.slots.subscribe("local.dsh-studio.services", syncServices));
1991
+ ctx.effect(() => ctx.locale.subscribe(syncServices));
1992
+ function StudioServices({ renderSlot }) {
1993
+ const items = import_react11.default.useSyncExternalStore((listener) => {
1994
+ serviceListeners.add(listener);
1995
+ return () => {
1996
+ serviceListeners.delete(listener);
1997
+ };
1998
+ }, () => services);
1999
+ const locale = import_react11.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2000
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StudioSurface, { className: "studio-shell", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("main", { className: "studio-page", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ServiceLayout, { locale, services: items, renderSlot }) }) });
2001
+ }
2002
+ function Icon({ size = 20 }) {
2003
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2004
+ "svg",
2005
+ {
2006
+ width: size,
2007
+ height: size,
2008
+ viewBox: "0 0 24 24",
2009
+ fill: "none",
2010
+ stroke: "currentColor",
2011
+ strokeWidth: "1.7",
2012
+ "aria-hidden": "true",
2013
+ children: [
2014
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "3", width: "7", height: "7", rx: "2" }),
2015
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "14", y: "3", width: "7", height: "7", rx: "2" }),
2016
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "14", width: "7", height: "7", rx: "2" }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M17.5 14v7M14 17.5h7" })
2018
+ ]
2019
+ }
2020
+ );
2021
+ }
2022
+ ctx.slots.inject(
2023
+ "main",
2024
+ () => ctx.slots.register(
2025
+ {
2026
+ name: "main",
2027
+ key: panel,
2028
+ children: {
2029
+ "local.dsh-studio.services": { kind: "list", scope: "root" }
2030
+ }
2031
+ },
2032
+ StudioServices
2033
+ )
2034
+ );
2035
+ function OptimizationPage() {
2036
+ const locale = import_react11.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2037
+ const [catalog, setCatalog] = import_react11.default.useState();
2038
+ const [catalogError, setCatalogError] = import_react11.default.useState("");
2039
+ import_react11.default.useEffect(() => {
2040
+ let active = true;
2041
+ const remote2 = ctx.get("remote.studioCatalog");
2042
+ if (!remote2) {
2043
+ setCatalogError("Studio catalog API unavailable");
2044
+ return;
2045
+ }
2046
+ remote2.read().then((result) => {
2047
+ if (!active) return;
2048
+ if (result.ok) setCatalog(result.value);
2049
+ else setCatalogError(result.error?.message ?? "Catalog request failed");
2050
+ }, (error) => {
2051
+ if (active) setCatalogError(String(error));
2052
+ });
2053
+ return () => {
2054
+ active = false;
2055
+ };
2056
+ }, []);
2057
+ const presetActions = import_react11.default.useMemo(() => {
2058
+ const call = async (method, ...args) => {
2059
+ const service = ctx.get("remote.studioCatalog");
2060
+ const result = await service[method](...args);
2061
+ if (!result.ok) throw new Error(result.error?.message ?? "Preset request failed");
2062
+ return result.value;
2063
+ };
2064
+ return {
2065
+ list: () => call("listPresets"),
2066
+ save: (payload) => call("savePreset", payload),
2067
+ apply: (id) => call("applyPreset", id),
2068
+ models: () => call("roleModels"),
2069
+ unapply: (id) => call("unapplyPreset", id),
2070
+ delete: (id) => call("deletePreset", id)
2071
+ };
2072
+ }, []);
2073
+ const extensionActions = import_react11.default.useMemo(() => {
2074
+ const call = async (method, ...args) => {
2075
+ const service = ctx.get("remote.studioCatalog");
2076
+ const result = await service[method](...args);
2077
+ if (!result.ok) throw new Error(result.error?.message ?? "Master update failed");
2078
+ return result.value;
2079
+ };
2080
+ return { read: () => call("readExtensions"), save: async (payload) => {
2081
+ setCatalog(await call("saveExtensions", payload));
2082
+ } };
2083
+ }, []);
2084
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OptimizationPrototype, { extensionActions, presetActions, locale, catalog, catalogError, onPreview: async (request) => {
2085
+ const service = ctx.get("remote.studioCatalog");
2086
+ const response = await service.preview(request);
2087
+ if (!response.ok) throw new Error(response.error?.message ?? "Preview failed");
2088
+ return response.value;
2089
+ } });
2090
+ }
2091
+ ctx.slots.inject("local.dsh-studio.services", () => ctx.slots.register({
2092
+ name: "local.dsh-studio.services",
2093
+ id: "optimization-prototype",
2094
+ order: 0,
2095
+ label: () => ({ ko: "\uD504\uB9AC\uC14B", en: "Presets", zh: "\u9884\u8BBE" })[ctx.locale.getLocale().active] ?? "Presets"
2096
+ }, OptimizationPage));
2097
+ syncServices();
2098
+ ctx.slots.inject(
2099
+ "sidebar.panellist",
2100
+ () => ctx.slots.register(
2101
+ {
2102
+ name: "sidebar.panellist",
2103
+ id: panel,
2104
+ order: 20,
2105
+ label: "DSH Studio"
2106
+ },
2107
+ Icon
2108
+ )
2109
+ );
2110
+ }
2111
+ return module.exports; }});