dsh-modellix 0.1.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/lib/client.js ADDED
@@ -0,0 +1,3653 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-modellix",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/client/design-state.ts
11
+ function selectedDesignModel(snapshot) {
12
+ if (snapshot.selectedModelId === null) return null;
13
+ return snapshot.models.find((model) => model.id === snapshot.selectedModelId) ?? null;
14
+ }
15
+ function canGenerateDesign(input) {
16
+ const { snapshot, draft, missingRequired, invalidFieldCount, interactionBusy } = input;
17
+ return snapshot.enabled && snapshot.credentialReady && selectedDesignModel(snapshot)?.available === true && draft !== null && invalidFieldCount === 0 && !missingRequired && !interactionBusy;
18
+ }
19
+ function isMissingDesignParameter(parameters, path) {
20
+ if (!Object.prototype.hasOwnProperty.call(parameters, path)) return true;
21
+ const value = parameters[path];
22
+ if (value === null || value === "") return true;
23
+ return Array.isArray(value) && value.length === 0;
24
+ }
25
+ /** Validates every constraint present on the client wire; requiredness is separate. */
26
+ function isDesignFieldValueValid(field, value) {
27
+ if (value === void 0 || value === null || value === "") return true;
28
+ if (field.kind === "enum") return field.options.some((option) => Object.is(option.value, value));
29
+ if (field.kind === "number" || field.kind === "integer") {
30
+ if (typeof value !== "number" || !Number.isFinite(value)) return false;
31
+ if (field.kind === "integer" && !Number.isInteger(value)) return false;
32
+ if (field.minimum !== null && value < field.minimum) return false;
33
+ if (field.maximum !== null && value > field.maximum) return false;
34
+ if (field.step !== null && field.step > 0) {
35
+ const steps = (value - (field.minimum ?? 0)) / field.step;
36
+ if (Math.abs(steps - Math.round(steps)) > 1e-9) return false;
37
+ }
38
+ return true;
39
+ }
40
+ if (field.kind === "boolean") return typeof value === "boolean";
41
+ if (field.kind === "array") return Array.isArray(value);
42
+ if (field.kind === "object") return typeof value === "object" && !Array.isArray(value);
43
+ if (typeof value !== "string") return false;
44
+ return field.maxLength === null || value.length <= field.maxLength;
45
+ }
46
+ function designOutcomeTransition(previous, current) {
47
+ if (previous === null) return emptyTransition();
48
+ const previousJobs = new Map(previous.jobs.map((job) => [job.jobId, job.status]));
49
+ const counts = {
50
+ running: 0,
51
+ succeeded: 0,
52
+ failed: 0,
53
+ expired: 0
54
+ };
55
+ for (const job of current.jobs) {
56
+ if (previousJobs.get(job.jobId) === job.status) continue;
57
+ incrementOutcome(counts, job.status);
58
+ }
59
+ return {
60
+ proposalReady: current.proposal !== null && current.proposal.proposalId !== previous.proposal?.proposalId,
61
+ ...counts
62
+ };
63
+ }
64
+ function incrementOutcome(counts, status) {
65
+ switch (status) {
66
+ case "running":
67
+ counts.running += 1;
68
+ break;
69
+ case "succeeded":
70
+ counts.succeeded += 1;
71
+ break;
72
+ case "expired":
73
+ counts.expired += 1;
74
+ break;
75
+ case "failed":
76
+ case "canceled":
77
+ case "submit-unknown":
78
+ counts.failed += 1;
79
+ break;
80
+ }
81
+ }
82
+ function emptyTransition() {
83
+ return {
84
+ proposalReady: false,
85
+ running: 0,
86
+ succeeded: 0,
87
+ failed: 0,
88
+ expired: 0
89
+ };
90
+ }
91
+ //#endregion
92
+ //#region src/shared/design-presentation-codes.ts
93
+ const DESIGN_NOTICE_CODES = [
94
+ "schema-unavailable",
95
+ "schema-invalid",
96
+ "catalog-stale",
97
+ "catalog-unavailable",
98
+ "credential-reloaded"
99
+ ];
100
+ const DESIGN_MODEL_UNAVAILABLE_CODES = ["removed-from-catalog"];
101
+ const DESIGN_FIELD_DISABLED_CODES = ["unsupported-schema-field"];
102
+ const DESIGN_DIAGNOSTIC_CODES = [
103
+ "credential-changed",
104
+ "submit-unknown",
105
+ "generation-failed",
106
+ "result-unavailable",
107
+ "credential-rejected",
108
+ "task-inaccessible",
109
+ "rate-limited",
110
+ "response-invalid",
111
+ "poll-unavailable"
112
+ ];
113
+ //#endregion
114
+ //#region src/shared/design-wire-limits.ts
115
+ /** Closed Design RPC budgets shared by the Host encoder and Client decoder. */
116
+ const DESIGN_WIRE_LIMITS = Object.freeze({
117
+ maxFields: 256,
118
+ maxOptions: 256,
119
+ maxResources: 256,
120
+ maxJsonBytes: 256 * 1024,
121
+ maxJsonDepth: 10,
122
+ maxJsonNodes: 4096
123
+ });
124
+ Object.freeze({
125
+ maxBytes: DESIGN_WIRE_LIMITS.maxJsonBytes,
126
+ maxDepth: DESIGN_WIRE_LIMITS.maxJsonDepth,
127
+ maxNodes: DESIGN_WIRE_LIMITS.maxJsonNodes
128
+ });
129
+ //#endregion
130
+ //#region src/client/contracts.ts
131
+ const MODELLIX_CLIENT_WIRE_VERSION = 1;
132
+ const MODELLIX_RPC_CHANNEL = "/modellix";
133
+ const MODELLIX_RPC_ENDPOINTS = Object.freeze({
134
+ stateGet: "state/get",
135
+ credentialSave: "credential/save",
136
+ onboardingDefer: "onboarding/defer",
137
+ settingsToggles: "settings/toggles",
138
+ credentialRemove: "credential/remove",
139
+ llmRefresh: "llm/refresh",
140
+ designRead: "design/read",
141
+ designRefresh: "design/refresh",
142
+ designSelectModel: "design/select-model",
143
+ designPropose: "design/propose",
144
+ designProposalApply: "design/proposal/apply",
145
+ designProposalReject: "design/proposal/reject",
146
+ designSubmit: "design/submit"
147
+ });
148
+ var ModellixClientContractError = class extends Error {
149
+ constructor(message) {
150
+ super(message);
151
+ this.name = "ModellixClientContractError";
152
+ }
153
+ };
154
+ const MAX_TEXT = 32e3;
155
+ const MAX_SHORT_TEXT = 4096;
156
+ const MAX_ID = 256;
157
+ const MAX_MODELS = 1e3;
158
+ const MAX_FIELDS = DESIGN_WIRE_LIMITS.maxFields;
159
+ const MAX_OPTIONS = DESIGN_WIRE_LIMITS.maxOptions;
160
+ const MAX_JOBS = 1e3;
161
+ const MAX_RESOURCES = DESIGN_WIRE_LIMITS.maxResources;
162
+ const MAX_RESOURCE_URL = 16384;
163
+ const MAX_JSON_DEPTH = DESIGN_WIRE_LIMITS.maxJsonDepth;
164
+ const MAX_JSON_NODES = DESIGN_WIRE_LIMITS.maxJsonNodes;
165
+ const MAX_RPC_RESPONSE_DEPTH = MAX_JSON_DEPTH + 4;
166
+ const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;
167
+ const SAFE_HASH = /^[A-Za-z0-9._:-]{8,256}$/;
168
+ const FORBIDDEN_RESPONSE_KEY = /^(?:api[-_]?key|authorization|secret|password|mask(?:ed)?|last[-_]?four|last4|credential[-_]?value)$/iu;
169
+ function parseSettingsSnapshot(input) {
170
+ assertNoSecretFields(input);
171
+ const root = object(input, "settings snapshot");
172
+ version(root.version);
173
+ const credential = object(root.credential, "credential descriptor");
174
+ const configured = boolean(credential.configured, "credential.configured");
175
+ const source = oneOf(credential.source, [
176
+ "local",
177
+ "env",
178
+ null
179
+ ], "credential.source");
180
+ const writable = boolean(credential.writable, "credential.writable");
181
+ if (!configured && source !== null) throw new ModellixClientContractError("missing Credential cannot expose a source");
182
+ if (configured && source === null) throw new ModellixClientContractError("configured Credential must expose a source");
183
+ if (source === "env" && writable) throw new ModellixClientContractError("environment Credential must be read-only");
184
+ const revision = opaqueRevision(credential.revision);
185
+ if (configured !== (revision !== null)) throw new ModellixClientContractError("Credential revision presence disagrees with configured state");
186
+ return {
187
+ version: 1,
188
+ settingsRevision: natural(root.settingsRevision, "settingsRevision"),
189
+ services: parseServices(root.services),
190
+ credential: {
191
+ configured,
192
+ source,
193
+ writable,
194
+ revision,
195
+ credentialEpoch: natural(credential.credentialEpoch, "credential.credentialEpoch"),
196
+ verification: oneOf(credential.verification, [
197
+ "unknown",
198
+ "unverified",
199
+ "valid",
200
+ "invalid"
201
+ ], "credential.verification"),
202
+ invalidEpoch: credential.invalidEpoch === null ? null : natural(credential.invalidEpoch, "credential.invalidEpoch")
203
+ },
204
+ onboarding: parseOnboarding(root.onboarding),
205
+ llm: parseLlmHealth(root.llm)
206
+ };
207
+ }
208
+ function parseDesignSnapshot(input) {
209
+ assertNoSecretFields(input);
210
+ const root = object(input, "Design snapshot");
211
+ version(root.version);
212
+ const models = array(root.models, "models", MAX_MODELS).map(parseModel);
213
+ const selectedModelId = root.selectedModelId === null ? null : safeId(root.selectedModelId, "selectedModelId");
214
+ if (selectedModelId !== null && !models.some((candidate) => candidate.id === selectedModelId)) throw new ModellixClientContractError("selectedModelId is absent from the model catalog");
215
+ const draft = root.draft === null ? null : parseDraft(root.draft);
216
+ if (draft !== null && draft.modelId !== selectedModelId) throw new ModellixClientContractError("Design draft does not belong to the selected model");
217
+ return {
218
+ version: 1,
219
+ enabled: boolean(root.enabled, "enabled"),
220
+ credentialReady: boolean(root.credentialReady, "credentialReady"),
221
+ models,
222
+ selectedModelId,
223
+ draft,
224
+ proposal: root.proposal === null ? null : parseProposal(root.proposal),
225
+ jobs: array(root.jobs, "jobs", MAX_JOBS).map(parseJob),
226
+ notice: root.notice === null ? null : oneOf(root.notice, DESIGN_NOTICE_CODES, "notice")
227
+ };
228
+ }
229
+ function parseDesignMutation(input) {
230
+ assertNoSecretFields(input);
231
+ const root = object(input, "Design mutation");
232
+ version(root.version);
233
+ if (root.accepted === true) return {
234
+ version: 1,
235
+ accepted: true,
236
+ state: parseDesignSnapshot(root.state)
237
+ };
238
+ if (root.accepted === false) return {
239
+ version: 1,
240
+ accepted: false,
241
+ code: stableErrorCode(object(root.error, "Design mutation error").code)
242
+ };
243
+ return {
244
+ version: 1,
245
+ accepted: true,
246
+ state: parseDesignSnapshot(input)
247
+ };
248
+ }
249
+ function parseAck(input) {
250
+ assertNoSecretFields(input);
251
+ const root = object(input, "acknowledgement");
252
+ version(root.version);
253
+ if (root.accepted !== true) throw new ModellixClientContractError("acknowledgement was not accepted");
254
+ return {
255
+ version: 1,
256
+ accepted: true
257
+ };
258
+ }
259
+ function parseSettingsMutation(input) {
260
+ assertNoSecretFields(input);
261
+ const root = object(input, "settings mutation");
262
+ version(root.version);
263
+ if (root.accepted === true) return {
264
+ version: 1,
265
+ accepted: true,
266
+ state: parseSettingsSnapshot(root.state)
267
+ };
268
+ if (root.accepted !== false) throw new ModellixClientContractError("settings mutation accepted flag is malformed");
269
+ if (root.reason === "credential-changed" || root.reason === "settings-changed") return {
270
+ version: 1,
271
+ accepted: false,
272
+ code: root.reason,
273
+ messageKey: null,
274
+ state: parseSettingsSnapshot(root.state)
275
+ };
276
+ const error = object(root.error, "settings mutation error");
277
+ return {
278
+ version: 1,
279
+ accepted: false,
280
+ code: stableErrorCode(error.code),
281
+ messageKey: stableMessageKey(error.messageKey),
282
+ state: null
283
+ };
284
+ }
285
+ function sanitizeParameters(input) {
286
+ return jsonObject(input, "parameters");
287
+ }
288
+ function safeResourceHref(value) {
289
+ if (value.length < 1 || value.length > MAX_RESOURCE_URL || /\s/u.test(value)) throw new ModellixClientContractError("resource URL is malformed");
290
+ let url;
291
+ try {
292
+ url = new URL(value);
293
+ } catch {
294
+ throw new ModellixClientContractError("resource URL must be absolute");
295
+ }
296
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.hostname.length === 0) throw new ModellixClientContractError("resource URL must be credential-free HTTPS");
297
+ return url.href;
298
+ }
299
+ function parseServices(value) {
300
+ const services = object(value, "services");
301
+ return {
302
+ design: boolean(services.design, "services.design"),
303
+ llm: boolean(services.llm, "services.llm"),
304
+ web: boolean(services.web, "services.web")
305
+ };
306
+ }
307
+ function parseOnboarding(value) {
308
+ const onboarding = object(value, "onboarding");
309
+ return {
310
+ status: oneOf(onboarding.status, [
311
+ "active",
312
+ "completed",
313
+ "deferred"
314
+ ], "onboarding.status"),
315
+ recoveryPending: boolean(onboarding.recoveryPending, "onboarding.recoveryPending"),
316
+ recoveryRequestId: onboarding.recoveryRequestId === null ? null : safeId(onboarding.recoveryRequestId, "onboarding.recoveryRequestId")
317
+ };
318
+ }
319
+ function parseLlmHealth(value) {
320
+ const llm = object(value, "llm");
321
+ return {
322
+ health: oneOf(llm.health, [
323
+ "unknown",
324
+ "missing",
325
+ "disabled",
326
+ "ready",
327
+ "error",
328
+ "policy-blocked"
329
+ ], "llm.health"),
330
+ modelCount: natural(llm.modelCount, "llm.modelCount"),
331
+ refreshedAt: llm.refreshedAt === null ? null : natural(llm.refreshedAt, "llm.refreshedAt")
332
+ };
333
+ }
334
+ function parseModel(value) {
335
+ const model = object(value, "model");
336
+ return {
337
+ id: safeId(model.id, "model.id"),
338
+ label: readable(model.label, "model.label", MAX_SHORT_TEXT),
339
+ kind: oneOf(model.kind, [
340
+ "image",
341
+ "video",
342
+ "audio",
343
+ "unknown"
344
+ ], "model.kind"),
345
+ featured: boolean(model.featured, "model.featured"),
346
+ available: boolean(model.available, "model.available"),
347
+ unavailableReason: model.unavailableReason === null ? null : oneOf(model.unavailableReason, DESIGN_MODEL_UNAVAILABLE_CODES, "model.unavailableReason")
348
+ };
349
+ }
350
+ function parseDraft(value) {
351
+ const draft = object(value, "draft");
352
+ const fields = array(draft.fields, "draft.fields", MAX_FIELDS).map(parseField);
353
+ const primaryInputPath = path(draft.primaryInputPath, "draft.primaryInputPath");
354
+ if (!fields.some((field) => field.path === primaryInputPath)) throw new ModellixClientContractError("primaryInputPath is absent from the Schema IR fields");
355
+ return {
356
+ modelId: safeId(draft.modelId, "draft.modelId"),
357
+ draftRevision: natural(draft.draftRevision, "draft.draftRevision"),
358
+ irContractHash: hash(draft.irContractHash, "draft.irContractHash"),
359
+ primaryInputPath,
360
+ fields,
361
+ parameters: jsonObject(draft.parameters, "draft.parameters")
362
+ };
363
+ }
364
+ function parseField(value) {
365
+ const field = object(value, "field");
366
+ const kind = oneOf(field.kind, [
367
+ "string",
368
+ "number",
369
+ "integer",
370
+ "boolean",
371
+ "enum",
372
+ "array",
373
+ "object",
374
+ "media"
375
+ ], "field.kind");
376
+ const options = array(field.options, "field.options", MAX_OPTIONS).map((candidate) => {
377
+ const option = object(candidate, "field option");
378
+ const optionValue = option.value;
379
+ if (typeof optionValue !== "string" && typeof optionValue !== "number" && typeof optionValue !== "boolean") throw new ModellixClientContractError("field option value must be a scalar");
380
+ if (typeof optionValue === "number" && !Number.isFinite(optionValue)) throw new ModellixClientContractError("field option number must be finite");
381
+ return {
382
+ label: readable(option.label, "field option label", MAX_SHORT_TEXT),
383
+ value: optionValue
384
+ };
385
+ });
386
+ if (kind === "enum" && options.length === 0) throw new ModellixClientContractError("enum field must include options");
387
+ return {
388
+ path: path(field.path, "field.path"),
389
+ label: readable(field.label, "field.label", MAX_SHORT_TEXT),
390
+ description: field.description === null ? null : readable(field.description, "field.description", MAX_TEXT),
391
+ kind,
392
+ widget: oneOf(field.widget, [
393
+ "input",
394
+ "textarea",
395
+ "select",
396
+ "switch",
397
+ "json",
398
+ "media"
399
+ ], "field.widget"),
400
+ required: boolean(field.required, "field.required"),
401
+ options,
402
+ minimum: nullableFinite(field.minimum, "field.minimum"),
403
+ maximum: nullableFinite(field.maximum, "field.maximum"),
404
+ step: nullableFinite(field.step, "field.step"),
405
+ maxLength: field.maxLength === null ? null : natural(field.maxLength, "field.maxLength"),
406
+ disabledReason: field.disabledReason === null ? null : oneOf(field.disabledReason, DESIGN_FIELD_DISABLED_CODES, "field.disabledReason")
407
+ };
408
+ }
409
+ function parseProposal(value) {
410
+ const proposal = object(value, "proposal");
411
+ return {
412
+ proposalId: safeId(proposal.proposalId, "proposal.proposalId"),
413
+ baseDraftRevision: natural(proposal.baseDraftRevision, "proposal.baseDraftRevision"),
414
+ summary: readable(proposal.summary, "proposal.summary", MAX_TEXT),
415
+ changes: array(proposal.changes, "proposal.changes", MAX_FIELDS).map((candidate) => {
416
+ const change = object(candidate, "proposal change");
417
+ return {
418
+ path: path(change.path, "proposal change path"),
419
+ label: readable(change.label, "proposal change label", MAX_SHORT_TEXT),
420
+ before: change.before === void 0 ? void 0 : jsonValue(change.before, "proposal change before"),
421
+ after: change.after === void 0 ? void 0 : jsonValue(change.after, "proposal change after")
422
+ };
423
+ }),
424
+ conflicts: array(proposal.conflicts, "proposal.conflicts", MAX_FIELDS).map((conflict) => readable(conflict, "proposal conflict", MAX_SHORT_TEXT))
425
+ };
426
+ }
427
+ function parseJob(value) {
428
+ const job = object(value, "job");
429
+ return {
430
+ jobId: safeId(job.jobId, "job.jobId"),
431
+ modelId: safeId(job.modelId, "job.modelId"),
432
+ status: oneOf(job.status, [
433
+ "running",
434
+ "succeeded",
435
+ "failed",
436
+ "canceled",
437
+ "submit-unknown",
438
+ "expired"
439
+ ], "job.status"),
440
+ createdAt: isoTimestamp(job.createdAt, "job.createdAt"),
441
+ updatedAt: isoTimestamp(job.updatedAt, "job.updatedAt"),
442
+ resources: array(job.resources, "job.resources", MAX_RESOURCES).map(parseResource),
443
+ diagnostic: job.diagnostic === null ? null : parseDiagnostic(job.diagnostic)
444
+ };
445
+ }
446
+ function parseResource(value) {
447
+ const resource = object(value, "resource");
448
+ return {
449
+ id: safeId(resource.id, "resource.id"),
450
+ kind: oneOf(resource.kind, [
451
+ "image",
452
+ "video",
453
+ "audio"
454
+ ], "resource.kind"),
455
+ url: safeResourceHref(readable(resource.url, "resource.url", MAX_RESOURCE_URL)),
456
+ downloadUrl: safeResourceHref(readable(resource.downloadUrl, "resource.downloadUrl", MAX_RESOURCE_URL)),
457
+ expiresAt: resource.expiresAt === null ? null : isoTimestamp(resource.expiresAt, "resource.expiresAt")
458
+ };
459
+ }
460
+ function parseDiagnostic(value) {
461
+ const diagnostic = object(value, "diagnostic");
462
+ if (Object.hasOwn(diagnostic, "message")) throw new ModellixClientContractError("diagnostic must use a stable code instead of Host prose");
463
+ return {
464
+ code: oneOf(diagnostic.code, DESIGN_DIAGNOSTIC_CODES, "diagnostic.code"),
465
+ retryable: boolean(diagnostic.retryable, "diagnostic.retryable")
466
+ };
467
+ }
468
+ function assertNoSecretFields(value) {
469
+ const seen = /* @__PURE__ */ new WeakSet();
470
+ const visit = (candidate, depth) => {
471
+ if (depth > MAX_RPC_RESPONSE_DEPTH) throw new ModellixClientContractError("RPC response nesting is too deep");
472
+ if (typeof candidate !== "object" || candidate === null) return;
473
+ if (seen.has(candidate)) throw new ModellixClientContractError("RPC response contains a cycle");
474
+ seen.add(candidate);
475
+ if (Array.isArray(candidate)) {
476
+ for (const item of candidate) visit(item, depth + 1);
477
+ return;
478
+ }
479
+ for (const [key, item] of Object.entries(candidate)) {
480
+ if (FORBIDDEN_RESPONSE_KEY.test(key)) throw new ModellixClientContractError("RPC response attempted to expose a Secret-shaped field");
481
+ visit(item, depth + 1);
482
+ }
483
+ };
484
+ visit(value, 0);
485
+ }
486
+ function jsonObject(value, label) {
487
+ const parsed = jsonValue(value, label);
488
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new ModellixClientContractError(`${label} must be a JSON object`);
489
+ return parsed;
490
+ }
491
+ function jsonValue(value, label) {
492
+ const budget = { nodes: 0 };
493
+ const parse = (candidate, depth) => {
494
+ budget.nodes += 1;
495
+ if (budget.nodes > MAX_JSON_NODES || depth > MAX_JSON_DEPTH) throw new ModellixClientContractError(`${label} exceeds the JSON budget`);
496
+ if (candidate === null || typeof candidate === "boolean") return candidate;
497
+ if (typeof candidate === "number") {
498
+ if (!Number.isFinite(candidate)) throw new ModellixClientContractError(`${label} contains a non-finite number`);
499
+ return candidate;
500
+ }
501
+ if (typeof candidate === "string") return readable(candidate, label, MAX_TEXT);
502
+ if (Array.isArray(candidate)) {
503
+ if (candidate.length > MAX_FIELDS) throw new ModellixClientContractError(`${label} contains an oversized array`);
504
+ return candidate.map((item) => parse(item, depth + 1));
505
+ }
506
+ if (typeof candidate === "object") {
507
+ const entries = Object.entries(candidate);
508
+ if (entries.length > MAX_FIELDS) throw new ModellixClientContractError(`${label} contains an oversized object`);
509
+ const result = {};
510
+ for (const [key, item] of entries) {
511
+ path(key, `${label} key`);
512
+ result[key] = parse(item, depth + 1);
513
+ }
514
+ return result;
515
+ }
516
+ throw new ModellixClientContractError(`${label} contains a non-JSON value`);
517
+ };
518
+ return parse(value, 0);
519
+ }
520
+ function object(value, label) {
521
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ModellixClientContractError(`${label} must be an object`);
522
+ return value;
523
+ }
524
+ function array(value, label, maximum) {
525
+ if (!Array.isArray(value) || value.length > maximum) throw new ModellixClientContractError(`${label} must be an array with at most ${maximum} items`);
526
+ return value;
527
+ }
528
+ function boolean(value, label) {
529
+ if (typeof value !== "boolean") throw new ModellixClientContractError(`${label} must be boolean`);
530
+ return value;
531
+ }
532
+ function natural(value, label) {
533
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new ModellixClientContractError(`${label} must be a natural number`);
534
+ return value;
535
+ }
536
+ function nullableFinite(value, label) {
537
+ if (value === null) return null;
538
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new ModellixClientContractError(`${label} must be finite or null`);
539
+ return value;
540
+ }
541
+ function readable(value, label, maximum) {
542
+ if (typeof value !== "string" || value.length > maximum) throw new ModellixClientContractError(`${label} must be a bounded string`);
543
+ for (const character of value) {
544
+ const point = character.codePointAt(0) ?? 0;
545
+ if (point === 0 || point === 127 || point < 32 && !"\n\r ".includes(character)) throw new ModellixClientContractError(`${label} contains a control character`);
546
+ }
547
+ return value;
548
+ }
549
+ function path(value, label) {
550
+ const result = readable(value, label, 512);
551
+ if (result.length === 0) throw new ModellixClientContractError(`${label} cannot be empty`);
552
+ return result;
553
+ }
554
+ function safeId(value, label) {
555
+ if (typeof value !== "string" || value.length > MAX_ID || !SAFE_ID.test(value)) throw new ModellixClientContractError(`${label} is malformed`);
556
+ return value;
557
+ }
558
+ function hash(value, label) {
559
+ if (typeof value !== "string" || !SAFE_HASH.test(value)) throw new ModellixClientContractError(`${label} is malformed`);
560
+ return value;
561
+ }
562
+ function opaqueRevision(value) {
563
+ if (value === null) return null;
564
+ return readable(value, "credential.revision", 256);
565
+ }
566
+ function stableErrorCode(value) {
567
+ if (typeof value !== "string" || !/^(?:MODELLIX_[A-Z0-9_]{1,96}|[a-z][a-z0-9-]{0,63})$/u.test(value)) throw new ModellixClientContractError("mutation error code is malformed");
568
+ return value;
569
+ }
570
+ function stableMessageKey(value) {
571
+ if (typeof value !== "string" || !/^[a-z][a-z0-9]*(?:\.[a-z0-9_-]+){1,15}$/u.test(value) || value.length > 256) throw new ModellixClientContractError("mutation messageKey is malformed");
572
+ return value;
573
+ }
574
+ function isoTimestamp(value, label) {
575
+ const result = readable(value, label, 64);
576
+ if (!Number.isFinite(Date.parse(result))) throw new ModellixClientContractError(`${label} must be an ISO timestamp`);
577
+ return result;
578
+ }
579
+ function oneOf(value, values, label) {
580
+ if (!values.includes(value)) throw new ModellixClientContractError(`${label} has an unsupported value`);
581
+ return value;
582
+ }
583
+ function version(value) {
584
+ if (value !== 1) throw new ModellixClientContractError("RPC wire version is unsupported");
585
+ }
586
+ //#endregion
587
+ //#region src/client/rpc.ts
588
+ var ModellixClientRpcError = class extends Error {
589
+ endpoint;
590
+ code;
591
+ messageKey;
592
+ state;
593
+ constructor(endpoint, code, options = {}) {
594
+ super("The Modellix Host operation could not be completed");
595
+ this.name = "ModellixClientRpcError";
596
+ this.endpoint = endpoint;
597
+ this.code = safeErrorCode(code);
598
+ this.messageKey = options.messageKey ?? null;
599
+ this.state = options.state ?? null;
600
+ }
601
+ };
602
+ var ModellixRpcClient = class {
603
+ #rpc;
604
+ constructor(rpc) {
605
+ this.#rpc = rpc;
606
+ }
607
+ settings(signal) {
608
+ return this.#call(MODELLIX_RPC_ENDPOINTS.stateGet, {}, parseSettingsSnapshot, signal);
609
+ }
610
+ saveOnboarding(apiKey, services, expectedCredentialEpoch, signal) {
611
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.credentialSave, {
612
+ apiKey,
613
+ services,
614
+ expectedCredentialEpoch
615
+ }, signal);
616
+ }
617
+ deferOnboarding(services, expectedSettingsRevision, signal) {
618
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.onboardingDefer, {
619
+ services,
620
+ expectedSettingsRevision
621
+ }, signal);
622
+ }
623
+ updateToggles(services, expectedSettingsRevision, signal) {
624
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.settingsToggles, {
625
+ services,
626
+ expectedSettingsRevision
627
+ }, signal);
628
+ }
629
+ replaceCredential(apiKey, expectedCredentialEpoch, services, signal) {
630
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.credentialSave, {
631
+ apiKey,
632
+ expectedCredentialEpoch,
633
+ services
634
+ }, signal);
635
+ }
636
+ removeCredential(expectedCredentialEpoch, signal) {
637
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.credentialRemove, { expectedCredentialEpoch }, signal);
638
+ }
639
+ refreshLlmCatalog(signal) {
640
+ return this.#settingsMutation(MODELLIX_RPC_ENDPOINTS.llmRefresh, {}, signal);
641
+ }
642
+ design(sessionId, signal) {
643
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designRead, { sessionId }, signal);
644
+ }
645
+ refreshDesignCatalog(sessionId, signal) {
646
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designRefresh, { sessionId }, signal);
647
+ }
648
+ selectDesignModel(sessionId, modelId, signal) {
649
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designSelectModel, {
650
+ sessionId,
651
+ modelId
652
+ }, signal);
653
+ }
654
+ proposeDesignParameters(input, signal) {
655
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designPropose, {
656
+ ...input,
657
+ parameters: sanitizeParameters(input.parameters)
658
+ }, signal);
659
+ }
660
+ applyDesignProposal(sessionId, proposalId, parameters, signal) {
661
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designProposalApply, {
662
+ sessionId,
663
+ proposalId,
664
+ parameters: sanitizeParameters(parameters)
665
+ }, signal);
666
+ }
667
+ rejectDesignProposal(sessionId, proposalId, signal) {
668
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designProposalReject, {
669
+ sessionId,
670
+ proposalId
671
+ }, signal);
672
+ }
673
+ submitDesign(input, signal) {
674
+ return this.#designCall(MODELLIX_RPC_ENDPOINTS.designSubmit, {
675
+ ...input,
676
+ parameters: sanitizeParameters(input.parameters)
677
+ }, signal);
678
+ }
679
+ async #call(endpoint, payload, parse, signal) {
680
+ let result;
681
+ try {
682
+ result = await this.#rpc.call(MODELLIX_RPC_CHANNEL, endpoint, {
683
+ version: 1,
684
+ ...payload
685
+ }, signal);
686
+ } catch (error) {
687
+ throw new ModellixClientRpcError(endpoint, signal?.aborted === true || error instanceof Error && error.name === "AbortError" ? "cancelled" : "transport");
688
+ }
689
+ if (!result.ok) throw new ModellixClientRpcError(endpoint, result.error.code);
690
+ try {
691
+ return parse(result.value);
692
+ } catch {
693
+ throw new ModellixClientRpcError(endpoint, "invalid-response");
694
+ }
695
+ }
696
+ async #settingsMutation(endpoint, payload, signal) {
697
+ const mutation = await this.#call(endpoint, payload, parseSettingsMutation, signal);
698
+ if (mutation.accepted) return mutation.state;
699
+ throw new ModellixClientRpcError(endpoint, mutation.code, {
700
+ messageKey: mutation.messageKey,
701
+ state: mutation.state
702
+ });
703
+ }
704
+ async #designCall(endpoint, payload, signal) {
705
+ const mutation = await this.#call(endpoint, payload, parseDesignMutation, signal);
706
+ if (mutation.accepted) return mutation.state;
707
+ throw new ModellixClientRpcError(endpoint, mutation.code);
708
+ }
709
+ };
710
+ function safeErrorCode(value) {
711
+ return /^(?:MODELLIX_[A-Z0-9_]{1,96}|[a-z][a-z0-9-]{0,63})$/u.test(value) ? value : "internal";
712
+ }
713
+ //#endregion
714
+ //#region src/client/store.ts
715
+ var ResourceStore = class {
716
+ #snapshot = {
717
+ status: "idle",
718
+ data: null,
719
+ pending: null,
720
+ errorCode: null,
721
+ errorOperation: null
722
+ };
723
+ #listeners = /* @__PURE__ */ new Set();
724
+ getSnapshot = () => this.#snapshot;
725
+ subscribe = (listener) => {
726
+ this.#listeners.add(listener);
727
+ return () => this.#listeners.delete(listener);
728
+ };
729
+ publish(snapshot) {
730
+ this.#snapshot = snapshot;
731
+ for (const listener of this.#listeners) listener();
732
+ }
733
+ };
734
+ var ResourceController = class {
735
+ store = new ResourceStore();
736
+ #generation = 0;
737
+ async perform(operation, task) {
738
+ const generation = ++this.#generation;
739
+ const previous = this.store.getSnapshot();
740
+ this.store.publish({
741
+ status: previous.data === null ? "loading" : previous.status,
742
+ data: previous.data,
743
+ pending: operation,
744
+ errorCode: null,
745
+ errorOperation: null
746
+ });
747
+ try {
748
+ const data = await task();
749
+ if (generation !== this.#generation) return false;
750
+ this.store.publish({
751
+ status: "ready",
752
+ data,
753
+ pending: null,
754
+ errorCode: null,
755
+ errorOperation: null
756
+ });
757
+ return true;
758
+ } catch (error) {
759
+ if (generation !== this.#generation) return false;
760
+ const errorCode = clientErrorCode(error);
761
+ if (errorCode === "cancelled") {
762
+ this.store.publish({
763
+ ...previous,
764
+ pending: null,
765
+ errorCode: null,
766
+ errorOperation: null
767
+ });
768
+ return false;
769
+ }
770
+ const latest = error instanceof ModellixClientRpcError && error.state !== null ? error.state : previous.data;
771
+ this.store.publish({
772
+ status: "error",
773
+ data: latest,
774
+ pending: null,
775
+ errorCode,
776
+ errorOperation: operation
777
+ });
778
+ return false;
779
+ }
780
+ }
781
+ publishData(data) {
782
+ ++this.#generation;
783
+ this.store.publish({
784
+ status: "ready",
785
+ data,
786
+ pending: null,
787
+ errorCode: null,
788
+ errorOperation: null
789
+ });
790
+ }
791
+ };
792
+ var SettingsController = class extends ResourceController {
793
+ #rpc;
794
+ constructor(rpc) {
795
+ super();
796
+ this.#rpc = rpc;
797
+ }
798
+ load(signal) {
799
+ return this.perform("load", () => this.#rpc.settings(signal));
800
+ }
801
+ saveOnboarding(apiKey, services, expectedCredentialEpoch, signal) {
802
+ return this.perform("save-onboarding", () => this.#rpc.saveOnboarding(apiKey, services, expectedCredentialEpoch, signal));
803
+ }
804
+ async deferOnboarding(services, expectedSettingsRevision, signal) {
805
+ return this.perform("defer-onboarding", () => this.#rpc.deferOnboarding(services, expectedSettingsRevision, signal));
806
+ }
807
+ updateToggles(services, expectedSettingsRevision, signal) {
808
+ return this.perform("save-toggles", () => this.#rpc.updateToggles(services, expectedSettingsRevision, signal));
809
+ }
810
+ replaceCredential(apiKey, expectedCredentialEpoch, services, signal) {
811
+ return this.perform("replace-credential", () => this.#rpc.replaceCredential(apiKey, expectedCredentialEpoch, services, signal));
812
+ }
813
+ removeCredential(expectedCredentialEpoch, signal) {
814
+ return this.perform("remove-credential", () => this.#rpc.removeCredential(expectedCredentialEpoch, signal));
815
+ }
816
+ async refreshLlmCatalog(signal) {
817
+ return this.perform("refresh-llm", () => this.#rpc.refreshLlmCatalog(signal));
818
+ }
819
+ };
820
+ var DesignController = class extends ResourceController {
821
+ #rpc;
822
+ #proposeInFlight = null;
823
+ #submitInFlight = null;
824
+ sessionId;
825
+ constructor(rpc, sessionId) {
826
+ super();
827
+ this.#rpc = rpc;
828
+ this.sessionId = sessionId;
829
+ }
830
+ load(signal) {
831
+ return this.perform("load", () => this.#rpc.design(this.sessionId, signal));
832
+ }
833
+ refreshCatalog(signal) {
834
+ return this.perform("refresh-design", () => this.#rpc.refreshDesignCatalog(this.sessionId, signal));
835
+ }
836
+ selectModel(modelId, signal) {
837
+ return this.perform("select-model", () => this.#rpc.selectDesignModel(this.sessionId, modelId, signal));
838
+ }
839
+ propose(instruction, parameters, signal) {
840
+ const current = this.store.getSnapshot();
841
+ if (current.pending !== null || this.#proposeInFlight !== null) return Promise.resolve(false);
842
+ const snapshot = current.data;
843
+ const draft = snapshot?.draft;
844
+ if (draft === null || draft === void 0) return Promise.resolve(false);
845
+ if (snapshot === null || selectedDesignModel(snapshot)?.available !== true) return Promise.resolve(false);
846
+ const proposal = this.perform("propose", () => this.#rpc.proposeDesignParameters({
847
+ sessionId: this.sessionId,
848
+ modelId: draft.modelId,
849
+ instruction,
850
+ draftRevision: draft.draftRevision,
851
+ irContractHash: draft.irContractHash,
852
+ parameters
853
+ }, signal));
854
+ this.#proposeInFlight = proposal;
855
+ const release = () => {
856
+ if (this.#proposeInFlight === proposal) this.#proposeInFlight = null;
857
+ };
858
+ proposal.then(release, release);
859
+ return proposal;
860
+ }
861
+ applyProposal(proposalId, parameters, signal) {
862
+ return this.perform("apply-proposal", () => this.#rpc.applyDesignProposal(this.sessionId, proposalId, parameters, signal));
863
+ }
864
+ rejectProposal(proposalId, signal) {
865
+ return this.perform("reject-proposal", () => this.#rpc.rejectDesignProposal(this.sessionId, proposalId, signal));
866
+ }
867
+ submit(parameters, signal) {
868
+ const current = this.store.getSnapshot();
869
+ if (current.pending !== null || this.#submitInFlight !== null) return Promise.resolve(false);
870
+ const snapshot = current.data;
871
+ const draft = snapshot?.draft;
872
+ if (draft === null || draft === void 0) return Promise.resolve(false);
873
+ if (snapshot === null || selectedDesignModel(snapshot)?.available !== true) return Promise.resolve(false);
874
+ const submission = this.perform("submit", () => this.#rpc.submitDesign({
875
+ sessionId: this.sessionId,
876
+ modelId: draft.modelId,
877
+ draftRevision: draft.draftRevision,
878
+ irContractHash: draft.irContractHash,
879
+ parameters
880
+ }, signal));
881
+ this.#submitInFlight = submission;
882
+ const release = () => {
883
+ if (this.#submitInFlight === submission) this.#submitInFlight = null;
884
+ };
885
+ submission.then(release, release);
886
+ return submission;
887
+ }
888
+ };
889
+ function clientErrorCode(error) {
890
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && /^(?:[a-z][a-z0-9-]{0,63}|MODELLIX_[A-Z0-9_]{1,55})$/u.test(error.code)) return error.code;
891
+ return "internal";
892
+ }
893
+ //#endregion
894
+ //#region src/client/a11y.ts
895
+ const FOCUSABLE_SELECTOR = [
896
+ "a[href]",
897
+ "button:not([disabled])",
898
+ "input:not([disabled])",
899
+ "select:not([disabled])",
900
+ "textarea:not([disabled])",
901
+ "[tabindex]:not([tabindex=\"-1\"])"
902
+ ].join(",");
903
+ const DOCUMENT_SCROLL_LOCKS = /* @__PURE__ */ new WeakMap();
904
+ const APPLICATION_INERT_LOCKS = /* @__PURE__ */ new WeakMap();
905
+ const MODELLIX_DIALOG_SENTINEL = "[data-mdlx-dialog-surface]";
906
+ const MODAL_DIALOG_SELECTOR = "[role=\"dialog\"][aria-modal=\"true\"]";
907
+ /**
908
+ * Defers a plugin dialog while another accessible modal owns the page.
909
+ *
910
+ * The public host API does not expose a modal coordinator. This arbitration
911
+ * therefore uses only the ARIA modal contract and a marker owned by this
912
+ * plugin; it does not depend on host classes or DOM structure.
913
+ */
914
+ function useExternalDialogGate(requestedOpen) {
915
+ const [decision, setDecision] = (0, react.useState)({
916
+ requestedOpen: false,
917
+ allowed: false
918
+ });
919
+ (0, react.useEffect)(() => {
920
+ if (!requestedOpen || typeof document === "undefined") {
921
+ setDecision((current) => current.requestedOpen || current.allowed ? {
922
+ requestedOpen: false,
923
+ allowed: false
924
+ } : current);
925
+ return;
926
+ }
927
+ const ownerDocument = document;
928
+ const Observer = ownerDocument.defaultView?.MutationObserver;
929
+ let disposed = false;
930
+ let reconciliationQueued = false;
931
+ const reconcile = () => {
932
+ reconciliationQueued = false;
933
+ if (disposed) return;
934
+ const allowed = !hasVisibleExternalDialog(ownerDocument);
935
+ setDecision((current) => current.requestedOpen && current.allowed === allowed ? current : {
936
+ requestedOpen: true,
937
+ allowed
938
+ });
939
+ };
940
+ const queueReconciliation = () => {
941
+ if (reconciliationQueued) return;
942
+ reconciliationQueued = true;
943
+ queueMicrotask(reconcile);
944
+ };
945
+ const observer = Observer === void 0 ? null : new Observer(queueReconciliation);
946
+ observer?.observe(ownerDocument.documentElement, {
947
+ subtree: true,
948
+ childList: true,
949
+ attributes: true,
950
+ attributeFilter: [
951
+ "aria-hidden",
952
+ "aria-modal",
953
+ "class",
954
+ "hidden",
955
+ "inert",
956
+ "role",
957
+ "style"
958
+ ]
959
+ });
960
+ queueReconciliation();
961
+ return () => {
962
+ disposed = true;
963
+ observer?.disconnect();
964
+ };
965
+ }, [requestedOpen]);
966
+ return requestedOpen && decision.requestedOpen && decision.allowed;
967
+ }
968
+ /** Adds the focus behavior intentionally absent from the public Modal primitive. */
969
+ function useDialogA11y(options) {
970
+ const { open, container, initialFocusSelector, mandatory, onEscape } = options;
971
+ const onEscapeRef = (0, react.useRef)(onEscape);
972
+ const restoreTargetRef = (0, react.useRef)(null);
973
+ onEscapeRef.current = onEscape;
974
+ (0, react.useEffect)(() => {
975
+ if (!open || typeof document === "undefined") return;
976
+ const activeBeforeOpen = document.activeElement instanceof HTMLElement ? document.activeElement : null;
977
+ restoreTargetRef.current ??= activeBeforeOpen;
978
+ const dialogRoot = container.current;
979
+ const appRoot = document.getElementById("root");
980
+ const unlockInert = lockApplicationRoot(appRoot);
981
+ const unlockScroll = lockDocumentScroll(document);
982
+ const focusInitial = () => {
983
+ const root = container.current;
984
+ if (root === null) return;
985
+ const preferred = initialFocusSelector === void 0 ? null : root.querySelector(initialFocusSelector);
986
+ const first = focusableElements(root)[0];
987
+ (preferred ?? first ?? root).focus();
988
+ };
989
+ queueMicrotask(focusInitial);
990
+ const onKeyDown = (event) => {
991
+ if (event.key === "Escape") {
992
+ event.preventDefault();
993
+ event.stopImmediatePropagation();
994
+ if (!mandatory) onEscapeRef.current();
995
+ return;
996
+ }
997
+ if (event.key !== "Tab") return;
998
+ const root = container.current;
999
+ if (root === null) return;
1000
+ const items = focusableElements(root);
1001
+ if (items.length === 0) {
1002
+ event.preventDefault();
1003
+ root.focus();
1004
+ return;
1005
+ }
1006
+ const first = items[0];
1007
+ const last = items[items.length - 1];
1008
+ const active = document.activeElement;
1009
+ if (event.shiftKey && (active === first || !root.contains(active))) {
1010
+ event.preventDefault();
1011
+ last?.focus();
1012
+ } else if (!event.shiftKey && (active === last || !root.contains(active))) {
1013
+ event.preventDefault();
1014
+ first?.focus();
1015
+ }
1016
+ };
1017
+ document.addEventListener("keydown", onKeyDown, true);
1018
+ return () => {
1019
+ document.removeEventListener("keydown", onKeyDown, true);
1020
+ unlockInert();
1021
+ unlockScroll();
1022
+ if (!hasVisibleExternalDialog(document)) {
1023
+ restoreFocus(restoreTargetRef.current, dialogRoot, appRoot);
1024
+ restoreTargetRef.current = null;
1025
+ }
1026
+ };
1027
+ }, [
1028
+ container,
1029
+ initialFocusSelector,
1030
+ mandatory,
1031
+ open
1032
+ ]);
1033
+ }
1034
+ function lockApplicationRoot(appRoot) {
1035
+ if (appRoot === null) return () => void 0;
1036
+ const current = APPLICATION_INERT_LOCKS.get(appRoot);
1037
+ if (current !== void 0) {
1038
+ current.count += 1;
1039
+ return () => releaseApplicationRoot(appRoot);
1040
+ }
1041
+ APPLICATION_INERT_LOCKS.set(appRoot, {
1042
+ count: 1,
1043
+ inert: appRoot.inert === true
1044
+ });
1045
+ appRoot.inert = true;
1046
+ return () => releaseApplicationRoot(appRoot);
1047
+ }
1048
+ function releaseApplicationRoot(appRoot) {
1049
+ const current = APPLICATION_INERT_LOCKS.get(appRoot);
1050
+ if (current === void 0) return;
1051
+ current.count -= 1;
1052
+ if (current.count > 0) return;
1053
+ appRoot.inert = current.inert;
1054
+ APPLICATION_INERT_LOCKS.delete(appRoot);
1055
+ }
1056
+ function lockDocumentScroll(ownerDocument) {
1057
+ const current = DOCUMENT_SCROLL_LOCKS.get(ownerDocument);
1058
+ if (current !== void 0) {
1059
+ current.count += 1;
1060
+ return () => releaseDocumentScroll(ownerDocument);
1061
+ }
1062
+ const html = ownerDocument.documentElement;
1063
+ const body = ownerDocument.body;
1064
+ const created = {
1065
+ count: 1,
1066
+ html,
1067
+ body,
1068
+ htmlOverflow: html.style.overflow,
1069
+ htmlOverscrollBehavior: html.style.overscrollBehavior,
1070
+ bodyOverflow: body.style.overflow,
1071
+ bodyOverscrollBehavior: body.style.overscrollBehavior
1072
+ };
1073
+ DOCUMENT_SCROLL_LOCKS.set(ownerDocument, created);
1074
+ html.style.overflow = "hidden";
1075
+ html.style.overscrollBehavior = "contain";
1076
+ body.style.overflow = "hidden";
1077
+ body.style.overscrollBehavior = "contain";
1078
+ return () => releaseDocumentScroll(ownerDocument);
1079
+ }
1080
+ function releaseDocumentScroll(ownerDocument) {
1081
+ const current = DOCUMENT_SCROLL_LOCKS.get(ownerDocument);
1082
+ if (current === void 0) return;
1083
+ current.count -= 1;
1084
+ if (current.count > 0) return;
1085
+ current.html.style.overflow = current.htmlOverflow;
1086
+ current.html.style.overscrollBehavior = current.htmlOverscrollBehavior;
1087
+ current.body.style.overflow = current.bodyOverflow;
1088
+ current.body.style.overscrollBehavior = current.bodyOverscrollBehavior;
1089
+ DOCUMENT_SCROLL_LOCKS.delete(ownerDocument);
1090
+ }
1091
+ function clearSecretInput(container) {
1092
+ const input = container?.querySelector("[data-mdlx-secret]");
1093
+ if (input !== null && input !== void 0) input.value = "";
1094
+ }
1095
+ function focusableElements(root) {
1096
+ return [...root.querySelectorAll(FOCUSABLE_SELECTOR)].filter((candidate) => isUsableFocusTarget(candidate, null));
1097
+ }
1098
+ function restoreFocus(previous, dialog, appRoot) {
1099
+ const ownerDocument = dialog?.ownerDocument ?? previous?.ownerDocument ?? document;
1100
+ if (previous !== null && isUsableFocusTarget(previous, dialog) && focus(previous)) return;
1101
+ const roots = [
1102
+ ownerDocument.querySelector("main"),
1103
+ ownerDocument.querySelector("[role=\"main\"]"),
1104
+ appRoot
1105
+ ];
1106
+ const visited = /* @__PURE__ */ new Set();
1107
+ for (const root of roots) {
1108
+ if (root === null || visited.has(root)) continue;
1109
+ visited.add(root);
1110
+ const fallback = [...root.querySelectorAll(FOCUSABLE_SELECTOR)].find((candidate) => isUsableFocusTarget(candidate, dialog));
1111
+ if (fallback !== void 0 && focus(fallback)) return;
1112
+ }
1113
+ }
1114
+ function hasVisibleExternalDialog(ownerDocument) {
1115
+ return [...ownerDocument.querySelectorAll(MODAL_DIALOG_SELECTOR)].some((candidate) => candidate.querySelector(MODELLIX_DIALOG_SENTINEL) === null && isVisibleDialog(candidate));
1116
+ }
1117
+ function isVisibleDialog(candidate) {
1118
+ if (candidate.tagName === "DIALOG" && !candidate.hasAttribute("open")) return false;
1119
+ let current = candidate;
1120
+ while (current !== null) {
1121
+ if (current.hidden || current === candidate && current.inert || current.getAttribute("aria-hidden") === "true") return false;
1122
+ const style = current.ownerDocument.defaultView?.getComputedStyle(current);
1123
+ if (style?.display === "none" || style?.visibility === "hidden" || style?.visibility === "collapse") return false;
1124
+ current = current.parentElement;
1125
+ }
1126
+ return true;
1127
+ }
1128
+ function focus(candidate) {
1129
+ candidate.focus({ preventScroll: true });
1130
+ return candidate.ownerDocument.activeElement === candidate;
1131
+ }
1132
+ function isUsableFocusTarget(candidate, excludedRoot) {
1133
+ if (!candidate.isConnected || candidate === candidate.ownerDocument.body || excludedRoot?.contains(candidate) === true || !candidate.matches(FOCUSABLE_SELECTOR) || candidate.tabIndex < 0 || candidate.getAttribute("aria-disabled") === "true" || candidate instanceof HTMLInputElement && candidate.type === "hidden") return false;
1134
+ let current = candidate;
1135
+ while (current !== null) {
1136
+ if (current.hidden || current.inert || current.getAttribute("aria-hidden") === "true") return false;
1137
+ const style = current.ownerDocument.defaultView?.getComputedStyle(current);
1138
+ if (style?.display === "none" || style?.visibility === "hidden" || style?.visibility === "collapse") return false;
1139
+ current = current.parentElement;
1140
+ }
1141
+ return true;
1142
+ }
1143
+ //#endregion
1144
+ //#region src/client/client-errors.ts
1145
+ /** Maps stable Host/RPC codes to user-facing recovery states without exposing prose. */
1146
+ function presentClientError(code) {
1147
+ switch (code) {
1148
+ case "settings-changed":
1149
+ case "credential-changed": return presentation("errorConflict");
1150
+ case "MODELLIX_CANDIDATE_KEY_INVALID":
1151
+ case "MODELLIX_API_KEY_INVALID":
1152
+ case "MODELLIX_UNAUTHORIZED": return presentation("errorKeyInvalid", true);
1153
+ case "MODELLIX_BILLING_BLOCKED": return presentation("errorBilling");
1154
+ case "MODELLIX_RATE_LIMITED": return presentation("errorRateLimited");
1155
+ case "MODELLIX_OFFLINE":
1156
+ case "transport": return presentation("errorOffline");
1157
+ case "MODELLIX_TIMEOUT": return presentation("errorTimeout");
1158
+ case "MODELLIX_SERVER_ERROR": return presentation("errorServer");
1159
+ case "MODELLIX_POLICY_BLOCKED": return presentation("errorPolicy");
1160
+ case "MODELLIX_API_KEY_REQUIRED": return presentation("keyRequired");
1161
+ case "MODELLIX_DESIGN_INPUT_INVALID": return presentation("parametersInvalid");
1162
+ case "MODELLIX_DESIGN_SCHEMA_INVALID": return presentation("errorDesignSchema");
1163
+ case "MODELLIX_DESIGN_CATALOG_UNAVAILABLE":
1164
+ case "MODELLIX_DESIGN_SCHEMA_UNAVAILABLE": return presentation("errorServer");
1165
+ case "MODELLIX_SUBMIT_UNKNOWN": return presentation("errorSubmitUnknown");
1166
+ case "MODELLIX_ASSET_EXPIRED": return presentation("errorAssetExpired");
1167
+ default: return presentation("errorGeneric");
1168
+ }
1169
+ }
1170
+ function presentation(messageKey, credentialFieldInvalid = false) {
1171
+ return {
1172
+ messageKey,
1173
+ credentialFieldInvalid
1174
+ };
1175
+ }
1176
+ //#endregion
1177
+ //#region src/client/shared.tsx
1178
+ const DEFAULT_SERVICES = Object.freeze({
1179
+ design: true,
1180
+ llm: true,
1181
+ web: true
1182
+ });
1183
+ function useResourceState(source) {
1184
+ return (0, react.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
1185
+ }
1186
+ function ErrorNotice({ code, t }) {
1187
+ if (code === null) return null;
1188
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1189
+ className: "mdlx-error",
1190
+ role: "alert",
1191
+ children: t(presentClientError(code).messageKey)
1192
+ });
1193
+ }
1194
+ function BusyStatus({ busy, text }) {
1195
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1196
+ className: "mdlx-live",
1197
+ role: "status",
1198
+ "aria-live": "polite",
1199
+ children: busy ? text : ""
1200
+ });
1201
+ }
1202
+ function ServiceSwitches({ value, disabled = false, onChange, t }) {
1203
+ const idPrefix = (0, react.useId)();
1204
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1205
+ className: "mdlx-service-list",
1206
+ children: [
1207
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceSwitch, {
1208
+ id: `${idPrefix}-design`,
1209
+ label: t("serviceDesign"),
1210
+ description: t("serviceDesignDescription"),
1211
+ checked: value.design,
1212
+ disabled,
1213
+ onChange: (design) => onChange({
1214
+ ...value,
1215
+ design
1216
+ })
1217
+ }),
1218
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceSwitch, {
1219
+ id: `${idPrefix}-llm`,
1220
+ label: t("serviceLlm"),
1221
+ description: t("serviceLlmDescription"),
1222
+ checked: value.llm,
1223
+ disabled,
1224
+ onChange: (llm) => onChange({
1225
+ ...value,
1226
+ llm
1227
+ })
1228
+ }),
1229
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceSwitch, {
1230
+ id: `${idPrefix}-web`,
1231
+ label: t("serviceWeb"),
1232
+ description: t("serviceWebDescription"),
1233
+ checked: value.web,
1234
+ disabled,
1235
+ onChange: (web) => onChange({
1236
+ ...value,
1237
+ web
1238
+ })
1239
+ })
1240
+ ]
1241
+ });
1242
+ }
1243
+ function ServiceSwitch({ id, label, description, checked, disabled, onChange }) {
1244
+ const descriptionId = `${id}-description`;
1245
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1246
+ className: "mdlx-switch-row",
1247
+ htmlFor: id,
1248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1249
+ className: "mdlx-switch-copy",
1250
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1251
+ id: descriptionId,
1252
+ children: description
1253
+ })]
1254
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1255
+ id,
1256
+ className: "mdlx-switch",
1257
+ type: "checkbox",
1258
+ role: "switch",
1259
+ checked,
1260
+ disabled,
1261
+ "aria-describedby": descriptionId,
1262
+ onChange: (event) => onChange(event.currentTarget.checked)
1263
+ })]
1264
+ });
1265
+ }
1266
+ function CredentialStatus({ configured, source, verification, t }) {
1267
+ const text = !configured ? t("notConfigured") : source === "env" ? t("configuredEnv") : t("configuredLocal");
1268
+ const verificationText = verification === "valid" ? t("verificationValid") : verification === "invalid" ? t("verificationInvalid") : t("verificationPending");
1269
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1270
+ className: "mdlx-status-copy",
1271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: !configured ? "warning" : verification === "invalid" ? "error" : verification === "valid" ? "done" : "ongoing" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1272
+ text,
1273
+ " · ",
1274
+ verificationText
1275
+ ] })]
1276
+ });
1277
+ }
1278
+ function CredentialModal(props) {
1279
+ const { open, mandatory, title, description, services, onServicesChange, busy, errorCode, onSave, onSaved, onCancel, laterLabel = "cancel", t } = props;
1280
+ const [draft, setDraft] = (0, react.useState)("");
1281
+ const [visible, setVisible] = (0, react.useState)(false);
1282
+ const [displayedErrorCode, setDisplayedErrorCode] = (0, react.useState)(errorCode);
1283
+ const contentRef = (0, react.useRef)(null);
1284
+ const draftRef = (0, react.useRef)("");
1285
+ const keyId = (0, react.useId)();
1286
+ const helpId = `${keyId}-help`;
1287
+ const errorId = `${keyId}-error`;
1288
+ const errorPresentation = displayedErrorCode === null ? null : presentClientError(displayedErrorCode);
1289
+ const surfaceOpen = useExternalDialogGate(open);
1290
+ const clear = (0, react.useCallback)(() => {
1291
+ draftRef.current = "";
1292
+ setDraft("");
1293
+ setVisible(false);
1294
+ setDisplayedErrorCode(null);
1295
+ clearSecretInput(contentRef.current);
1296
+ }, []);
1297
+ const close = (0, react.useCallback)(() => {
1298
+ clear();
1299
+ onCancel();
1300
+ }, [clear, onCancel]);
1301
+ useDialogA11y({
1302
+ open: surfaceOpen,
1303
+ container: contentRef,
1304
+ initialFocusSelector: "[data-mdlx-initial-focus]",
1305
+ mandatory,
1306
+ onEscape: close
1307
+ });
1308
+ (0, react.useEffect)(() => () => {
1309
+ draftRef.current = "";
1310
+ clearSecretInput(contentRef.current);
1311
+ }, []);
1312
+ (0, react.useEffect)(() => {
1313
+ setDisplayedErrorCode(errorCode);
1314
+ }, [errorCode]);
1315
+ const submit = async () => {
1316
+ const candidate = draftRef.current.trim();
1317
+ if (candidate.length === 0 || busy) return;
1318
+ if (!await onSave(candidate)) return;
1319
+ clear();
1320
+ onSaved();
1321
+ };
1322
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1323
+ open: surfaceOpen,
1324
+ title,
1325
+ onClose: mandatory ? () => void 0 : close,
1326
+ headless: true,
1327
+ className: "mdlx-modal",
1328
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1329
+ ref: contentRef,
1330
+ className: "mdlx-modal-content",
1331
+ "data-mdlx-dialog-surface": "",
1332
+ tabIndex: -1,
1333
+ noValidate: true,
1334
+ onSubmit: (event) => {
1335
+ event.preventDefault();
1336
+ submit();
1337
+ },
1338
+ children: [
1339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1340
+ className: "mdlx-heading",
1341
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1342
+ className: "mdlx-modal-title",
1343
+ children: title
1344
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1345
+ className: "mdlx-modal-description",
1346
+ children: description
1347
+ })]
1348
+ }),
1349
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1350
+ className: "mdlx-field",
1351
+ children: [
1352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1353
+ className: "mdlx-label",
1354
+ htmlFor: keyId,
1355
+ children: t("keyLabel")
1356
+ }),
1357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1358
+ className: "mdlx-input-row",
1359
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1360
+ id: keyId,
1361
+ name: "modellix-api-key",
1362
+ className: "mdlx-input",
1363
+ type: visible ? "text" : "password",
1364
+ value: draft,
1365
+ autoComplete: "new-password",
1366
+ spellCheck: false,
1367
+ placeholder: t("keyPlaceholder"),
1368
+ "aria-describedby": `${helpId}${errorPresentation?.credentialFieldInvalid === true ? ` ${errorId}` : ""}`,
1369
+ "aria-invalid": errorPresentation?.credentialFieldInvalid || void 0,
1370
+ "data-mdlx-secret": "",
1371
+ "data-mdlx-initial-focus": "",
1372
+ onChange: (event) => {
1373
+ const value = event.currentTarget.value;
1374
+ draftRef.current = value;
1375
+ setDraft(value);
1376
+ setDisplayedErrorCode(null);
1377
+ }
1378
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1379
+ type: "button",
1380
+ variant: "outline",
1381
+ "aria-label": visible ? t("hideKey") : t("showKey"),
1382
+ "aria-pressed": visible,
1383
+ onClick: () => setVisible((current) => !current),
1384
+ children: visible ? t("hideKey") : t("showKey")
1385
+ })]
1386
+ }),
1387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1388
+ id: helpId,
1389
+ className: "mdlx-help",
1390
+ children: t("keyHelp")
1391
+ })
1392
+ ]
1393
+ }),
1394
+ services !== void 0 && onServicesChange !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceSwitches, {
1395
+ value: services,
1396
+ disabled: busy,
1397
+ onChange: onServicesChange,
1398
+ t
1399
+ }),
1400
+ errorPresentation !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1401
+ id: errorId,
1402
+ className: "mdlx-error",
1403
+ role: "alert",
1404
+ children: t(errorPresentation.messageKey)
1405
+ }),
1406
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
1407
+ className: "mdlx-safe-link",
1408
+ href: "https://docs.modellix.ai/get-started",
1409
+ target: "_blank",
1410
+ rel: "noopener noreferrer",
1411
+ referrerPolicy: "no-referrer",
1412
+ children: [
1413
+ t("docs"),
1414
+ " ",
1415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
1416
+ ]
1417
+ }),
1418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1419
+ className: "mdlx-actions",
1420
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1421
+ type: "button",
1422
+ variant: "outline",
1423
+ disabled: busy,
1424
+ onClick: close,
1425
+ children: t(laterLabel)
1426
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1427
+ type: "submit",
1428
+ variant: "primary",
1429
+ disabled: busy || draft.trim().length === 0,
1430
+ "aria-busy": busy,
1431
+ children: busy ? t("saving") : t("saveEnable")
1432
+ })]
1433
+ }),
1434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
1435
+ busy,
1436
+ text: t("saving")
1437
+ })
1438
+ ]
1439
+ })
1440
+ });
1441
+ }
1442
+ function formatClientValue(value) {
1443
+ if (value === void 0) return "—";
1444
+ if (typeof value === "string") return value;
1445
+ try {
1446
+ const text = JSON.stringify(value);
1447
+ if (typeof text !== "string") return "—";
1448
+ return text.length > 512 ? `${text.slice(0, 509)}…` : text;
1449
+ } catch {
1450
+ return "—";
1451
+ }
1452
+ }
1453
+ //#endregion
1454
+ //#region src/client/onboarding-state.ts
1455
+ function shouldPromptOnboarding(snapshot) {
1456
+ if (snapshot.onboarding.recoveryRequestId !== null || snapshot.credential.verification === "invalid") return false;
1457
+ if (!snapshot.credential.writable && snapshot.onboarding.status === "deferred") return false;
1458
+ if (snapshot.onboarding.recoveryPending) return true;
1459
+ return !snapshot.credential.configured && snapshot.onboarding.status !== "deferred";
1460
+ }
1461
+ //#endregion
1462
+ //#region src/client/Onboarding.tsx
1463
+ function ModellixOnboarding({ complete, controller, t }) {
1464
+ const state = useResourceState(controller.store);
1465
+ const [services, setServices] = (0, react.useState)(DEFAULT_SERVICES);
1466
+ const [open, setOpen] = (0, react.useState)(true);
1467
+ const [loadRecoveryVisible, setLoadRecoveryVisible] = (0, react.useState)(false);
1468
+ (0, react.useEffect)(() => {
1469
+ if (controller.store.getSnapshot().status !== "idle") return;
1470
+ const abort = new AbortController();
1471
+ controller.load(abort.signal);
1472
+ return () => abort.abort();
1473
+ }, [controller]);
1474
+ const snapshot = state.data;
1475
+ const promptRequired = snapshot === null ? false : shouldPromptOnboarding(snapshot);
1476
+ (0, react.useEffect)(() => {
1477
+ if (snapshot === null && state.status === "error") setLoadRecoveryVisible(true);
1478
+ }, [snapshot, state.status]);
1479
+ (0, react.useEffect)(() => {
1480
+ if (snapshot === null) return;
1481
+ setServices(snapshot.services);
1482
+ if (!promptRequired) complete();
1483
+ }, [
1484
+ complete,
1485
+ promptRequired,
1486
+ snapshot
1487
+ ]);
1488
+ const save = (0, react.useCallback)(async (apiKey) => {
1489
+ if (snapshot === null) return false;
1490
+ return controller.saveOnboarding(apiKey, services, snapshot.credential.credentialEpoch);
1491
+ }, [
1492
+ controller,
1493
+ services,
1494
+ snapshot
1495
+ ]);
1496
+ const defer = (0, react.useCallback)(() => {
1497
+ if (snapshot === null || state.pending !== null) return;
1498
+ controller.deferOnboarding(services, snapshot.settingsRevision).then((accepted) => {
1499
+ if (!accepted) return;
1500
+ setOpen(false);
1501
+ complete();
1502
+ });
1503
+ }, [
1504
+ complete,
1505
+ controller,
1506
+ services,
1507
+ snapshot,
1508
+ state.pending
1509
+ ]);
1510
+ if (snapshot === null) {
1511
+ if (!loadRecoveryVisible) return null;
1512
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnboardingLoadRecoveryDialog, {
1513
+ busy: state.pending === "load",
1514
+ errorCode: state.errorOperation === "load" ? state.errorCode : null,
1515
+ onRetry: () => {
1516
+ controller.load();
1517
+ },
1518
+ onLater: complete,
1519
+ t
1520
+ });
1521
+ }
1522
+ if (!promptRequired) return null;
1523
+ if (!snapshot.credential.writable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnboardingReadonlyCredentialDialog, {
1524
+ open,
1525
+ busy: state.pending === "defer-onboarding",
1526
+ errorCode: state.errorOperation === "defer-onboarding" ? state.errorCode : null,
1527
+ description: snapshot.credential.source === "env" ? t("readonlyEnvInvalid") : t("credentialReadonly"),
1528
+ onLater: defer,
1529
+ t
1530
+ });
1531
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
1532
+ open,
1533
+ mandatory: true,
1534
+ title: t("onboardingTitle"),
1535
+ description: t("onboardingDescription"),
1536
+ services,
1537
+ onServicesChange: setServices,
1538
+ busy: state.pending === "save-onboarding" || state.pending === "defer-onboarding",
1539
+ errorCode: state.errorOperation === "save-onboarding" || state.errorOperation === "defer-onboarding" ? state.errorCode : null,
1540
+ onSave: save,
1541
+ onSaved: complete,
1542
+ onCancel: defer,
1543
+ laterLabel: "later",
1544
+ t
1545
+ });
1546
+ }
1547
+ function OnboardingLoadRecoveryDialog({ busy, errorCode, onRetry, onLater, t }) {
1548
+ const contentRef = (0, react.useRef)(null);
1549
+ const surfaceOpen = useExternalDialogGate(true);
1550
+ useDialogA11y({
1551
+ open: surfaceOpen,
1552
+ container: contentRef,
1553
+ initialFocusSelector: "[data-mdlx-initial-focus]",
1554
+ mandatory: true,
1555
+ onEscape: onLater
1556
+ });
1557
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1558
+ open: surfaceOpen,
1559
+ title: t("onboardingLoadErrorTitle"),
1560
+ onClose: () => void 0,
1561
+ headless: true,
1562
+ className: "mdlx-modal mdlx-modal-confirm",
1563
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1564
+ ref: contentRef,
1565
+ className: "mdlx-modal-content",
1566
+ "data-mdlx-dialog-surface": "",
1567
+ tabIndex: -1,
1568
+ children: [
1569
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1570
+ className: "mdlx-heading",
1571
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1572
+ className: "mdlx-modal-title",
1573
+ children: t("onboardingLoadErrorTitle")
1574
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1575
+ className: "mdlx-modal-description",
1576
+ children: t("onboardingLoadErrorDescription")
1577
+ })]
1578
+ }),
1579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
1580
+ code: errorCode,
1581
+ t
1582
+ }),
1583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1584
+ className: "mdlx-actions",
1585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1586
+ type: "button",
1587
+ variant: "outline",
1588
+ onClick: onLater,
1589
+ children: t("later")
1590
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1591
+ type: "button",
1592
+ variant: "primary",
1593
+ disabled: busy,
1594
+ "aria-busy": busy,
1595
+ "data-mdlx-initial-focus": "",
1596
+ onClick: onRetry,
1597
+ children: busy ? t("loading") : t("retry")
1598
+ })]
1599
+ }),
1600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
1601
+ busy,
1602
+ text: t("loading")
1603
+ })
1604
+ ]
1605
+ })
1606
+ });
1607
+ }
1608
+ function OnboardingReadonlyCredentialDialog({ open, busy, errorCode, description, onLater, t }) {
1609
+ const contentRef = (0, react.useRef)(null);
1610
+ const surfaceOpen = useExternalDialogGate(open);
1611
+ useDialogA11y({
1612
+ open: surfaceOpen,
1613
+ container: contentRef,
1614
+ initialFocusSelector: "[data-mdlx-initial-focus]",
1615
+ mandatory: true,
1616
+ onEscape: onLater
1617
+ });
1618
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1619
+ open: surfaceOpen,
1620
+ title: t("readonlyKeyTitle"),
1621
+ onClose: () => void 0,
1622
+ headless: true,
1623
+ className: "mdlx-modal mdlx-modal-confirm",
1624
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1625
+ ref: contentRef,
1626
+ className: "mdlx-modal-content",
1627
+ "data-mdlx-dialog-surface": "",
1628
+ tabIndex: -1,
1629
+ children: [
1630
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1631
+ className: "mdlx-heading",
1632
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1633
+ className: "mdlx-modal-title",
1634
+ children: t("readonlyKeyTitle")
1635
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1636
+ className: "mdlx-modal-description",
1637
+ children: description
1638
+ })]
1639
+ }),
1640
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
1641
+ code: errorCode,
1642
+ t
1643
+ }),
1644
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1645
+ className: "mdlx-actions",
1646
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1647
+ type: "button",
1648
+ variant: "primary",
1649
+ disabled: busy,
1650
+ "aria-busy": busy,
1651
+ "data-mdlx-initial-focus": "",
1652
+ onClick: onLater,
1653
+ children: busy ? t("saving") : t("later")
1654
+ })
1655
+ }),
1656
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
1657
+ busy,
1658
+ text: t("saving")
1659
+ })
1660
+ ]
1661
+ })
1662
+ });
1663
+ }
1664
+ //#endregion
1665
+ //#region src/client/credential-dialog.ts
1666
+ const RECOVERY_CREDENTIAL_DIALOG_OWNER = "shell:credential-recovery";
1667
+ /**
1668
+ * Arbitrates every post-onboarding Credential editor that shares one
1669
+ * SettingsController. A current dialog keeps its lease when recovery arrives,
1670
+ * so it can upgrade in place instead of competing with the shell overlay.
1671
+ */
1672
+ var CredentialDialogCoordinator = class {
1673
+ #listeners = /* @__PURE__ */ new Set();
1674
+ #snapshot = Object.freeze({
1675
+ revision: 0,
1676
+ activeOwner: null,
1677
+ recoveryToken: null,
1678
+ dismissedRecoveryToken: null
1679
+ });
1680
+ subscribe = (listener) => {
1681
+ this.#listeners.add(listener);
1682
+ return () => this.#listeners.delete(listener);
1683
+ };
1684
+ getSnapshot = () => this.#snapshot;
1685
+ open(owner) {
1686
+ if (this.#snapshot.activeOwner !== null) return;
1687
+ this.#publish({ activeOwner: owner });
1688
+ }
1689
+ presentRecovery(token) {
1690
+ if (token === this.#snapshot.dismissedRecoveryToken) return;
1691
+ if (token === this.#snapshot.recoveryToken) {
1692
+ if (this.#snapshot.activeOwner === null) this.#publish({ activeOwner: RECOVERY_CREDENTIAL_DIALOG_OWNER });
1693
+ return;
1694
+ }
1695
+ this.#publish({
1696
+ recoveryToken: token,
1697
+ dismissedRecoveryToken: null,
1698
+ activeOwner: this.#snapshot.activeOwner ?? "shell:credential-recovery"
1699
+ });
1700
+ }
1701
+ clearRecovery() {
1702
+ if (this.#snapshot.recoveryToken === null && this.#snapshot.dismissedRecoveryToken === null) return;
1703
+ this.#publish({
1704
+ recoveryToken: null,
1705
+ dismissedRecoveryToken: null,
1706
+ activeOwner: this.#snapshot.activeOwner === "shell:credential-recovery" ? null : this.#snapshot.activeOwner
1707
+ });
1708
+ }
1709
+ /** Releases an ordinary Modal and hands a queued recovery to the shell. */
1710
+ close(owner) {
1711
+ if (this.#snapshot.activeOwner !== owner) return;
1712
+ this.#publish({ activeOwner: this.#snapshot.recoveryToken === null ? null : owner === "shell:credential-recovery" ? null : RECOVERY_CREDENTIAL_DIALOG_OWNER });
1713
+ }
1714
+ /** Closes a Credential editor and dismisses only the current recovery token. */
1715
+ dismissCredential(owner) {
1716
+ if (this.#snapshot.activeOwner !== owner) return;
1717
+ this.#publish({
1718
+ activeOwner: null,
1719
+ recoveryToken: null,
1720
+ dismissedRecoveryToken: this.#snapshot.recoveryToken ?? this.#snapshot.dismissedRecoveryToken
1721
+ });
1722
+ }
1723
+ completeCredential(owner) {
1724
+ this.dismissCredential(owner);
1725
+ }
1726
+ release(owner) {
1727
+ if (this.#snapshot.activeOwner !== owner) return;
1728
+ this.#publish({ activeOwner: this.#snapshot.recoveryToken === null ? null : owner === "shell:credential-recovery" ? null : RECOVERY_CREDENTIAL_DIALOG_OWNER });
1729
+ }
1730
+ #publish(patch) {
1731
+ const next = Object.freeze({
1732
+ ...this.#snapshot,
1733
+ ...patch,
1734
+ revision: this.#snapshot.revision + 1
1735
+ });
1736
+ if (next.activeOwner === this.#snapshot.activeOwner && next.recoveryToken === this.#snapshot.recoveryToken && next.dismissedRecoveryToken === this.#snapshot.dismissedRecoveryToken) return;
1737
+ this.#snapshot = next;
1738
+ for (const listener of this.#listeners) listener();
1739
+ }
1740
+ };
1741
+ const COORDINATORS = /* @__PURE__ */ new WeakMap();
1742
+ function credentialDialogCoordinatorFor(controller) {
1743
+ const current = COORDINATORS.get(controller);
1744
+ if (current !== void 0) return current;
1745
+ const created = new CredentialDialogCoordinator();
1746
+ COORDINATORS.set(controller, created);
1747
+ return created;
1748
+ }
1749
+ function useCredentialDialogSnapshot(coordinator) {
1750
+ return (0, react.useSyncExternalStore)(coordinator.subscribe, coordinator.getSnapshot, coordinator.getSnapshot);
1751
+ }
1752
+ //#endregion
1753
+ //#region src/client/CredentialRecoveryOverlay.tsx
1754
+ const RECOVERY_REFRESH_MS = 5e3;
1755
+ /** Persistent root seat that turns the first current-epoch 401 into one Modal. */
1756
+ function CredentialRecoveryOverlay({ controller, t }) {
1757
+ const state = useResourceState(controller.store);
1758
+ const snapshot = state.data;
1759
+ const dialogCoordinator = credentialDialogCoordinatorFor(controller);
1760
+ const dialog = useCredentialDialogSnapshot(dialogCoordinator);
1761
+ const invalidEpoch = snapshot?.credential.verification === "invalid" ? snapshot.credential.invalidEpoch : null;
1762
+ const recoveryToken = snapshot?.onboarding.recoveryRequestId ?? null ?? (invalidEpoch === null ? null : `invalid:${String(invalidEpoch)}`);
1763
+ const needsCredential = snapshot !== null && (!snapshot.credential.configured || snapshot.credential.verification === "invalid");
1764
+ const recoveryPresented = needsCredential && recoveryToken !== null && recoveryToken === dialog.recoveryToken && recoveryToken !== dialog.dismissedRecoveryToken;
1765
+ (0, react.useEffect)(() => {
1766
+ if (!needsCredential || recoveryToken === null) {
1767
+ dialogCoordinator.clearRecovery();
1768
+ return;
1769
+ }
1770
+ dialogCoordinator.presentRecovery(recoveryToken);
1771
+ }, [
1772
+ dialogCoordinator,
1773
+ needsCredential,
1774
+ recoveryToken
1775
+ ]);
1776
+ (0, react.useEffect)(() => {
1777
+ if (recoveryPresented) return;
1778
+ const refresh = () => {
1779
+ const current = controller.store.getSnapshot();
1780
+ if (current.pending !== null || current.status === "loading") return;
1781
+ controller.load();
1782
+ };
1783
+ refresh();
1784
+ const timer = window.setInterval(refresh, RECOVERY_REFRESH_MS);
1785
+ return () => window.clearInterval(timer);
1786
+ }, [controller, recoveryPresented]);
1787
+ (0, react.useEffect)(() => () => dialogCoordinator.release(RECOVERY_CREDENTIAL_DIALOG_OWNER), [dialogCoordinator]);
1788
+ const dismiss = (0, react.useCallback)(() => {
1789
+ dialogCoordinator.dismissCredential(RECOVERY_CREDENTIAL_DIALOG_OWNER);
1790
+ }, [dialogCoordinator]);
1791
+ if (!recoveryPresented || dialog.activeOwner !== "shell:credential-recovery" || snapshot === null) return null;
1792
+ const credential = snapshot.credential;
1793
+ if (!credential.writable || credential.source === "env") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnboardingReadonlyCredentialDialog, {
1794
+ open: true,
1795
+ busy: false,
1796
+ errorCode: null,
1797
+ description: credential.source === "env" ? t("readonlyEnvInvalid") : t("credentialReadonly"),
1798
+ onLater: dismiss,
1799
+ t
1800
+ });
1801
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
1802
+ open: true,
1803
+ mandatory: true,
1804
+ title: credential.configured ? t("replaceKey") : t("onboardingTitle"),
1805
+ description: credential.configured ? t("errorKeyInvalid") : t("keyRequired"),
1806
+ busy: state.pending === "replace-credential",
1807
+ errorCode: state.errorOperation === "replace-credential" ? state.errorCode : null,
1808
+ onSave: (apiKey) => controller.replaceCredential(apiKey, credential.credentialEpoch, snapshot.services),
1809
+ onSaved: () => dialogCoordinator.completeCredential(RECOVERY_CREDENTIAL_DIALOG_OWNER),
1810
+ onCancel: dismiss,
1811
+ laterLabel: "later",
1812
+ t
1813
+ });
1814
+ }
1815
+ //#endregion
1816
+ //#region src/client/design-presentation.ts
1817
+ const NOTICE_MESSAGE_KEYS = {
1818
+ "schema-unavailable": "designNoticeSchemaUnavailable",
1819
+ "schema-invalid": "designNoticeSchemaInvalid",
1820
+ "catalog-stale": "designNoticeCatalogStale",
1821
+ "catalog-unavailable": "designNoticeCatalogUnavailable",
1822
+ "credential-reloaded": "designNoticeCredentialReloaded"
1823
+ };
1824
+ const MODEL_UNAVAILABLE_MESSAGE_KEYS = { "removed-from-catalog": "modelRemovedFromCatalog" };
1825
+ const FIELD_DISABLED_MESSAGE_KEYS = { "unsupported-schema-field": "unsupportedSchemaField" };
1826
+ const DIAGNOSTIC_MESSAGE_KEYS = {
1827
+ "credential-changed": "diagnosticCredentialChanged",
1828
+ "submit-unknown": "diagnosticSubmitUnknown",
1829
+ "generation-failed": "diagnosticGenerationFailed",
1830
+ "result-unavailable": "diagnosticResultUnavailable",
1831
+ "credential-rejected": "diagnosticCredentialRejected",
1832
+ "task-inaccessible": "diagnosticTaskInaccessible",
1833
+ "rate-limited": "diagnosticRateLimited",
1834
+ "response-invalid": "diagnosticResponseInvalid",
1835
+ "poll-unavailable": "diagnosticPollUnavailable"
1836
+ };
1837
+ const RESOURCE_PREVIEW_MESSAGE_KEYS = {
1838
+ image: "generatedPreview",
1839
+ video: "generatedVideoPreview",
1840
+ audio: "generatedAudioPreview"
1841
+ };
1842
+ function designNoticeMessageKey(code) {
1843
+ return NOTICE_MESSAGE_KEYS[code];
1844
+ }
1845
+ function designModelUnavailableMessageKey(code) {
1846
+ return MODEL_UNAVAILABLE_MESSAGE_KEYS[code];
1847
+ }
1848
+ function designFieldDisabledMessageKey(code) {
1849
+ return FIELD_DISABLED_MESSAGE_KEYS[code];
1850
+ }
1851
+ function designDiagnosticMessageKey(code) {
1852
+ return DIAGNOSTIC_MESSAGE_KEYS[code];
1853
+ }
1854
+ function designResourcePreviewMessageKey(kind) {
1855
+ return RESOURCE_PREVIEW_MESSAGE_KEYS[kind];
1856
+ }
1857
+ function jsonParameterIssueMessageKey(issue) {
1858
+ return issue === "syntax" ? "invalidJson" : "invalidParameter";
1859
+ }
1860
+ function parseJsonParameterText(text, validate) {
1861
+ if (text.trim() === "") return { status: "empty" };
1862
+ let parsed;
1863
+ try {
1864
+ parsed = JSON.parse(text);
1865
+ } catch {
1866
+ return {
1867
+ status: "invalid",
1868
+ issue: "syntax"
1869
+ };
1870
+ }
1871
+ if (!isClientJsonValue(parsed) || !validate(parsed)) return {
1872
+ status: "invalid",
1873
+ issue: "constraint"
1874
+ };
1875
+ return {
1876
+ status: "valid",
1877
+ value: parsed
1878
+ };
1879
+ }
1880
+ function isClientJsonValue(value, depth = 0) {
1881
+ if (depth > 10) return false;
1882
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
1883
+ if (typeof value === "number") return Number.isFinite(value);
1884
+ if (Array.isArray(value)) return value.length <= 4096 && value.every((item) => isClientJsonValue(item, depth + 1));
1885
+ if (typeof value !== "object") return false;
1886
+ return Object.entries(value).every(([, item]) => isClientJsonValue(item, depth + 1));
1887
+ }
1888
+ //#endregion
1889
+ //#region src/client/DesignResultPreview.tsx
1890
+ const NO_REFERRER_ATTRIBUTE = { referrerPolicy: "no-referrer" };
1891
+ function DesignResultPreview({ resource, t }) {
1892
+ const accessibleName = t(designResourcePreviewMessageKey(resource.kind));
1893
+ if (resource.kind === "image") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1894
+ className: "mdlx-media",
1895
+ src: resource.url,
1896
+ alt: accessibleName,
1897
+ loading: "lazy",
1898
+ referrerPolicy: "no-referrer"
1899
+ });
1900
+ if (resource.kind === "video") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
1901
+ ...NO_REFERRER_ATTRIBUTE,
1902
+ className: "mdlx-media",
1903
+ src: resource.url,
1904
+ "aria-label": accessibleName,
1905
+ controls: true,
1906
+ playsInline: true,
1907
+ preload: "metadata"
1908
+ });
1909
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
1910
+ ...NO_REFERRER_ATTRIBUTE,
1911
+ className: "mdlx-media mdlx-media-audio",
1912
+ src: resource.url,
1913
+ "aria-label": accessibleName,
1914
+ controls: true,
1915
+ preload: "metadata"
1916
+ });
1917
+ }
1918
+ //#endregion
1919
+ //#region src/client/DesignView.tsx
1920
+ function ModellixDesignView({ controller, settingsController, t }) {
1921
+ const state = useResourceState(controller.store);
1922
+ const settingsState = useResourceState(settingsController.store);
1923
+ const snapshot = state.data;
1924
+ const settings = settingsState.data;
1925
+ const [parameters, setParameters] = (0, react.useState)({});
1926
+ const [instruction, setInstruction] = (0, react.useState)("");
1927
+ const [modelQuery, setModelQuery] = (0, react.useState)("");
1928
+ const [modelKind, setModelKind] = (0, react.useState)("all");
1929
+ const [invalidFields, setInvalidFields] = (0, react.useState)(() => /* @__PURE__ */ new Set());
1930
+ const [outcomeAnnouncement, setOutcomeAnnouncement] = (0, react.useState)("");
1931
+ const gatePresented = (0, react.useRef)(false);
1932
+ const credentialDialogOwner = `design:${(0, react.useId)()}`;
1933
+ const credentialDialogs = credentialDialogCoordinatorFor(settingsController);
1934
+ const credentialDialog = useCredentialDialogSnapshot(credentialDialogs);
1935
+ const credentialOpen = credentialDialog.activeOwner === credentialDialogOwner;
1936
+ const credentialRecovery = credentialOpen && credentialDialog.recoveryToken !== null;
1937
+ const previousOutcome = (0, react.useRef)(null);
1938
+ const visibleModels = (0, react.useMemo)(() => {
1939
+ const query = modelQuery.trim().toLocaleLowerCase();
1940
+ return (snapshot?.models ?? []).filter((model) => (modelKind === "all" || model.kind === modelKind) && (query === "" || `${model.label}\n${model.id}`.toLocaleLowerCase().includes(query)));
1941
+ }, [
1942
+ modelKind,
1943
+ modelQuery,
1944
+ snapshot?.models
1945
+ ]);
1946
+ (0, react.useEffect)(() => {
1947
+ const abort = new AbortController();
1948
+ controller.load(abort.signal);
1949
+ return () => abort.abort();
1950
+ }, [controller]);
1951
+ (0, react.useEffect)(() => () => credentialDialogs.release(credentialDialogOwner), [credentialDialogOwner, credentialDialogs]);
1952
+ (0, react.useEffect)(() => {
1953
+ if (snapshot?.credentialReady !== false || settingsController.store.getSnapshot().status !== "idle") return;
1954
+ const abort = new AbortController();
1955
+ settingsController.load(abort.signal);
1956
+ return () => abort.abort();
1957
+ }, [settingsController, snapshot?.credentialReady]);
1958
+ (0, react.useEffect)(() => {
1959
+ const draft = snapshot?.draft;
1960
+ if (draft === null || draft === void 0) {
1961
+ setParameters({});
1962
+ setInvalidFields(/* @__PURE__ */ new Set());
1963
+ return;
1964
+ }
1965
+ setParameters(draft.parameters);
1966
+ setInvalidFields(/* @__PURE__ */ new Set());
1967
+ }, [snapshot?.draft?.draftRevision, snapshot?.draft?.irContractHash]);
1968
+ (0, react.useEffect)(() => {
1969
+ if (snapshot?.credentialReady !== false || settings === null || settings.onboarding.status === "active" || gatePresented.current) return;
1970
+ gatePresented.current = true;
1971
+ if (settings.credential.writable) credentialDialogs.open(credentialDialogOwner);
1972
+ }, [
1973
+ credentialDialogOwner,
1974
+ credentialDialogs,
1975
+ settings,
1976
+ snapshot?.credentialReady
1977
+ ]);
1978
+ (0, react.useEffect)(() => {
1979
+ if (snapshot === null || !snapshot.jobs.some((job) => job.status === "running")) return;
1980
+ const abort = new AbortController();
1981
+ const timer = window.setTimeout(() => {
1982
+ controller.load(abort.signal);
1983
+ }, 5e3);
1984
+ return () => {
1985
+ window.clearTimeout(timer);
1986
+ abort.abort();
1987
+ };
1988
+ }, [controller, snapshot]);
1989
+ (0, react.useEffect)(() => {
1990
+ if (state.pending !== null) setOutcomeAnnouncement("");
1991
+ }, [state.pending]);
1992
+ (0, react.useEffect)(() => {
1993
+ if (snapshot === null) return;
1994
+ const transition = designOutcomeTransition(previousOutcome.current, snapshot);
1995
+ previousOutcome.current = {
1996
+ proposal: snapshot.proposal,
1997
+ jobs: snapshot.jobs
1998
+ };
1999
+ const announcement = designOutcomeText(transition, t);
2000
+ if (announcement !== null) setOutcomeAnnouncement(announcement);
2001
+ }, [
2002
+ snapshot?.jobs,
2003
+ snapshot?.proposal,
2004
+ t
2005
+ ]);
2006
+ const updateParameter = (0, react.useCallback)((path, value) => {
2007
+ setParameters((current) => {
2008
+ const next = { ...current };
2009
+ if (value === void 0) delete next[path];
2010
+ else next[path] = value;
2011
+ return next;
2012
+ });
2013
+ }, []);
2014
+ const setFieldValidity = (0, react.useCallback)((path, valid) => {
2015
+ setInvalidFields((current) => {
2016
+ const next = new Set(current);
2017
+ if (valid) next.delete(path);
2018
+ else next.add(path);
2019
+ return next;
2020
+ });
2021
+ }, []);
2022
+ if (snapshot === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2023
+ className: "mdlx-design",
2024
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2025
+ className: "mdlx-empty",
2026
+ role: "status",
2027
+ "aria-live": "polite",
2028
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: state.status === "error" ? t("errorGeneric") : t("loading") }), state.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2029
+ type: "button",
2030
+ variant: "outline",
2031
+ onClick: () => {
2032
+ controller.load();
2033
+ },
2034
+ children: t("retry")
2035
+ })]
2036
+ })
2037
+ });
2038
+ const draft = snapshot.draft;
2039
+ const promptPath = draft?.primaryInputPath ?? null;
2040
+ const promptValue = promptPath !== null && typeof parameters[promptPath] === "string" ? parameters[promptPath] : "";
2041
+ const missingRequired = draft?.fields.some((field) => field.required && isMissingDesignParameter(parameters, field.path)) ?? true;
2042
+ const submitting = state.pending === "submit";
2043
+ const interactionBusy = state.pending !== null;
2044
+ const selectedModel = selectedDesignModel(snapshot);
2045
+ const canGenerate = canGenerateDesign({
2046
+ snapshot,
2047
+ draft,
2048
+ invalidFieldCount: invalidFields.size,
2049
+ missingRequired,
2050
+ interactionBusy
2051
+ });
2052
+ const supplementalFields = draft?.fields.filter((field) => field.path !== draft.primaryInputPath) ?? [];
2053
+ const requiredFields = supplementalFields.filter((field) => field.required);
2054
+ const optionalFields = supplementalFields.filter((field) => !field.required);
2055
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2056
+ className: "mdlx-design",
2057
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2058
+ className: "mdlx-design-shell",
2059
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2060
+ className: "mdlx-design-pane",
2061
+ "aria-labelledby": "mdlx-design-title",
2062
+ children: [
2063
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2064
+ className: "mdlx-heading",
2065
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2066
+ id: "mdlx-design-title",
2067
+ children: t("designTitle")
2068
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2069
+ className: "mdlx-muted",
2070
+ children: t("designDescription")
2071
+ })]
2072
+ }),
2073
+ !snapshot.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2074
+ className: "mdlx-info",
2075
+ children: t("designDisabled")
2076
+ }),
2077
+ !snapshot.credentialReady && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2078
+ className: "mdlx-info",
2079
+ children: [
2080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("keyRequired") }),
2081
+ settings?.credential.writable === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2082
+ type: "button",
2083
+ variant: "primary",
2084
+ onClick: () => credentialDialogs.open(credentialDialogOwner),
2085
+ children: t("configureToContinue")
2086
+ }),
2087
+ settings?.credential.source === "env" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2088
+ className: "mdlx-muted",
2089
+ children: t("envReadonly")
2090
+ })
2091
+ ]
2092
+ }),
2093
+ snapshot.notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2094
+ className: "mdlx-info",
2095
+ role: "status",
2096
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [t("notice"), ": "] }), t(designNoticeMessageKey(snapshot.notice))]
2097
+ }),
2098
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2099
+ className: "mdlx-field",
2100
+ children: [
2101
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2102
+ className: "mdlx-label",
2103
+ htmlFor: "mdlx-design-model",
2104
+ children: t("modelLabel")
2105
+ }),
2106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2107
+ className: "mdlx-model-tools",
2108
+ children: [
2109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2110
+ className: "mdlx-input",
2111
+ value: modelQuery,
2112
+ maxLength: 512,
2113
+ placeholder: t("modelSearchPlaceholder"),
2114
+ "aria-label": t("modelSearchLabel"),
2115
+ disabled: interactionBusy || !snapshot.enabled,
2116
+ onChange: (event) => setModelQuery(event.currentTarget.value)
2117
+ }),
2118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2119
+ className: "mdlx-select",
2120
+ value: modelKind,
2121
+ "aria-label": t("modelCategoryLabel"),
2122
+ disabled: interactionBusy || !snapshot.enabled,
2123
+ onChange: (event) => {
2124
+ setModelKind(event.currentTarget.value);
2125
+ },
2126
+ children: [
2127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2128
+ value: "all",
2129
+ children: t("modelCategoryAll")
2130
+ }),
2131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2132
+ value: "image",
2133
+ children: t("modelCategoryImage")
2134
+ }),
2135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2136
+ value: "video",
2137
+ children: t("modelCategoryVideo")
2138
+ }),
2139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2140
+ value: "audio",
2141
+ children: t("modelCategoryAudio")
2142
+ })
2143
+ ]
2144
+ }),
2145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2146
+ type: "button",
2147
+ variant: "outline",
2148
+ disabled: interactionBusy || !snapshot.enabled || !snapshot.credentialReady,
2149
+ "aria-busy": state.pending === "refresh-design",
2150
+ onClick: () => {
2151
+ controller.refreshCatalog();
2152
+ },
2153
+ children: state.pending === "refresh-design" ? t("modelRefreshing") : t("modelRefresh")
2154
+ })
2155
+ ]
2156
+ }),
2157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2158
+ id: "mdlx-design-model",
2159
+ className: "mdlx-select",
2160
+ value: snapshot.selectedModelId ?? "",
2161
+ "aria-describedby": selectedModel?.available === false ? "mdlx-design-model-status" : void 0,
2162
+ disabled: interactionBusy || !snapshot.enabled || snapshot.models.length === 0,
2163
+ onChange: (event) => {
2164
+ const modelId = event.currentTarget.value;
2165
+ if (modelId !== "") controller.selectModel(modelId);
2166
+ },
2167
+ children: [
2168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2169
+ value: "",
2170
+ disabled: true,
2171
+ children: t("chooseModel")
2172
+ }),
2173
+ snapshot.selectedModelId !== null && !visibleModels.some((model) => model.id === snapshot.selectedModelId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2174
+ value: snapshot.selectedModelId,
2175
+ disabled: selectedModel?.available === false,
2176
+ children: snapshot.models.find((model) => model.id === snapshot.selectedModelId)?.label ?? snapshot.selectedModelId
2177
+ }),
2178
+ visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2179
+ value: model.id,
2180
+ disabled: !model.available,
2181
+ children: [model.label, model.featured ? " ★" : ""]
2182
+ }, model.id))
2183
+ ]
2184
+ }),
2185
+ selectedModel?.available === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2186
+ id: "mdlx-design-model-status",
2187
+ className: "mdlx-error",
2188
+ role: "status",
2189
+ children: selectedModel.unavailableReason === null ? t("modelUnavailable") : t(designModelUnavailableMessageKey(selectedModel.unavailableReason))
2190
+ }),
2191
+ snapshot.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2192
+ className: "mdlx-help",
2193
+ children: t("noModels")
2194
+ }) : visibleModels.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2195
+ className: "mdlx-help",
2196
+ children: t("noMatchingModels")
2197
+ })
2198
+ ]
2199
+ }),
2200
+ draft !== null && promptPath !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2201
+ className: "mdlx-field",
2202
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2203
+ className: "mdlx-label",
2204
+ htmlFor: "mdlx-design-prompt",
2205
+ children: [
2206
+ t("promptLabel"),
2207
+ " ",
2208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2209
+ className: "mdlx-required",
2210
+ children: t("required")
2211
+ })
2212
+ ]
2213
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2214
+ id: "mdlx-design-prompt",
2215
+ className: "mdlx-textarea",
2216
+ value: promptValue,
2217
+ maxLength: fieldAtPath(draft.fields, promptPath)?.maxLength ?? void 0,
2218
+ placeholder: t("promptPlaceholder"),
2219
+ disabled: interactionBusy,
2220
+ onChange: (event) => updateParameter(promptPath, event.currentTarget.value)
2221
+ })]
2222
+ }),
2223
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2224
+ className: "mdlx-design-scroll",
2225
+ "aria-labelledby": "mdlx-parameters-title",
2226
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2227
+ className: "mdlx-heading",
2228
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2229
+ id: "mdlx-parameters-title",
2230
+ children: t("parametersTitle")
2231
+ })
2232
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2233
+ className: "mdlx-parameter-list",
2234
+ children: [requiredFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2235
+ field,
2236
+ value: parameters[field.path],
2237
+ invalid: invalidFields.has(field.path),
2238
+ disabled: interactionBusy,
2239
+ onChange: (value) => updateParameter(field.path, value),
2240
+ onValidityChange: (valid) => setFieldValidity(field.path, valid),
2241
+ t
2242
+ }, field.path)), optionalFields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2243
+ className: "mdlx-advanced",
2244
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advancedParameters", { count: optionalFields.length }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2245
+ className: "mdlx-parameter-list",
2246
+ children: optionalFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignParameterField, {
2247
+ field,
2248
+ value: parameters[field.path],
2249
+ invalid: invalidFields.has(field.path),
2250
+ disabled: interactionBusy,
2251
+ onChange: (value) => updateParameter(field.path, value),
2252
+ onValidityChange: (valid) => setFieldValidity(field.path, valid),
2253
+ t
2254
+ }, field.path))
2255
+ })]
2256
+ })]
2257
+ })]
2258
+ }),
2259
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2260
+ className: "mdlx-card",
2261
+ "aria-labelledby": "mdlx-assistant-title",
2262
+ children: [
2263
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2264
+ className: "mdlx-heading",
2265
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2266
+ id: "mdlx-assistant-title",
2267
+ children: t("assistantTitle")
2268
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2269
+ id: "mdlx-assistant-paid-notice",
2270
+ className: "mdlx-help",
2271
+ children: t("assistantPaidNotice")
2272
+ })]
2273
+ }),
2274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2275
+ id: "mdlx-assistant-instruction",
2276
+ className: "mdlx-textarea mdlx-textarea-small",
2277
+ value: instruction,
2278
+ maxLength: 8e3,
2279
+ placeholder: t("assistantPlaceholder"),
2280
+ "aria-labelledby": "mdlx-assistant-title",
2281
+ "aria-describedby": "mdlx-assistant-paid-notice",
2282
+ disabled: interactionBusy,
2283
+ onChange: (event) => setInstruction(event.currentTarget.value)
2284
+ }),
2285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2286
+ className: "mdlx-actions",
2287
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2288
+ type: "button",
2289
+ variant: "outline",
2290
+ disabled: interactionBusy || instruction.trim().length === 0,
2291
+ "aria-busy": state.pending === "propose",
2292
+ onClick: () => {
2293
+ const request = instruction.trim();
2294
+ if (request === "") return;
2295
+ controller.propose(request, parameters).then((accepted) => {
2296
+ if (accepted) setInstruction("");
2297
+ });
2298
+ },
2299
+ children: state.pending === "propose" ? t("proposing") : t("propose")
2300
+ })
2301
+ })
2302
+ ]
2303
+ }),
2304
+ snapshot.proposal !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProposalCard, {
2305
+ snapshot,
2306
+ parameters,
2307
+ controller,
2308
+ t
2309
+ }),
2310
+ draft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2311
+ className: "mdlx-generate-block",
2312
+ children: [
2313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2314
+ className: "mdlx-help",
2315
+ children: t("paidNotice")
2316
+ }),
2317
+ missingRequired && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2318
+ className: "mdlx-error",
2319
+ children: t("requiredMissing")
2320
+ }),
2321
+ invalidFields.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2322
+ className: "mdlx-error",
2323
+ children: t("parametersInvalid")
2324
+ }),
2325
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2326
+ className: "mdlx-actions",
2327
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2328
+ type: "button",
2329
+ variant: snapshot.credentialReady && snapshot.proposal === null ? "primary" : "outline",
2330
+ disabled: !canGenerate,
2331
+ "aria-busy": submitting,
2332
+ onClick: () => {
2333
+ controller.submit(parameters);
2334
+ },
2335
+ children: submitting ? t("generating") : t("generate")
2336
+ })
2337
+ })
2338
+ ]
2339
+ }),
2340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
2341
+ code: state.errorCode,
2342
+ t
2343
+ }),
2344
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
2345
+ busy: interactionBusy,
2346
+ text: operationText(state.pending, t)
2347
+ }),
2348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2349
+ className: "mdlx-live",
2350
+ role: "status",
2351
+ "aria-live": "polite",
2352
+ children: outcomeAnnouncement
2353
+ })
2354
+ ]
2355
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResults, {
2356
+ snapshot,
2357
+ dateLocale: t("dateLocale"),
2358
+ dialogCoordinator: credentialDialogs,
2359
+ dialog: credentialDialog,
2360
+ t
2361
+ })]
2362
+ }), settings !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
2363
+ open: credentialOpen,
2364
+ mandatory: true,
2365
+ title: settings.credential.configured ? t("replaceKey") : t("onboardingTitle"),
2366
+ description: credentialRecovery ? settings.credential.configured ? t("errorKeyInvalid") : t("keyRequired") : t("onboardingDescription"),
2367
+ busy: settingsState.pending === "replace-credential",
2368
+ errorCode: settingsState.errorOperation === "replace-credential" ? settingsState.errorCode : null,
2369
+ onSave: (apiKey) => settingsController.replaceCredential(apiKey, settings.credential.credentialEpoch, settings.services),
2370
+ onSaved: () => {
2371
+ credentialDialogs.completeCredential(credentialDialogOwner);
2372
+ controller.load();
2373
+ },
2374
+ onCancel: () => credentialDialogs.dismissCredential(credentialDialogOwner),
2375
+ laterLabel: "later",
2376
+ t
2377
+ })]
2378
+ });
2379
+ }
2380
+ function DesignParameterField({ field, value, invalid, disabled, onChange, onValidityChange, t }) {
2381
+ const id = (0, react.useId)();
2382
+ const helpId = `${id}-help`;
2383
+ const errorId = `${id}-error`;
2384
+ const locked = disabled || field.disabledReason !== null;
2385
+ const jsonControl = field.widget === "json" || field.kind === "array" || field.kind === "object";
2386
+ const describedBy = `${helpId}${invalid && !jsonControl ? ` ${errorId}` : ""}`;
2387
+ const commit = (next) => {
2388
+ onValidityChange(isDesignFieldValueValid(field, next));
2389
+ onChange(next);
2390
+ };
2391
+ const label = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2392
+ className: "mdlx-label",
2393
+ htmlFor: id,
2394
+ children: [field.label, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2395
+ className: field.required ? "mdlx-required" : "mdlx-muted",
2396
+ children: field.required ? t("required") : t("optional")
2397
+ })]
2398
+ });
2399
+ let control;
2400
+ if (field.widget === "select" || field.kind === "enum") {
2401
+ const selected = field.options.findIndex((option) => option.value === value);
2402
+ control = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2403
+ id,
2404
+ className: "mdlx-select",
2405
+ value: selected < 0 ? "" : String(selected),
2406
+ disabled: locked,
2407
+ "aria-invalid": invalid || void 0,
2408
+ "aria-describedby": describedBy,
2409
+ onChange: (event) => {
2410
+ const selectedIndex = event.currentTarget.value;
2411
+ commit(selectedIndex === "" ? void 0 : field.options[Number(selectedIndex)]?.value);
2412
+ },
2413
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2414
+ value: "",
2415
+ children: "—"
2416
+ }), field.options.map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2417
+ value: String(index),
2418
+ children: option.label
2419
+ }, `${field.path}-${index}`))]
2420
+ });
2421
+ } else if (field.widget === "switch" || field.kind === "boolean") control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2422
+ className: "mdlx-switch-target",
2423
+ htmlFor: id,
2424
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2425
+ id,
2426
+ className: "mdlx-switch",
2427
+ type: "checkbox",
2428
+ role: "switch",
2429
+ checked: value === true,
2430
+ disabled: locked,
2431
+ "aria-invalid": invalid || void 0,
2432
+ "aria-describedby": describedBy,
2433
+ onChange: (event) => commit(event.currentTarget.checked)
2434
+ })
2435
+ });
2436
+ else if (field.kind === "number" || field.kind === "integer") control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2437
+ id,
2438
+ className: "mdlx-native-input",
2439
+ type: "number",
2440
+ value: typeof value === "number" ? String(value) : "",
2441
+ min: field.minimum ?? void 0,
2442
+ max: field.maximum ?? void 0,
2443
+ step: field.step ?? (field.kind === "integer" ? 1 : "any"),
2444
+ disabled: locked,
2445
+ "aria-invalid": invalid || void 0,
2446
+ "aria-describedby": describedBy,
2447
+ onChange: (event) => {
2448
+ const text = event.currentTarget.value;
2449
+ if (text === "") commit(void 0);
2450
+ else {
2451
+ const parsed = Number(text);
2452
+ if (Number.isFinite(parsed)) commit(parsed);
2453
+ }
2454
+ }
2455
+ });
2456
+ else if (field.widget === "json" || field.kind === "array" || field.kind === "object") control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(JsonParameter, {
2457
+ id,
2458
+ value,
2459
+ disabled: locked,
2460
+ describedBy: helpId,
2461
+ validate: (candidate) => isDesignFieldValueValid(field, candidate),
2462
+ onChange,
2463
+ onValidityChange,
2464
+ t
2465
+ });
2466
+ else if (field.widget === "textarea") control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2467
+ id,
2468
+ className: "mdlx-textarea mdlx-textarea-small",
2469
+ value: typeof value === "string" ? value : "",
2470
+ maxLength: field.maxLength ?? void 0,
2471
+ disabled: locked,
2472
+ "aria-invalid": invalid || void 0,
2473
+ "aria-describedby": describedBy,
2474
+ onChange: (event) => commit(event.currentTarget.value)
2475
+ });
2476
+ else control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2477
+ id,
2478
+ className: "mdlx-input",
2479
+ value: typeof value === "string" ? value : "",
2480
+ maxLength: field.maxLength ?? void 0,
2481
+ disabled: locked,
2482
+ "aria-invalid": invalid || void 0,
2483
+ "aria-describedby": describedBy,
2484
+ onChange: (event) => commit(event.currentTarget.value)
2485
+ });
2486
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2487
+ className: "mdlx-parameter",
2488
+ children: [
2489
+ label,
2490
+ control,
2491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2492
+ id: helpId,
2493
+ className: "mdlx-help",
2494
+ children: field.disabledReason === null ? field.description ?? (locked ? t("fieldUnavailable") : "") : t(designFieldDisabledMessageKey(field.disabledReason))
2495
+ }),
2496
+ invalid && !jsonControl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2497
+ id: errorId,
2498
+ className: "mdlx-error",
2499
+ children: t("invalidParameter")
2500
+ })
2501
+ ]
2502
+ });
2503
+ }
2504
+ function JsonParameter({ id, value, disabled, describedBy, validate, onChange, onValidityChange, t }) {
2505
+ const errorId = `${id}-json-error`;
2506
+ const [text, setText] = (0, react.useState)(() => value === void 0 ? "" : JSON.stringify(value, null, 2));
2507
+ const [issue, setIssue] = (0, react.useState)(null);
2508
+ (0, react.useEffect)(() => {
2509
+ setText(value === void 0 ? "" : JSON.stringify(value, null, 2));
2510
+ setIssue(null);
2511
+ }, [value]);
2512
+ const invalid = issue !== null;
2513
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2514
+ id,
2515
+ className: "mdlx-textarea mdlx-textarea-small mdlx-code",
2516
+ value: text,
2517
+ disabled,
2518
+ "aria-invalid": invalid || void 0,
2519
+ "aria-describedby": `${describedBy}${invalid ? ` ${errorId}` : ""}`,
2520
+ onChange: (event) => {
2521
+ const next = event.currentTarget.value;
2522
+ setText(next);
2523
+ const result = parseJsonParameterText(next, validate);
2524
+ if (result.status === "empty") {
2525
+ setIssue(null);
2526
+ onValidityChange(true);
2527
+ onChange(void 0);
2528
+ } else if (result.status === "valid") {
2529
+ setIssue(null);
2530
+ onValidityChange(true);
2531
+ onChange(result.value);
2532
+ } else {
2533
+ setIssue(result.issue);
2534
+ onValidityChange(false);
2535
+ }
2536
+ }
2537
+ }), issue !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2538
+ id: errorId,
2539
+ className: "mdlx-error",
2540
+ children: t(jsonParameterIssueMessageKey(issue))
2541
+ })] });
2542
+ }
2543
+ function ProposalCard({ snapshot, parameters, controller, t }) {
2544
+ const proposal = snapshot.proposal;
2545
+ if (proposal === null) return null;
2546
+ const busy = controller.store.getSnapshot().pending !== null;
2547
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2548
+ className: "mdlx-proposal",
2549
+ "aria-labelledby": "mdlx-proposal-title",
2550
+ children: [
2551
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2552
+ className: "mdlx-heading",
2553
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2554
+ id: "mdlx-proposal-title",
2555
+ children: t("proposalTitle")
2556
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2557
+ className: "mdlx-muted",
2558
+ children: t("proposalSummary", { count: proposal.changes.length })
2559
+ })]
2560
+ }),
2561
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2562
+ className: "mdlx-change-list",
2563
+ children: proposal.changes.map((change) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
2564
+ className: "mdlx-change",
2565
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: change.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2566
+ className: "mdlx-code",
2567
+ children: [
2568
+ formatClientValue(change.before),
2569
+ " → ",
2570
+ formatClientValue(change.after)
2571
+ ]
2572
+ })]
2573
+ }, change.path))
2574
+ }),
2575
+ proposal.conflicts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2576
+ className: "mdlx-error",
2577
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("conflicts") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("proposalConflictsSummary", { count: proposal.conflicts.length }) })]
2578
+ }),
2579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2580
+ className: "mdlx-actions",
2581
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2582
+ type: "button",
2583
+ variant: "outline",
2584
+ disabled: busy,
2585
+ onClick: () => {
2586
+ controller.rejectProposal(proposal.proposalId);
2587
+ },
2588
+ children: t("rejectProposal")
2589
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2590
+ type: "button",
2591
+ variant: "primary",
2592
+ disabled: busy || proposal.conflicts.length > 0,
2593
+ onClick: () => {
2594
+ controller.applyProposal(proposal.proposalId, parameters);
2595
+ },
2596
+ children: t("applyProposal")
2597
+ })]
2598
+ })
2599
+ ]
2600
+ });
2601
+ }
2602
+ function DesignResults({ snapshot, dateLocale, dialogCoordinator, dialog, t }) {
2603
+ const groups = (0, react.useMemo)(() => {
2604
+ const running = [];
2605
+ const succeeded = [];
2606
+ const diagnostics = [];
2607
+ for (const job of snapshot.jobs) if (job.status === "running") running.push(job);
2608
+ else if (job.status === "succeeded") succeeded.push(job);
2609
+ else diagnostics.push(job);
2610
+ return {
2611
+ running,
2612
+ succeeded,
2613
+ diagnostics
2614
+ };
2615
+ }, [snapshot.jobs]);
2616
+ const empty = snapshot.jobs.length === 0;
2617
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2618
+ className: "mdlx-design-pane",
2619
+ "aria-labelledby": "mdlx-results-title",
2620
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
2621
+ className: "mdlx-heading",
2622
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2623
+ id: "mdlx-results-title",
2624
+ children: t("resultsTitle")
2625
+ })
2626
+ }), empty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2627
+ className: "mdlx-empty",
2628
+ children: t("noResults")
2629
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2630
+ className: "mdlx-design-scroll",
2631
+ children: [
2632
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2633
+ title: t("runningTitle"),
2634
+ jobs: groups.running,
2635
+ dateLocale,
2636
+ dialogCoordinator,
2637
+ dialog,
2638
+ t
2639
+ }),
2640
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2641
+ title: t("succeededTitle"),
2642
+ jobs: groups.succeeded,
2643
+ dateLocale,
2644
+ dialogCoordinator,
2645
+ dialog,
2646
+ t
2647
+ }),
2648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultSection, {
2649
+ title: t("diagnosticsTitle"),
2650
+ jobs: groups.diagnostics,
2651
+ dateLocale,
2652
+ dialogCoordinator,
2653
+ dialog,
2654
+ t
2655
+ })
2656
+ ]
2657
+ })]
2658
+ });
2659
+ }
2660
+ function ResultSection({ title, jobs, dateLocale, dialogCoordinator, dialog, t }) {
2661
+ if (jobs.length === 0) return null;
2662
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2663
+ className: "mdlx-result-section",
2664
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2665
+ className: "mdlx-result-list",
2666
+ children: jobs.map((job) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultCard, {
2667
+ job,
2668
+ dateLocale,
2669
+ dialogCoordinator,
2670
+ dialog,
2671
+ t
2672
+ }, job.jobId))
2673
+ })]
2674
+ });
2675
+ }
2676
+ function ResultCard({ job, dateLocale, dialogCoordinator, dialog, t }) {
2677
+ const status = jobStatus(job.status, t);
2678
+ const created = formatTime(job.createdAt, dateLocale);
2679
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
2680
+ className: "mdlx-result-card",
2681
+ children: [
2682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2683
+ className: "mdlx-result-head",
2684
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2685
+ className: "mdlx-status-copy",
2686
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: job.status === "succeeded" ? "done" : job.status === "running" ? "ongoing" : job.status === "failed" ? "error" : "warning" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: status })]
2687
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2688
+ className: "mdlx-muted",
2689
+ children: t("jobCreated", { time: created })
2690
+ })]
2691
+ }),
2692
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2693
+ className: "mdlx-muted",
2694
+ children: t("jobModel", { model: job.modelId })
2695
+ }),
2696
+ job.resources.length > 0 && job.status !== "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2697
+ className: "mdlx-resource-list",
2698
+ children: job.resources.map((resource) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultResource, {
2699
+ resource,
2700
+ dateLocale,
2701
+ dialogCoordinator,
2702
+ dialog,
2703
+ t
2704
+ }, resource.id))
2705
+ }),
2706
+ job.diagnostic !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2707
+ className: "mdlx-error",
2708
+ children: [
2709
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2710
+ className: "mdlx-code",
2711
+ children: job.diagnostic.code
2712
+ }),
2713
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(designDiagnosticMessageKey(job.diagnostic.code)) }),
2714
+ job.diagnostic.retryable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("diagnosticRetryable") })
2715
+ ]
2716
+ })
2717
+ ]
2718
+ });
2719
+ }
2720
+ function ResultResource({ resource, dateLocale, dialogCoordinator, dialog, t }) {
2721
+ const imageDialogOwner = `design-image:${(0, react.useId)()}`;
2722
+ const imageSurfaceOpen = useExternalDialogGate(dialog.activeOwner === imageDialogOwner);
2723
+ const imageDialogRef = (0, react.useRef)(null);
2724
+ const closeImage = (0, react.useCallback)(() => dialogCoordinator.close(imageDialogOwner), [dialogCoordinator, imageDialogOwner]);
2725
+ (0, react.useEffect)(() => () => dialogCoordinator.release(imageDialogOwner), [dialogCoordinator, imageDialogOwner]);
2726
+ useDialogA11y({
2727
+ open: imageSurfaceOpen,
2728
+ container: imageDialogRef,
2729
+ initialFocusSelector: "[data-mdlx-initial-focus]",
2730
+ mandatory: false,
2731
+ onEscape: closeImage
2732
+ });
2733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
2734
+ className: "mdlx-resource",
2735
+ children: [
2736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesignResultPreview, {
2737
+ resource,
2738
+ t
2739
+ }),
2740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2741
+ className: "mdlx-actions mdlx-actions-start",
2742
+ children: [resource.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2743
+ type: "button",
2744
+ className: "mdlx-safe-link mdlx-link-button",
2745
+ onClick: () => dialogCoordinator.open(imageDialogOwner),
2746
+ children: t("openImage")
2747
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2748
+ className: "mdlx-safe-link",
2749
+ href: resource.downloadUrl,
2750
+ target: "_blank",
2751
+ rel: "noopener noreferrer",
2752
+ referrerPolicy: "no-referrer",
2753
+ download: true,
2754
+ children: [
2755
+ t("download"),
2756
+ " ",
2757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2758
+ ]
2759
+ })]
2760
+ }),
2761
+ resource.expiresAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2762
+ className: "mdlx-help",
2763
+ children: t("expiresAt", { time: formatTime(resource.expiresAt, dateLocale) })
2764
+ })
2765
+ ]
2766
+ }), resource.kind === "image" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2767
+ open: imageSurfaceOpen,
2768
+ title: t("imageViewerTitle"),
2769
+ closeLabel: t("close"),
2770
+ onClose: closeImage,
2771
+ headless: true,
2772
+ className: "mdlx-modal mdlx-image-modal",
2773
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2774
+ ref: imageDialogRef,
2775
+ className: "mdlx-modal-content",
2776
+ "data-mdlx-dialog-surface": "",
2777
+ tabIndex: -1,
2778
+ children: [
2779
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2780
+ className: "mdlx-heading",
2781
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2782
+ className: "mdlx-modal-title",
2783
+ children: t("imageViewerTitle")
2784
+ })
2785
+ }),
2786
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
2787
+ className: "mdlx-image-full",
2788
+ src: resource.url,
2789
+ alt: t("generatedPreview"),
2790
+ referrerPolicy: "no-referrer"
2791
+ }),
2792
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2793
+ className: "mdlx-actions",
2794
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2795
+ type: "button",
2796
+ variant: "outline",
2797
+ "data-mdlx-initial-focus": "",
2798
+ onClick: closeImage,
2799
+ children: t("close")
2800
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
2801
+ className: "mdlx-safe-link",
2802
+ href: resource.downloadUrl,
2803
+ target: "_blank",
2804
+ rel: "noopener noreferrer",
2805
+ referrerPolicy: "no-referrer",
2806
+ download: true,
2807
+ children: [
2808
+ t("download"),
2809
+ " ",
2810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline14, { size: 14 })
2811
+ ]
2812
+ })]
2813
+ })
2814
+ ]
2815
+ })
2816
+ })] });
2817
+ }
2818
+ function fieldAtPath(fields, path) {
2819
+ return fields.find((field) => field.path === path);
2820
+ }
2821
+ function designOutcomeText(transition, t) {
2822
+ const messages = [];
2823
+ if (transition.proposalReady) messages.push(t("proposalReadyAnnouncement"));
2824
+ if (transition.succeeded > 0) messages.push(t("jobsSucceededAnnouncement", { count: transition.succeeded }));
2825
+ if (transition.failed > 0) messages.push(t("jobsFailedAnnouncement", { count: transition.failed }));
2826
+ if (transition.expired > 0) messages.push(t("jobsExpiredAnnouncement", { count: transition.expired }));
2827
+ if (transition.running > 0) messages.push(t("jobsRunningAnnouncement", { count: transition.running }));
2828
+ return messages.length === 0 ? null : messages.join(" ");
2829
+ }
2830
+ function operationText(operation, t) {
2831
+ if (operation === "propose") return t("proposing");
2832
+ if (operation === "submit") return t("generating");
2833
+ return operation === null ? "" : t("loading");
2834
+ }
2835
+ function jobStatus(status, t) {
2836
+ switch (status) {
2837
+ case "running": return t("running");
2838
+ case "succeeded": return t("succeeded");
2839
+ case "failed": return t("failed");
2840
+ case "canceled": return t("canceled");
2841
+ case "submit-unknown": return t("unknown");
2842
+ case "expired": return t("expired");
2843
+ }
2844
+ }
2845
+ function formatTime(value, locale) {
2846
+ return new Intl.DateTimeFormat(locale, {
2847
+ dateStyle: "medium",
2848
+ timeStyle: "short"
2849
+ }).format(new Date(value));
2850
+ }
2851
+ //#endregion
2852
+ //#region src/client/locales.ts
2853
+ const MODELLIX_LOCALE_NAMESPACE = "modellix";
2854
+ const zh = {
2855
+ dateLocale: "zh-CN",
2856
+ nav: "Modellix",
2857
+ designTab: "Design",
2858
+ onboardingTitle: "连接 Modellix",
2859
+ onboardingDescription: "输入一个 Modellix API Key,即可启用 Design、LLM 模型和 Web Tools。",
2860
+ keyLabel: "Modellix API Key",
2861
+ keyHelp: "Key 只发送到本机 Harness Host 保存;保存后不会在浏览器中回显。",
2862
+ keyPlaceholder: "输入 API Key",
2863
+ showKey: "显示 API Key",
2864
+ hideKey: "隐藏 API Key",
2865
+ saveEnable: "保存并启用",
2866
+ saving: "正在保存…",
2867
+ later: "稍后处理",
2868
+ docs: "查看 Modellix API Key 文档",
2869
+ serviceDesign: "Design",
2870
+ serviceDesignDescription: "通过对话与参数表单生成图片、视频和音频。",
2871
+ serviceLlm: "LLM 模型",
2872
+ serviceLlmDescription: "在 Harness 中快速切换 Modellix 支持的模型。",
2873
+ serviceWeb: "Web Tools",
2874
+ serviceWebDescription: "为原生 web_search 与 web_fetch 提供 Modellix Provider。",
2875
+ settingsTitle: "Modellix",
2876
+ settingsDescription: "一个 API Key,统一使用 Design、LLM 与 Web Tools。",
2877
+ credentialTitle: "API Key",
2878
+ configuredLocal: "已配置,本机 Credential 管理",
2879
+ configuredEnv: "已由环境变量配置,只读",
2880
+ notConfigured: "尚未配置",
2881
+ verificationValid: "已验证",
2882
+ verificationInvalid: "Key 无效,需要更换",
2883
+ verificationPending: "等待验证",
2884
+ replaceKey: "更换 API Key",
2885
+ configureKey: "配置 API Key",
2886
+ removeKey: "移除 API Key",
2887
+ removeTitle: "移除 Modellix API Key?",
2888
+ removeDescription: "移除后,已启用的 Modellix 功能会在下次调用时提示重新配置。",
2889
+ removeConfirm: "确认移除",
2890
+ removing: "正在移除…",
2891
+ cancel: "取消",
2892
+ serviceTitle: "功能开关",
2893
+ saveChanges: "保存更改",
2894
+ saved: "更改已保存",
2895
+ refreshLlm: "刷新 LLM 模型",
2896
+ refreshingLlm: "正在刷新…",
2897
+ llmTitle: "LLM 模型目录",
2898
+ llmModelCount: "可用模型:{count}",
2899
+ llmUnknown: "尚未检查",
2900
+ llmReady: "目录可用",
2901
+ llmMissing: "需要先配置 API Key",
2902
+ llmDisabled: "LLM 功能已关闭",
2903
+ llmError: "模型目录暂时不可用",
2904
+ llmPolicyBlocked: "当前策略不允许刷新模型目录",
2905
+ llmUpdated: "LLM 模型上次刷新:{time}",
2906
+ envReadonly: "环境变量提供的 Key 不能在此更换或移除。",
2907
+ loading: "正在读取 Modellix 配置…",
2908
+ retry: "重试",
2909
+ onboardingLoadErrorTitle: "暂时无法读取 Modellix 配置",
2910
+ onboardingLoadErrorDescription: "可以检查本机 Harness 连接后重试,或稍后再配置。",
2911
+ readonlyKeyTitle: "Modellix API Key 需要在环境中更新",
2912
+ readonlyEnvInvalid: "环境变量提供的 Key 无效且不能在此覆盖。请更新启动环境中的 MODELLIX_API_KEY 后重启 Harness。",
2913
+ credentialReadonly: "当前 Credential 存储为只读,无法在此保存 Key。请调整本机 Harness Credential 配置。",
2914
+ errorGeneric: "暂时无法完成操作,请重试。",
2915
+ errorConflict: "配置已在其他位置更新,请刷新后重试。",
2916
+ errorKeyInvalid: "这个 API Key 无效,请检查后重新输入。",
2917
+ errorBilling: "账户当前无法计费,请检查 Modellix 余额或账单状态。",
2918
+ errorRateLimited: "请求过于频繁,请稍后手动重试。",
2919
+ errorOffline: "当前无法连接 Modellix,请检查网络后重试。",
2920
+ errorTimeout: "Modellix 响应超时,请稍后手动重试。",
2921
+ errorServer: "Modellix 服务暂时不可用,请稍后手动重试。",
2922
+ errorPolicy: "当前账户或环境策略不允许此操作。",
2923
+ errorSubmitUnknown: "提交结果未知。请先查看结果列表,避免重复计费。",
2924
+ errorAssetExpired: "结果资源已过期,无法继续访问。",
2925
+ errorDesignSchema: "模型参数定义已变化或无法解析,请刷新模型后重试。",
2926
+ designTitle: "Modellix Design",
2927
+ designDescription: "用自然语言或精准参数快速生成图片、视频与音频。",
2928
+ modelLabel: "模型",
2929
+ modelSearchLabel: "搜索 Design 模型",
2930
+ modelSearchPlaceholder: "按模型名或 provider 搜索",
2931
+ modelCategoryLabel: "按输出类型筛选模型",
2932
+ modelCategoryAll: "全部类型",
2933
+ modelCategoryImage: "图片",
2934
+ modelCategoryVideo: "视频",
2935
+ modelCategoryAudio: "音频",
2936
+ modelRefresh: "刷新模型",
2937
+ modelRefreshing: "正在刷新…",
2938
+ chooseModel: "选择模型",
2939
+ promptLabel: "你想生成什么?",
2940
+ promptPlaceholder: "描述画面、视频动作或要合成的声音…",
2941
+ parametersTitle: "参数",
2942
+ advancedParameters: "高级参数({count})",
2943
+ required: "必填",
2944
+ optional: "可选",
2945
+ requiredMissing: "请填写所有必填参数后再生成。",
2946
+ assistantTitle: "用对话调整参数",
2947
+ assistantPaidNotice: "发送后会使用同一个 Modellix Key 调用固定 LLM 生成参数提议,可能产生费用;不会自动生成媒体。",
2948
+ assistantPlaceholder: "例如:改成 16:9,生成 8 秒视频,风格更电影化",
2949
+ propose: "生成参数提议",
2950
+ proposing: "正在分析…",
2951
+ proposalTitle: "待确认的参数变更",
2952
+ proposalSummary: "已建议 {count} 项参数变更。",
2953
+ proposalConflictsSummary: "提议包含 {count} 项冲突,请调整指令后重新生成参数提议。",
2954
+ applyProposal: "应用变更",
2955
+ rejectProposal: "拒绝",
2956
+ conflicts: "需要先解决冲突",
2957
+ generate: "确认并生成",
2958
+ generating: "正在提交…",
2959
+ paidNotice: "生成是计费操作;每次点击只提交一次,不会自动重试。",
2960
+ designDisabled: "Design 已关闭,可在 Modellix 设置中重新开启。",
2961
+ keyRequired: "请先在 Modellix 设置中配置 API Key。",
2962
+ noModels: "当前没有可用模型,请稍后刷新。",
2963
+ noMatchingModels: "没有匹配当前搜索和类型的模型。",
2964
+ modelUnavailable: "当前模型不可用,请选择其他模型。",
2965
+ modelRemovedFromCatalog: "当前模型已不在最新目录中,请选择其他模型。",
2966
+ unsupportedSchemaField: "当前参数使用了尚不支持的 Schema 结构,无法编辑。",
2967
+ designNoticeSchemaUnavailable: "暂时无法读取推荐模型的参数定义,请刷新模型后重试。",
2968
+ designNoticeSchemaInvalid: "推荐模型的参数定义暂不受支持,请选择其他模型。",
2969
+ designNoticeCatalogStale: "实时模型目录刷新失败,当前显示最近一次读取的结果。",
2970
+ designNoticeCatalogUnavailable: "Modellix Design 模型目录暂时不可用,请稍后刷新。",
2971
+ designNoticeCredentialReloaded: "API Key 已更改,Design 模型和参数已重新加载。",
2972
+ resultsTitle: "结果",
2973
+ runningTitle: "进行中",
2974
+ succeededTitle: "已完成",
2975
+ diagnosticsTitle: "诊断",
2976
+ noResults: "还没有生成记录。确认参数并生成后,结果会显示在这里。",
2977
+ running: "生成中",
2978
+ succeeded: "已完成",
2979
+ failed: "失败",
2980
+ canceled: "已取消",
2981
+ unknown: "提交结果未知",
2982
+ expired: "结果已过期",
2983
+ openImage: "打开原图",
2984
+ imageViewerTitle: "生成图片",
2985
+ close: "关闭",
2986
+ download: "下载结果",
2987
+ expiresAt: "有效期至 {time}",
2988
+ invalidJson: "请输入有效的 JSON。",
2989
+ invalidParameter: "此参数不符合当前模型的约束。",
2990
+ parametersInvalid: "请修正不符合模型约束的参数后再生成。",
2991
+ fieldUnavailable: "此参数当前不可编辑",
2992
+ generatedPreview: "生成结果预览",
2993
+ generatedVideoPreview: "生成视频结果预览",
2994
+ generatedAudioPreview: "生成音频结果预览",
2995
+ jobModel: "模型:{model}",
2996
+ jobCreated: "创建于 {time}",
2997
+ diagnosticRetryable: "插件会继续尝试安全的只读状态查询,但绝不会自动重复计费生成提交。",
2998
+ diagnosticCredentialChanged: "此任务属于更早的 API Key,无法继续刷新。",
2999
+ diagnosticSubmitUnknown: "生成提交结果未知,请先检查结果列表,避免重复计费。",
3000
+ diagnosticGenerationFailed: "生成任务未完成。",
3001
+ diagnosticResultUnavailable: "生成已完成,但没有可用的结果资源。",
3002
+ diagnosticCredentialRejected: "刷新任务时 Modellix 拒绝了当前 API Key。",
3003
+ diagnosticTaskInaccessible: "此生成任务已无法访问。",
3004
+ diagnosticRateLimited: "任务刷新受到限流,稍后会继续。",
3005
+ diagnosticResponseInvalid: "暂时无法解析任务响应。",
3006
+ diagnosticPollUnavailable: "任务刷新暂时不可用,稍后会继续。",
3007
+ configureToContinue: "配置 Key 后继续",
3008
+ notice: "提示",
3009
+ proposalReadyAnnouncement: "参数提议已就绪,请检查后决定是否应用。",
3010
+ jobsRunningAnnouncement: "已提交 {count} 个生成任务,结果会自动更新。",
3011
+ jobsSucceededAnnouncement: "{count} 个生成任务已完成。",
3012
+ jobsFailedAnnouncement: "{count} 个生成任务未成功,请查看诊断。",
3013
+ jobsExpiredAnnouncement: "{count} 个生成结果已过期。"
3014
+ };
3015
+ const en = {
3016
+ dateLocale: "en-US",
3017
+ nav: "Modellix",
3018
+ designTab: "Design",
3019
+ onboardingTitle: "Connect Modellix",
3020
+ onboardingDescription: "Enter one Modellix API Key to enable Design, LLM models, and Web Tools.",
3021
+ keyLabel: "Modellix API Key",
3022
+ keyHelp: "The Key is sent only to the local Harness Host for storage and is never shown again.",
3023
+ keyPlaceholder: "Enter API Key",
3024
+ showKey: "Show API Key",
3025
+ hideKey: "Hide API Key",
3026
+ saveEnable: "Save and enable",
3027
+ saving: "Saving…",
3028
+ later: "Configure later",
3029
+ docs: "Open the Modellix API Key documentation",
3030
+ serviceDesign: "Design",
3031
+ serviceDesignDescription: "Generate images, video, and audio through chat and parameter forms.",
3032
+ serviceLlm: "LLM models",
3033
+ serviceLlmDescription: "Quickly switch between Modellix models in Harness.",
3034
+ serviceWeb: "Web Tools",
3035
+ serviceWebDescription: "Provide Modellix for native web_search and web_fetch.",
3036
+ settingsTitle: "Modellix",
3037
+ settingsDescription: "One API Key for Design, LLM, and Web Tools.",
3038
+ credentialTitle: "API Key",
3039
+ configuredLocal: "Configured in the local Credential store",
3040
+ configuredEnv: "Configured by a read-only environment variable",
3041
+ notConfigured: "Not configured",
3042
+ verificationValid: "Verified",
3043
+ verificationInvalid: "Invalid Key; replace it to continue",
3044
+ verificationPending: "Waiting for verification",
3045
+ replaceKey: "Replace API Key",
3046
+ configureKey: "Configure API Key",
3047
+ removeKey: "Remove API Key",
3048
+ removeTitle: "Remove the Modellix API Key?",
3049
+ removeDescription: "Enabled Modellix features will request a new Key on their next use.",
3050
+ removeConfirm: "Remove",
3051
+ removing: "Removing…",
3052
+ cancel: "Cancel",
3053
+ serviceTitle: "Features",
3054
+ saveChanges: "Save changes",
3055
+ saved: "Changes saved",
3056
+ refreshLlm: "Refresh LLM models",
3057
+ refreshingLlm: "Refreshing…",
3058
+ llmTitle: "LLM model catalog",
3059
+ llmModelCount: "Available models: {count}",
3060
+ llmUnknown: "Not checked yet",
3061
+ llmReady: "Catalog available",
3062
+ llmMissing: "Configure an API Key first",
3063
+ llmDisabled: "LLM is disabled",
3064
+ llmError: "The model catalog is temporarily unavailable",
3065
+ llmPolicyBlocked: "The current policy does not allow catalog refresh",
3066
+ llmUpdated: "LLM models last refreshed: {time}",
3067
+ envReadonly: "An environment-provided Key cannot be replaced or removed here.",
3068
+ loading: "Loading Modellix settings…",
3069
+ retry: "Retry",
3070
+ onboardingLoadErrorTitle: "Modellix settings are temporarily unavailable",
3071
+ onboardingLoadErrorDescription: "Check the local Harness connection and retry, or configure Modellix later.",
3072
+ readonlyKeyTitle: "Update the Modellix API Key in the environment",
3073
+ readonlyEnvInvalid: "The environment-provided Key is invalid and cannot be overridden here. Update MODELLIX_API_KEY in the launch environment and restart Harness.",
3074
+ credentialReadonly: "The current Credential store is read-only. Update the local Harness Credential configuration before saving a Key.",
3075
+ errorGeneric: "The operation could not be completed. Try again.",
3076
+ errorConflict: "Settings changed elsewhere. Refresh and try again.",
3077
+ errorKeyInvalid: "This API Key is invalid. Check it and enter it again.",
3078
+ errorBilling: "Billing is unavailable. Check the Modellix balance or billing status.",
3079
+ errorRateLimited: "Too many requests. Wait before retrying manually.",
3080
+ errorOffline: "Modellix cannot be reached. Check the network and try again.",
3081
+ errorTimeout: "Modellix timed out. Retry manually later.",
3082
+ errorServer: "Modellix is temporarily unavailable. Retry manually later.",
3083
+ errorPolicy: "The current account or environment policy does not allow this operation.",
3084
+ errorSubmitUnknown: "The submission outcome is unknown. Check Results before submitting another billed request.",
3085
+ errorAssetExpired: "This result resource has expired and is no longer available.",
3086
+ errorDesignSchema: "The model parameter definition changed or could not be parsed. Refresh the model and try again.",
3087
+ designTitle: "Modellix Design",
3088
+ designDescription: "Use natural language or exact parameters to generate images, video, and audio.",
3089
+ modelLabel: "Model",
3090
+ modelSearchLabel: "Search Design models",
3091
+ modelSearchPlaceholder: "Search by model or provider",
3092
+ modelCategoryLabel: "Filter models by output type",
3093
+ modelCategoryAll: "All types",
3094
+ modelCategoryImage: "Image",
3095
+ modelCategoryVideo: "Video",
3096
+ modelCategoryAudio: "Audio",
3097
+ modelRefresh: "Refresh models",
3098
+ modelRefreshing: "Refreshing…",
3099
+ chooseModel: "Choose a model",
3100
+ promptLabel: "What do you want to create?",
3101
+ promptPlaceholder: "Describe the image, video motion, or sound…",
3102
+ parametersTitle: "Parameters",
3103
+ advancedParameters: "Advanced parameters ({count})",
3104
+ required: "Required",
3105
+ optional: "Optional",
3106
+ requiredMissing: "Complete every required parameter before generating.",
3107
+ assistantTitle: "Adjust parameters by chat",
3108
+ assistantPaidNotice: "Sending calls a fixed LLM with the same Modellix Key to propose parameter changes and may incur a charge. It never starts media generation.",
3109
+ assistantPlaceholder: "For example: make it 16:9, eight seconds, and more cinematic",
3110
+ propose: "Propose parameter changes",
3111
+ proposing: "Analyzing…",
3112
+ proposalTitle: "Parameter changes awaiting confirmation",
3113
+ proposalSummary: "Proposed parameter changes: {count}.",
3114
+ proposalConflictsSummary: "The proposal has {count} conflict(s). Revise the instruction and request a new proposal.",
3115
+ applyProposal: "Apply changes",
3116
+ rejectProposal: "Reject",
3117
+ conflicts: "Resolve conflicts before applying",
3118
+ generate: "Confirm and generate",
3119
+ generating: "Submitting…",
3120
+ paidNotice: "Generation is billed. Each click submits once and is never retried automatically.",
3121
+ designDisabled: "Design is disabled. Enable it in Modellix settings.",
3122
+ keyRequired: "Configure a Modellix API Key in settings first.",
3123
+ noModels: "No model is currently available. Refresh and try again.",
3124
+ noMatchingModels: "No model matches the current search and type filter.",
3125
+ modelUnavailable: "The selected model is unavailable. Choose another model.",
3126
+ modelRemovedFromCatalog: "The selected model is no longer in the current catalog. Choose another model.",
3127
+ unsupportedSchemaField: "This parameter uses an unsupported Schema structure and cannot be edited.",
3128
+ designNoticeSchemaUnavailable: "The suggested model parameters are temporarily unavailable. Refresh the model and try again.",
3129
+ designNoticeSchemaInvalid: "The suggested model parameter definition is not supported. Choose another model.",
3130
+ designNoticeCatalogStale: "The live model catalog could not be refreshed. The most recent result is shown.",
3131
+ designNoticeCatalogUnavailable: "The Modellix Design model catalog is temporarily unavailable. Refresh it later.",
3132
+ designNoticeCredentialReloaded: "The API Key changed, so the Design model and parameters were reloaded.",
3133
+ resultsTitle: "Results",
3134
+ runningTitle: "Running",
3135
+ succeededTitle: "Succeeded",
3136
+ diagnosticsTitle: "Diagnostics",
3137
+ noResults: "No generations yet. Confirm parameters and generate to see results here.",
3138
+ running: "Running",
3139
+ succeeded: "Succeeded",
3140
+ failed: "Failed",
3141
+ canceled: "Canceled",
3142
+ unknown: "Submission outcome unknown",
3143
+ expired: "Result expired",
3144
+ openImage: "Open full image",
3145
+ imageViewerTitle: "Generated image",
3146
+ close: "Close",
3147
+ download: "Download result",
3148
+ expiresAt: "Available until {time}",
3149
+ invalidJson: "Enter valid JSON.",
3150
+ invalidParameter: "This value does not satisfy the current model constraints.",
3151
+ parametersInvalid: "Fix parameters that do not satisfy the model constraints before generating.",
3152
+ fieldUnavailable: "This parameter cannot be edited right now",
3153
+ generatedPreview: "Generated result preview",
3154
+ generatedVideoPreview: "Generated video result preview",
3155
+ generatedAudioPreview: "Generated audio result preview",
3156
+ jobModel: "Model: {model}",
3157
+ jobCreated: "Created {time}",
3158
+ diagnosticRetryable: "The plugin will continue safe read-only status checks, but it never repeats a billed generation submission automatically.",
3159
+ diagnosticCredentialChanged: "This task belongs to an earlier API Key and cannot be refreshed.",
3160
+ diagnosticSubmitUnknown: "The generation submission outcome is unknown. Check Results before submitting another billed request.",
3161
+ diagnosticGenerationFailed: "The generation did not complete.",
3162
+ diagnosticResultUnavailable: "The generation completed without a usable result resource.",
3163
+ diagnosticCredentialRejected: "Modellix rejected the current API Key while refreshing this task.",
3164
+ diagnosticTaskInaccessible: "This generation task is no longer accessible.",
3165
+ diagnosticRateLimited: "Task refresh is rate limited and will resume later.",
3166
+ diagnosticResponseInvalid: "The task response could not be understood.",
3167
+ diagnosticPollUnavailable: "Task refresh is temporarily unavailable and will resume later.",
3168
+ configureToContinue: "Configure a Key to continue",
3169
+ notice: "Notice",
3170
+ proposalReadyAnnouncement: "A parameter proposal is ready. Review it before applying.",
3171
+ jobsRunningAnnouncement: "{count} generation job(s) submitted. Results will update automatically.",
3172
+ jobsSucceededAnnouncement: "{count} generation job(s) completed.",
3173
+ jobsFailedAnnouncement: "{count} generation job(s) did not succeed. Review diagnostics.",
3174
+ jobsExpiredAnnouncement: "{count} generation result(s) expired."
3175
+ };
3176
+ //#endregion
3177
+ //#region src/client/SettingsSection.tsx
3178
+ function ModellixSettingsSection({ controller, t }) {
3179
+ const state = useResourceState(controller.store);
3180
+ const snapshot = state.data;
3181
+ const [services, setServices] = (0, react.useState)(null);
3182
+ const [announcement, setAnnouncement] = (0, react.useState)("");
3183
+ const credentialDialogOwner = `settings:${(0, react.useId)()}`;
3184
+ const removeDialogOwner = `settings-remove:${(0, react.useId)()}`;
3185
+ const credentialDialogs = credentialDialogCoordinatorFor(controller);
3186
+ const credentialDialog = useCredentialDialogSnapshot(credentialDialogs);
3187
+ const credentialOpen = credentialDialog.activeOwner === credentialDialogOwner;
3188
+ const removeOpen = credentialDialog.activeOwner === removeDialogOwner;
3189
+ const credentialRecovery = credentialOpen && credentialDialog.recoveryToken !== null;
3190
+ (0, react.useEffect)(() => {
3191
+ if (controller.store.getSnapshot().status !== "idle") return;
3192
+ const abort = new AbortController();
3193
+ controller.load(abort.signal);
3194
+ return () => abort.abort();
3195
+ }, [controller]);
3196
+ (0, react.useEffect)(() => () => {
3197
+ credentialDialogs.release(credentialDialogOwner);
3198
+ credentialDialogs.release(removeDialogOwner);
3199
+ }, [
3200
+ credentialDialogOwner,
3201
+ credentialDialogs,
3202
+ removeDialogOwner
3203
+ ]);
3204
+ (0, react.useEffect)(() => {
3205
+ if (snapshot !== null) setServices(snapshot.services);
3206
+ }, [snapshot?.settingsRevision]);
3207
+ const busy = state.pending !== null;
3208
+ const saveServices = (0, react.useCallback)(() => {
3209
+ if (snapshot === null || services === null || busy) return;
3210
+ setAnnouncement("");
3211
+ controller.updateToggles(services, snapshot.settingsRevision).then((accepted) => {
3212
+ if (accepted) setAnnouncement(t("saved"));
3213
+ });
3214
+ }, [
3215
+ busy,
3216
+ controller,
3217
+ services,
3218
+ snapshot,
3219
+ t
3220
+ ]);
3221
+ if (snapshot === null || services === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3222
+ className: "mdlx-settings",
3223
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3224
+ className: "mdlx-live",
3225
+ role: "status",
3226
+ "aria-live": "polite",
3227
+ children: state.status === "error" ? t("errorGeneric") : t("loading")
3228
+ }), state.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3229
+ type: "button",
3230
+ variant: "outline",
3231
+ onClick: () => {
3232
+ controller.load();
3233
+ },
3234
+ children: t("retry")
3235
+ })]
3236
+ });
3237
+ const servicesChanged = !sameServices(snapshot.services, services);
3238
+ const credential = snapshot.credential;
3239
+ const canWriteCredential = credential.writable && credential.source !== "env";
3240
+ const llmRefreshDisabled = busy || !snapshot.services.llm || !credential.configured;
3241
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3242
+ className: "mdlx-settings",
3243
+ children: [
3244
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3245
+ className: "mdlx-heading",
3246
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("settingsTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3247
+ className: "mdlx-muted",
3248
+ children: t("settingsDescription")
3249
+ })]
3250
+ }),
3251
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3252
+ className: "mdlx-card",
3253
+ "aria-labelledby": "mdlx-credential-title",
3254
+ children: [
3255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3256
+ className: "mdlx-card-head",
3257
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3258
+ className: "mdlx-heading",
3259
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3260
+ id: "mdlx-credential-title",
3261
+ children: t("credentialTitle")
3262
+ })
3263
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialStatus, {
3264
+ configured: credential.configured,
3265
+ source: credential.source,
3266
+ verification: credential.verification,
3267
+ t
3268
+ })]
3269
+ }),
3270
+ credential.source === "env" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3271
+ className: "mdlx-muted",
3272
+ children: t("envReadonly")
3273
+ }),
3274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3275
+ className: "mdlx-actions mdlx-actions-start",
3276
+ children: [canWriteCredential && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3277
+ type: "button",
3278
+ variant: !credential.configured || credential.verification === "invalid" ? "primary" : "outline",
3279
+ disabled: busy,
3280
+ onClick: () => credentialDialogs.open(credentialDialogOwner),
3281
+ children: credential.configured ? t("replaceKey") : t("configureKey")
3282
+ }), credential.configured && canWriteCredential && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3283
+ type: "button",
3284
+ variant: "ghost",
3285
+ disabled: busy,
3286
+ onClick: () => credentialDialogs.open(removeDialogOwner),
3287
+ children: t("removeKey")
3288
+ })]
3289
+ })
3290
+ ]
3291
+ }),
3292
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3293
+ className: "mdlx-card",
3294
+ "aria-labelledby": "mdlx-services-title",
3295
+ children: [
3296
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3297
+ className: "mdlx-heading",
3298
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3299
+ id: "mdlx-services-title",
3300
+ children: t("serviceTitle")
3301
+ })
3302
+ }),
3303
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceSwitches, {
3304
+ value: services,
3305
+ disabled: busy,
3306
+ onChange: setServices,
3307
+ t
3308
+ }),
3309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3310
+ className: "mdlx-actions",
3311
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3312
+ type: "button",
3313
+ variant: !credential.configured || credential.verification === "invalid" ? "outline" : "primary",
3314
+ disabled: busy || !servicesChanged,
3315
+ "aria-busy": state.pending === "save-toggles",
3316
+ onClick: saveServices,
3317
+ children: state.pending === "save-toggles" ? t("saving") : t("saveChanges")
3318
+ })
3319
+ })
3320
+ ]
3321
+ }),
3322
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LlmCatalogCard, {
3323
+ snapshot,
3324
+ dateLocale: t("dateLocale"),
3325
+ busy: state.pending === "refresh-llm",
3326
+ disabled: llmRefreshDisabled,
3327
+ onRefresh: () => {
3328
+ setAnnouncement("");
3329
+ controller.refreshLlmCatalog().then((accepted) => {
3330
+ if (accepted) setAnnouncement(t("saved"));
3331
+ });
3332
+ },
3333
+ t
3334
+ }),
3335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
3336
+ code: state.errorCode,
3337
+ t
3338
+ }),
3339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3340
+ className: "mdlx-live",
3341
+ role: "status",
3342
+ "aria-live": "polite",
3343
+ children: announcement
3344
+ }),
3345
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CredentialModal, {
3346
+ open: credentialOpen,
3347
+ mandatory: credentialRecovery || credential.verification === "invalid",
3348
+ title: credential.configured ? t("replaceKey") : t("configureKey"),
3349
+ description: credentialRecovery ? credential.configured ? t("errorKeyInvalid") : t("keyRequired") : t("onboardingDescription"),
3350
+ busy: state.pending === "replace-credential",
3351
+ errorCode: state.errorOperation === "replace-credential" ? state.errorCode : null,
3352
+ onSave: (apiKey) => controller.replaceCredential(apiKey, credential.credentialEpoch, snapshot.services),
3353
+ onSaved: () => {
3354
+ credentialDialogs.completeCredential(credentialDialogOwner);
3355
+ setAnnouncement(t("saved"));
3356
+ },
3357
+ onCancel: () => credentialDialogs.dismissCredential(credentialDialogOwner),
3358
+ laterLabel: credentialRecovery ? "later" : "cancel",
3359
+ t
3360
+ }),
3361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RemoveCredentialDialog, {
3362
+ open: removeOpen,
3363
+ busy: state.pending === "remove-credential",
3364
+ errorCode: state.errorOperation === "remove-credential" ? state.errorCode : null,
3365
+ onClose: () => credentialDialogs.close(removeDialogOwner),
3366
+ onConfirm: () => {
3367
+ if (state.pending !== null) return;
3368
+ controller.removeCredential(credential.credentialEpoch).then((accepted) => {
3369
+ if (!accepted) return;
3370
+ credentialDialogs.close(removeDialogOwner);
3371
+ setAnnouncement(t("saved"));
3372
+ });
3373
+ },
3374
+ t
3375
+ })
3376
+ ]
3377
+ });
3378
+ }
3379
+ function LlmCatalogCard({ snapshot, dateLocale, busy, disabled, onRefresh, t }) {
3380
+ const text = llmHealthText(snapshot.llm.health, t);
3381
+ const dot = snapshot.llm.health === "ready" ? "done" : snapshot.llm.health === "error" ? "error" : snapshot.llm.health === "unknown" ? "ongoing" : "warning";
3382
+ const refreshedAt = (0, react.useMemo)(() => snapshot.llm.refreshedAt === null ? null : new Intl.DateTimeFormat(dateLocale, {
3383
+ dateStyle: "medium",
3384
+ timeStyle: "short"
3385
+ }).format(new Date(snapshot.llm.refreshedAt)), [dateLocale, snapshot.llm.refreshedAt]);
3386
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3387
+ className: "mdlx-card",
3388
+ "aria-labelledby": "mdlx-llm-title",
3389
+ children: [
3390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3391
+ className: "mdlx-card-head",
3392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3393
+ className: "mdlx-heading",
3394
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3395
+ id: "mdlx-llm-title",
3396
+ children: t("llmTitle")
3397
+ })
3398
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3399
+ className: "mdlx-status-copy",
3400
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: dot }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text })]
3401
+ })]
3402
+ }),
3403
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3404
+ className: "mdlx-muted",
3405
+ children: t("llmModelCount", { count: snapshot.llm.modelCount })
3406
+ }),
3407
+ refreshedAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3408
+ className: "mdlx-muted",
3409
+ children: t("llmUpdated", { time: refreshedAt })
3410
+ }),
3411
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3412
+ className: "mdlx-actions",
3413
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3414
+ type: "button",
3415
+ variant: "outline",
3416
+ disabled,
3417
+ "aria-busy": busy,
3418
+ onClick: onRefresh,
3419
+ children: busy ? t("refreshingLlm") : t("refreshLlm")
3420
+ })
3421
+ }),
3422
+ disabled && !busy && snapshot.llm.health !== "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3423
+ className: "mdlx-help",
3424
+ children: text
3425
+ })
3426
+ ]
3427
+ });
3428
+ }
3429
+ function RemoveCredentialDialog({ open, busy, errorCode, onClose, onConfirm, t }) {
3430
+ const contentRef = (0, react.useRef)(null);
3431
+ const surfaceOpen = useExternalDialogGate(open);
3432
+ useDialogA11y({
3433
+ open: surfaceOpen,
3434
+ container: contentRef,
3435
+ initialFocusSelector: "[data-mdlx-initial-focus]",
3436
+ mandatory: false,
3437
+ onEscape: onClose
3438
+ });
3439
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
3440
+ open: surfaceOpen,
3441
+ title: t("removeTitle"),
3442
+ closeLabel: t("cancel"),
3443
+ onClose,
3444
+ headless: true,
3445
+ className: "mdlx-modal mdlx-modal-confirm",
3446
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3447
+ ref: contentRef,
3448
+ className: "mdlx-modal-content",
3449
+ "data-mdlx-dialog-surface": "",
3450
+ tabIndex: -1,
3451
+ children: [
3452
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3453
+ className: "mdlx-heading",
3454
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3455
+ className: "mdlx-modal-title",
3456
+ children: t("removeTitle")
3457
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3458
+ className: "mdlx-modal-description",
3459
+ children: t("removeDescription")
3460
+ })]
3461
+ }),
3462
+ errorCode !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorNotice, {
3463
+ code: errorCode,
3464
+ t
3465
+ }),
3466
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3467
+ className: "mdlx-actions",
3468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3469
+ type: "button",
3470
+ variant: "outline",
3471
+ disabled: busy,
3472
+ "data-mdlx-initial-focus": "",
3473
+ onClick: onClose,
3474
+ children: t("cancel")
3475
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3476
+ type: "button",
3477
+ variant: "primary",
3478
+ disabled: busy,
3479
+ "aria-busy": busy,
3480
+ onClick: onConfirm,
3481
+ children: busy ? t("removing") : t("removeConfirm")
3482
+ })]
3483
+ }),
3484
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BusyStatus, {
3485
+ busy,
3486
+ text: t("removing")
3487
+ })
3488
+ ]
3489
+ })
3490
+ });
3491
+ }
3492
+ function sameServices(a, b) {
3493
+ return a.design === b.design && a.llm === b.llm && a.web === b.web;
3494
+ }
3495
+ function llmHealthText(health, t) {
3496
+ switch (health) {
3497
+ case "ready": return t("llmReady");
3498
+ case "missing": return t("llmMissing");
3499
+ case "disabled": return t("llmDisabled");
3500
+ case "error": return t("llmError");
3501
+ case "policy-blocked": return t("llmPolicyBlocked");
3502
+ case "unknown": return t("llmUnknown");
3503
+ }
3504
+ }
3505
+ //#endregion
3506
+ //#region src/client/styles.ts
3507
+ const MODELLIX_CLIENT_CSS = String.raw`
3508
+ .mdlx-settings,.mdlx-design,.mdlx-modal-content{font-family:var(--dsw-font-family,inherit);color:var(--dsw-alias-label-primary);box-sizing:border-box}.mdlx-settings *,.mdlx-design *,.mdlx-modal-content *{box-sizing:border-box}
3509
+ .mdlx-settings{width:min(100%,760px);min-width:0;display:grid;gap:12px;padding:4px}
3510
+ .mdlx-heading{display:grid;gap:4px;min-width:0;margin:0 0 8px}.mdlx-heading h2,.mdlx-heading h3,.mdlx-heading p{margin:0;overflow-wrap:anywhere}
3511
+ .mdlx-heading h2{font-size:20px;line-height:28px;font-weight:500}.mdlx-heading h3{font-size:15px;line-height:22px;font-weight:600}
3512
+ .mdlx-muted,.mdlx-help{margin:0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5;overflow-wrap:anywhere}
3513
+ .mdlx-card{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:16px;display:grid;gap:16px}
3514
+ .mdlx-card-head,.mdlx-status-row,.mdlx-actions,.mdlx-result-head{display:flex;align-items:center;gap:12px;min-width:0;justify-content:space-between;flex-wrap:wrap}
3515
+ .mdlx-card-head>.mdlx-heading{margin-bottom:0}
3516
+ .mdlx-status-copy{display:flex;align-items:center;gap:8px;min-width:0}.mdlx-status-copy span{overflow-wrap:anywhere}
3517
+ .mdlx-actions{justify-content:flex-end}.mdlx-actions>*{min-width:0;max-width:100%}.mdlx-actions-start{justify-content:flex-start}.mdlx-grow{flex:1 1 auto}.mdlx-settings button,.mdlx-design button,.mdlx-modal-content button{height:auto;min-height:36px}
3518
+ .mdlx-service-list{display:grid;gap:8px}.mdlx-switch-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;padding:12px;border-radius:12px;border:1px solid var(--dsw-alias-border-l2)}
3519
+ .mdlx-switch-copy{display:grid;gap:2px;min-width:0}.mdlx-switch-copy strong{font-size:14px;font-weight:500;overflow-wrap:anywhere}.mdlx-switch-copy span{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;overflow-wrap:anywhere}
3520
+ .mdlx-switch-target{display:inline-flex;width:24px;height:24px;align-items:center;justify-content:center;justify-self:center}.mdlx-switch{width:24px;height:24px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}.mdlx-switch:disabled{cursor:not-allowed}
3521
+ .mdlx-field{display:grid;gap:8px;min-width:0}.mdlx-label{font-size:14px;font-weight:500;overflow-wrap:anywhere}.mdlx-label>.mdlx-required,.mdlx-label>.mdlx-muted{margin-inline-start:4px}.mdlx-required{color:var(--dsw-alias-state-error-primary)}
3522
+ .mdlx-input-row{display:grid;grid-template-columns:minmax(0,1fr) max-content;gap:8px;min-width:0;align-items:center}.mdlx-input-row>.mdlx-input{width:100%;min-width:0;max-width:100%;box-sizing:border-box}.mdlx-input-row>button{min-width:max-content;min-height:40px;justify-self:end}.mdlx-input{width:100%;min-width:0;max-width:100%;box-sizing:border-box;border-color:var(--dsw-alias-label-tertiary)}
3523
+ .mdlx-model-tools{display:grid;grid-template-columns:minmax(120px,1fr) minmax(112px,.45fr) auto;gap:8px;align-items:center}
3524
+ .mdlx-model-tools>button{min-height:40px}.mdlx-textarea,.mdlx-select,.mdlx-native-input{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-label-tertiary);border-radius:12px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px}.mdlx-textarea{min-height:108px;padding:10px 12px;resize:vertical}.mdlx-textarea-small{min-height:80px}.mdlx-select,.mdlx-native-input{min-height:40px;padding:8px 12px}
3525
+ .mdlx-input:hover,.mdlx-textarea:hover,.mdlx-select:hover,.mdlx-native-input:hover{border-color:var(--dsw-alias-label-secondary)}
3526
+ .mdlx-input:has(input[aria-invalid="true"]),.mdlx-textarea[aria-invalid="true"],.mdlx-select[aria-invalid="true"],.mdlx-native-input[aria-invalid="true"]{border-color:var(--dsw-alias-state-error-primary)}.mdlx-input:has(input:disabled),.mdlx-textarea:disabled,.mdlx-select:disabled,.mdlx-native-input:disabled{cursor:not-allowed;opacity:.55}
3527
+ .mdlx-error{min-width:0;border:1px solid var(--dsw-alias-state-error-primary);border-radius:12px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-label-primary);padding:10px 12px;font-size:13px;line-height:20px;overflow-wrap:anywhere}.mdlx-error p{margin:4px 0 0}.mdlx-error ul{margin:8px 0 0;padding-inline-start:20px}
3528
+ .mdlx-info{display:grid;gap:12px;min-width:0;border-radius:12px;background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary);padding:10px 12px;font-size:13px;line-height:20px;overflow-wrap:anywhere}.mdlx-info p{margin:0}
3529
+ .mdlx-live{min-height:20px;font-size:13px;color:var(--dsw-alias-label-secondary)}.mdlx-live:empty{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
3530
+ .mdlx-safe-link{display:inline-flex;align-items:center;gap:6px;max-width:100%;color:var(--dsw-alias-brand-text);font-size:13px;text-decoration:none;min-height:24px;overflow-wrap:anywhere}.mdlx-safe-link:hover{text-decoration:underline}
3531
+ .mdlx-link-button{appearance:none;border:0;padding:0;background:transparent;font:inherit;cursor:pointer}
3532
+ .mdlx-modal{box-sizing:border-box;width:min(600px,calc(100vw - 48px));max-height:calc(100dvh - 48px);padding:0!important;gap:0!important;border-radius:24px!important;box-shadow:var(--dsw-shadow-lv3)!important;overflow:auto;overscroll-behavior:contain}.mdlx-modal-confirm{width:min(380px,calc(100vw - 48px))}.mdlx-modal-content{width:100%;min-width:0;padding:28px;display:grid;gap:20px;outline:none}.mdlx-modal-content>.mdlx-heading{margin-bottom:0}.mdlx-modal-title{font-size:20px;line-height:28px;font-weight:500;margin:0;overflow-wrap:anywhere}.mdlx-modal-description{font-size:14px;line-height:22px;color:var(--dsw-alias-label-secondary);margin:0;overflow-wrap:anywhere}
3533
+ .mdlx-design{container-type:inline-size;height:100%;min-height:0;box-sizing:border-box;padding:16px;padding-bottom:max(16px,env(safe-area-inset-bottom));scroll-padding-block-end:max(24px,env(safe-area-inset-bottom));overflow:auto;background:var(--dsw-alias-bg-base)}
3534
+ .mdlx-design-shell{width:100%;min-height:100%;display:grid;grid-template-columns:minmax(300px,42fr) minmax(360px,58fr);gap:16px}.mdlx-design-pane{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:16px;display:flex;flex-direction:column;gap:16px;overflow-wrap:anywhere}
3535
+ .mdlx-design-scroll{display:grid;gap:16px;min-height:0}.mdlx-parameter-list{display:grid;gap:12px}.mdlx-parameter{display:grid;gap:6px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}
3536
+ .mdlx-advanced{display:grid;gap:12px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px}.mdlx-advanced>summary{cursor:pointer;font-size:14px;font-weight:500;min-height:24px;overflow-wrap:anywhere}
3537
+ .mdlx-proposal{display:grid;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:12px;background:var(--dsw-alias-bg-layer-2)}
3538
+ .mdlx-generate-block{display:grid;gap:8px;min-width:0}.mdlx-generate-block>p{margin:0}
3539
+ .mdlx-change-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.mdlx-change{display:grid;grid-template-columns:minmax(100px,.4fr) minmax(0,1fr);gap:8px;min-width:0;font-size:13px}.mdlx-change>*{min-width:0;overflow-wrap:anywhere}.mdlx-code{font-family:var(--ds-font-family-code,monospace);font-size:12px;overflow-wrap:anywhere}
3540
+ .mdlx-result-section{display:grid;gap:10px}.mdlx-result-section h3{margin:0;font-size:14px;font-weight:600}.mdlx-result-list{display:grid;gap:12px;margin:0;padding:0;list-style:none}
3541
+ .mdlx-result-card{display:grid;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:12px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}
3542
+ .mdlx-resource-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.mdlx-resource{display:grid;gap:8px;min-width:0}.mdlx-media{display:block;width:100%;max-height:360px;object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
3543
+ .mdlx-media-audio{min-height:48px}.mdlx-empty{display:grid;place-items:center;min-height:160px;text-align:center;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;padding:24px}
3544
+ .mdlx-image-modal{width:min(1000px,calc(100vw - 48px))}.mdlx-image-full{display:block;width:100%;max-height:calc(100dvh - 220px);object-fit:contain;border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
3545
+ .mdlx-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
3546
+ .mdlx-input{height:auto;min-height:40px}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
3547
+ @media (max-width:768px){.mdlx-design{padding:12px;padding-bottom:max(12px,env(safe-area-inset-bottom))}.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
3548
+ @container (max-width:992px){.mdlx-design-shell{grid-template-columns:minmax(0,1fr)}.mdlx-design-pane{padding:12px}.mdlx-resource-list{grid-template-columns:minmax(0,1fr)}}
3549
+ @media (max-width:560px){.mdlx-modal{width:calc(100vw - 48px)}.mdlx-modal-content{padding:24px}.mdlx-input-row,.mdlx-model-tools{grid-template-columns:minmax(0,1fr)}.mdlx-input-row>button,.mdlx-model-tools button,.mdlx-actions button{width:100%;min-width:0;justify-self:stretch}.mdlx-actions{display:grid;grid-template-columns:minmax(0,1fr)}.mdlx-switch-row{grid-template-columns:minmax(0,1fr) 48px}.mdlx-change{grid-template-columns:minmax(0,1fr)}}
3550
+ @media (pointer:coarse){.mdlx-switch-row,.mdlx-switch-target,.mdlx-safe-link{min-width:48px;min-height:48px}.mdlx-settings button,.mdlx-design button,.mdlx-modal-content button{min-height:48px}.mdlx-input,.mdlx-select,.mdlx-native-input,.mdlx-advanced>summary{min-height:48px}}
3551
+ @media (prefers-reduced-motion:reduce){.mdlx-settings *,.mdlx-design *,.mdlx-modal-content *{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
3552
+ @media (forced-colors:active){.mdlx-modal{border:1px solid CanvasText;box-shadow:none!important}.mdlx-card,.mdlx-switch-row,.mdlx-design-pane,.mdlx-result-card,.mdlx-proposal,.mdlx-error,.mdlx-info{forced-color-adjust:auto;border:1px solid CanvasText}.mdlx-input,.mdlx-textarea,.mdlx-select,.mdlx-native-input{border-color:CanvasText}.mdlx-safe-link{color:LinkText}.mdlx-input:focus-within,.mdlx-textarea:focus-visible,.mdlx-select:focus-visible,.mdlx-native-input:focus-visible,.mdlx-switch:focus-visible,.mdlx-safe-link:focus-visible,.mdlx-modal-content:focus-visible{outline-color:Highlight}}
3553
+ `;
3554
+ function installModellixClientStyles(target = typeof document === "undefined" ? void 0 : document) {
3555
+ if (target === void 0) return () => void 0;
3556
+ const element = target.createElement("style");
3557
+ element.setAttribute("data-dsh-modellix", "client");
3558
+ element.textContent = MODELLIX_CLIENT_CSS;
3559
+ target.head.append(element);
3560
+ return () => element.remove();
3561
+ }
3562
+ //#endregion
3563
+ //#region src/client/registration.ts
3564
+ const MODELLIX_CLIENT_SLOTS = Object.freeze([
3565
+ {
3566
+ name: "shell.overlay",
3567
+ id: "modellix.credential-recovery",
3568
+ order: 10
3569
+ },
3570
+ {
3571
+ name: "settings.onboarding",
3572
+ id: "modellix.onboarding",
3573
+ order: 10
3574
+ },
3575
+ {
3576
+ name: "settings.section",
3577
+ id: "modellix",
3578
+ order: 30
3579
+ },
3580
+ {
3581
+ name: "conversation.view",
3582
+ id: "modellix.design",
3583
+ order: 20
3584
+ }
3585
+ ]);
3586
+ //#endregion
3587
+ //#region src/client/index.tsx
3588
+ const inject = [
3589
+ "slots",
3590
+ "locale",
3591
+ "connection"
3592
+ ];
3593
+ function apply(ctx) {
3594
+ ctx.effect(() => ctx.locale.register(MODELLIX_LOCALE_NAMESPACE, {
3595
+ zh,
3596
+ en
3597
+ }), "modellix: client dictionaries");
3598
+ ctx.effect(() => installModellixClientStyles(), "modellix: client styles");
3599
+ const t = ctx.locale.bind(MODELLIX_LOCALE_NAMESPACE);
3600
+ const rpc = new ModellixRpcClient(ctx.connection.rpc);
3601
+ const settingsController = new SettingsController(rpc);
3602
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
3603
+ name: "shell.overlay",
3604
+ id: "modellix.credential-recovery",
3605
+ order: 10,
3606
+ locale: MODELLIX_LOCALE_NAMESPACE,
3607
+ inject: () => ({ controller: settingsController })
3608
+ }, CredentialRecoveryOverlay));
3609
+ ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3610
+ name: "settings.onboarding",
3611
+ id: "modellix.onboarding",
3612
+ order: 10,
3613
+ locale: MODELLIX_LOCALE_NAMESPACE,
3614
+ inject: () => ({ controller: settingsController })
3615
+ }, ModellixOnboarding));
3616
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
3617
+ name: "settings.section",
3618
+ id: "modellix",
3619
+ order: 30,
3620
+ label: () => t("nav"),
3621
+ locale: MODELLIX_LOCALE_NAMESPACE,
3622
+ inject: () => ({ controller: settingsController })
3623
+ }, ModellixSettingsSection));
3624
+ ctx.slots.inject("conversation.view", () => ctx.slots.register({
3625
+ name: "conversation.view",
3626
+ id: "modellix.design",
3627
+ order: 20,
3628
+ label: () => t("designTab"),
3629
+ locale: MODELLIX_LOCALE_NAMESPACE,
3630
+ inject: (sessionId) => ({
3631
+ controller: new DesignController(rpc, sessionId),
3632
+ settingsController
3633
+ })
3634
+ }, ModellixDesignView));
3635
+ }
3636
+ //#endregion
3637
+ exports.MODELLIX_CLIENT_SLOTS = MODELLIX_CLIENT_SLOTS;
3638
+ exports.MODELLIX_CLIENT_WIRE_VERSION = MODELLIX_CLIENT_WIRE_VERSION;
3639
+ exports.MODELLIX_RPC_CHANNEL = MODELLIX_RPC_CHANNEL;
3640
+ exports.MODELLIX_RPC_ENDPOINTS = MODELLIX_RPC_ENDPOINTS;
3641
+ exports.ModellixClientContractError = ModellixClientContractError;
3642
+ exports.apply = apply;
3643
+ exports.inject = inject;
3644
+ exports.parseAck = parseAck;
3645
+ exports.parseDesignMutation = parseDesignMutation;
3646
+ exports.parseDesignSnapshot = parseDesignSnapshot;
3647
+ exports.parseSettingsMutation = parseSettingsMutation;
3648
+ exports.parseSettingsSnapshot = parseSettingsSnapshot;
3649
+ exports.safeResourceHref = safeResourceHref;
3650
+ exports.sanitizeParameters = sanitizeParameters;
3651
+
3652
+ return module.exports; } });
3653
+ //# sourceMappingURL=client.js.map