dsh-llm-fallbacks 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,11 +4,170 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let _deepseek_ai_dsh_client_web_react = require("@deepseek-ai/dsh-client-web-react");
7
+ //#region \0rolldown/runtime.js
8
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
9
+ //#endregion
8
10
  let react = require("react");
9
11
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
12
  let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
11
13
  let react_jsx_runtime = require("react/jsx-runtime");
14
+ //#region node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js
15
+ /**
16
+ * @license React
17
+ * use-sync-external-store-shim.production.js
18
+ *
19
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
20
+ *
21
+ * This source code is licensed under the MIT license found in the
22
+ * LICENSE file in the root directory of this source tree.
23
+ */
24
+ var require_use_sync_external_store_shim_production = /* @__PURE__ */ __commonJSMin(((exports) => {
25
+ var React$1 = require("react");
26
+ function is(x, y) {
27
+ return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y;
28
+ }
29
+ var objectIs = "function" === typeof Object.is ? Object.is : is;
30
+ var useState = React$1.useState;
31
+ var useEffect = React$1.useEffect;
32
+ var useLayoutEffect = React$1.useLayoutEffect;
33
+ var useDebugValue = React$1.useDebugValue;
34
+ function useSyncExternalStore$2(subscribe, getSnapshot) {
35
+ var value = getSnapshot(), _useState = useState({ inst: {
36
+ value,
37
+ getSnapshot
38
+ } }), inst = _useState[0].inst, forceUpdate = _useState[1];
39
+ useLayoutEffect(function() {
40
+ inst.value = value;
41
+ inst.getSnapshot = getSnapshot;
42
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst });
43
+ }, [
44
+ subscribe,
45
+ value,
46
+ getSnapshot
47
+ ]);
48
+ useEffect(function() {
49
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst });
50
+ return subscribe(function() {
51
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst });
52
+ });
53
+ }, [subscribe]);
54
+ useDebugValue(value);
55
+ return value;
56
+ }
57
+ function checkIfSnapshotChanged(inst) {
58
+ var latestGetSnapshot = inst.getSnapshot;
59
+ inst = inst.value;
60
+ try {
61
+ var nextValue = latestGetSnapshot();
62
+ return !objectIs(inst, nextValue);
63
+ } catch (error) {
64
+ return !0;
65
+ }
66
+ }
67
+ function useSyncExternalStore$1(subscribe, getSnapshot) {
68
+ return getSnapshot();
69
+ }
70
+ var shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
71
+ exports.useSyncExternalStore = void 0 !== React$1.useSyncExternalStore ? React$1.useSyncExternalStore : shim;
72
+ }));
73
+ //#endregion
74
+ //#region node_modules/use-sync-external-store/shim/index.js
75
+ var require_shim = /* @__PURE__ */ __commonJSMin(((exports, module) => {
76
+ module.exports = require_use_sync_external_store_shim_production();
77
+ }));
78
+ //#endregion
79
+ //#region node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.js
80
+ /**
81
+ * @license React
82
+ * use-sync-external-store-shim/with-selector.production.js
83
+ *
84
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
85
+ *
86
+ * This source code is licensed under the MIT license found in the
87
+ * LICENSE file in the root directory of this source tree.
88
+ */
89
+ var require_with_selector_production = /* @__PURE__ */ __commonJSMin(((exports) => {
90
+ var React = require("react");
91
+ var shim = require_shim();
92
+ function is(x, y) {
93
+ return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y;
94
+ }
95
+ var objectIs = "function" === typeof Object.is ? Object.is : is;
96
+ var useSyncExternalStore = shim.useSyncExternalStore;
97
+ var useRef = React.useRef;
98
+ var useEffect = React.useEffect;
99
+ var useMemo = React.useMemo;
100
+ var useDebugValue = React.useDebugValue;
101
+ exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
102
+ var instRef = useRef(null);
103
+ if (null === instRef.current) {
104
+ var inst = {
105
+ hasValue: !1,
106
+ value: null
107
+ };
108
+ instRef.current = inst;
109
+ } else inst = instRef.current;
110
+ instRef = useMemo(function() {
111
+ function memoizedSelector(nextSnapshot) {
112
+ if (!hasMemo) {
113
+ hasMemo = !0;
114
+ memoizedSnapshot = nextSnapshot;
115
+ nextSnapshot = selector(nextSnapshot);
116
+ if (void 0 !== isEqual && inst.hasValue) {
117
+ var currentSelection = inst.value;
118
+ if (isEqual(currentSelection, nextSnapshot)) return memoizedSelection = currentSelection;
119
+ }
120
+ return memoizedSelection = nextSnapshot;
121
+ }
122
+ currentSelection = memoizedSelection;
123
+ if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection;
124
+ var nextSelection = selector(nextSnapshot);
125
+ if (void 0 !== isEqual && isEqual(currentSelection, nextSelection)) return memoizedSnapshot = nextSnapshot, currentSelection;
126
+ memoizedSnapshot = nextSnapshot;
127
+ return memoizedSelection = nextSelection;
128
+ }
129
+ var hasMemo = !1, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot;
130
+ return [function() {
131
+ return memoizedSelector(getSnapshot());
132
+ }, null === maybeGetServerSnapshot ? void 0 : function() {
133
+ return memoizedSelector(maybeGetServerSnapshot());
134
+ }];
135
+ }, [
136
+ getSnapshot,
137
+ getServerSnapshot,
138
+ selector,
139
+ isEqual
140
+ ]);
141
+ var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]);
142
+ useEffect(function() {
143
+ inst.hasValue = !0;
144
+ inst.value = value;
145
+ }, [value]);
146
+ useDebugValue(value);
147
+ return value;
148
+ };
149
+ }));
150
+ //#endregion
151
+ //#region src/client/use-snapshot.ts
152
+ var import_with_selector = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
153
+ module.exports = require_with_selector_production();
154
+ })))();
155
+ /**
156
+ * Bind a bare observable source to a typed uSES selector hook.
157
+ * subscribe/getSnapshot are captured once per source into stable closures
158
+ * (also re-binds `this` for method-based sources), so components never
159
+ * resubscribe across renders. Equality defaults to Object.is.
160
+ * @param w - snapshot source (engine store, Session object, store instance).
161
+ * @returns the selector hook.
162
+ */
163
+ function bindSnapshotSelector(w) {
164
+ const subscribe = (fn) => w.subscribe(fn);
165
+ const getSnapshot = () => w.getSnapshot();
166
+ return function useSelector(sel, eq) {
167
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(subscribe, getSnapshot, void 0, sel, eq);
168
+ };
169
+ }
170
+ //#endregion
12
171
  //#region src/selectors.ts
13
172
  /** Catchable error for illegal/unknown selectors (config-warning path). */
14
173
  var SelectorError = class extends Error {
@@ -38,6 +197,238 @@ window.__ModuleLoader__.load({
38
197
  raw: trimmed
39
198
  };
40
199
  }
200
+ /**
201
+ * Frozen preset windows (UTC+8). `liang-*` presets have NO day mask (they
202
+ * apply every day, weekends included); `glm-peak` is Monday–Friday only.
203
+ * The two valleys are `complement: true` of their peak.
204
+ */
205
+ const PRESETS = {
206
+ "liang-peak": {
207
+ windows: [{
208
+ start: "09:00",
209
+ end: "12:00"
210
+ }, {
211
+ start: "14:00",
212
+ end: "18:00"
213
+ }],
214
+ complement: false,
215
+ label: "Liang Peak"
216
+ },
217
+ "liang-valley": {
218
+ windows: [{
219
+ start: "09:00",
220
+ end: "12:00"
221
+ }, {
222
+ start: "14:00",
223
+ end: "18:00"
224
+ }],
225
+ complement: true,
226
+ label: "Liang Valley"
227
+ },
228
+ "glm-peak": {
229
+ windows: [{
230
+ start: "14:00",
231
+ end: "18:00",
232
+ days: [
233
+ 1,
234
+ 2,
235
+ 3,
236
+ 4,
237
+ 5
238
+ ]
239
+ }],
240
+ complement: false,
241
+ label: "GLM Peak"
242
+ },
243
+ "glm-valley": {
244
+ windows: [{
245
+ start: "14:00",
246
+ end: "18:00",
247
+ days: [
248
+ 1,
249
+ 2,
250
+ 3,
251
+ 4,
252
+ 5
253
+ ]
254
+ }],
255
+ complement: true,
256
+ label: "GLM Valley"
257
+ }
258
+ };
259
+ const HHMM_RE$1 = /^([01]\d|2[0-3]):[0-5]\d$/;
260
+ const WEEKDAY_INDEX = {
261
+ Sun: 0,
262
+ Mon: 1,
263
+ Tue: 2,
264
+ Wed: 3,
265
+ Thu: 4,
266
+ Fri: 5,
267
+ Sat: 6
268
+ };
269
+ /** Malformed rows warn once per row INSTANCE (config snapshots are stable
270
+ * across requests — this is what keeps "warn once" from becoming spam). */
271
+ const warnedMalformedRows = /* @__PURE__ */ new WeakSet();
272
+ /** Invalid `tz` values warn once per distinct value. */
273
+ const warnedTimeZones = /* @__PURE__ */ new Set();
274
+ function warnMalformed(row, reason) {
275
+ if (typeof row !== "object" || row === null) {
276
+ console.warn(`llm-fallbacks: skipping malformed time-slot row (${reason})`);
277
+ return;
278
+ }
279
+ if (warnedMalformedRows.has(row)) return;
280
+ warnedMalformedRows.add(row);
281
+ console.warn(`llm-fallbacks: skipping malformed time-slot row (${reason}): ${JSON.stringify(row)}`);
282
+ }
283
+ /** Wall-clock weekday (0=Sunday) + minutes-since-midnight of `now` in `tz`
284
+ * (standard `Intl` timezone rules, DST-safe). */
285
+ function wallClock(now, tz) {
286
+ try {
287
+ const parts = new Intl.DateTimeFormat("en-US", {
288
+ timeZone: tz,
289
+ weekday: "short",
290
+ hour: "2-digit",
291
+ minute: "2-digit",
292
+ hourCycle: "h23"
293
+ }).formatToParts(now);
294
+ let weekday = 0;
295
+ let minutes = 0;
296
+ for (const part of parts) if (part.type === "weekday") weekday = WEEKDAY_INDEX[part.value] ?? 0;
297
+ else if (part.type === "hour") minutes += Number(part.value) * 60;
298
+ else if (part.type === "minute") minutes += Number(part.value);
299
+ return {
300
+ weekday,
301
+ minutes
302
+ };
303
+ } catch (error) {
304
+ if (!warnedTimeZones.has(tz)) {
305
+ warnedTimeZones.add(tz);
306
+ console.warn(`llm-fallbacks: invalid timezone "${tz}" (${error.message}) — slot matching falls back to UTC`);
307
+ }
308
+ return {
309
+ weekday: now.getUTCDay(),
310
+ minutes: now.getUTCHours() * 60 + now.getUTCMinutes()
311
+ };
312
+ }
313
+ }
314
+ /** `HH:mm` → minutes-since-midnight (inputs are regex-validated or frozen
315
+ * constants — this never sees garbage). */
316
+ function minutesOf(hhmm) {
317
+ const [hour, minute] = hhmm.split(":").map(Number);
318
+ return hour * 60 + minute;
319
+ }
320
+ /** P4 containment rule: day mask matches AND the window contains `t`
321
+ * (`end` exclusive; `start > end` wraps midnight — custom rows only). */
322
+ function containsWindow(window, clock) {
323
+ if (!(window.days === void 0 || window.days.length === 0 || window.days.includes(clock.weekday))) return false;
324
+ const start = minutesOf(window.start);
325
+ const end = minutesOf(window.end);
326
+ return start <= end ? start <= clock.minutes && clock.minutes < end : start <= clock.minutes || clock.minutes < end;
327
+ }
328
+ function matchesAnyWindow(windows, clock) {
329
+ return windows.some((window) => containsWindow(window, clock));
330
+ }
331
+ /** Validate one stored row → frozen windows, or `undefined` (warn once +
332
+ * skip). Preset rows reject stored windows/day masks (P4); custom rows
333
+ * require strict `HH:mm` bounds; chains must be non-empty. */
334
+ function describeRow(row) {
335
+ if (!Array.isArray(row.chain) || row.chain.length === 0) {
336
+ warnMalformed(row, "empty chain");
337
+ return;
338
+ }
339
+ if (row.kind === "preset") {
340
+ const preset = row.preset;
341
+ if (typeof preset !== "string" || !Object.hasOwn(PRESETS, preset)) {
342
+ warnMalformed(row, `unknown preset ${JSON.stringify(preset)}`);
343
+ return;
344
+ }
345
+ if (row.start !== void 0 || row.end !== void 0 || row.days !== void 0 && row.days.length > 0) {
346
+ warnMalformed(row, `preset windows are fixed — row "${preset}" cannot carry start/end/days`);
347
+ return;
348
+ }
349
+ return PRESETS[preset];
350
+ }
351
+ if (row.kind === "custom") {
352
+ const { start, end } = row;
353
+ if (typeof start !== "string" || typeof end !== "string" || !HHMM_RE$1.test(start) || !HHMM_RE$1.test(end)) {
354
+ warnMalformed(row, `invalid custom window ${JSON.stringify(start)}-${JSON.stringify(end)} (expected HH:mm)`);
355
+ return;
356
+ }
357
+ return {
358
+ windows: [{
359
+ start,
360
+ end,
361
+ days: row.days
362
+ }],
363
+ complement: false
364
+ };
365
+ }
366
+ warnMalformed(row, `unknown kind ${JSON.stringify(row.kind)}`);
367
+ }
368
+ /** Display label for a winning row (preset rows use the frozen label;
369
+ * custom rows prefer their display name, falling back to the window). */
370
+ function labelOf(row) {
371
+ if (row.kind === "preset" && typeof row.preset === "string" && Object.hasOwn(PRESETS, row.preset)) return PRESETS[row.preset].label;
372
+ return row.name !== void 0 && row.name.trim() !== "" ? row.name : `custom ${row.start}-${row.end}`;
373
+ }
374
+ /**
375
+ * All-day conformance (P6): the all-day chain is conforming when its LAST
376
+ * entry (the tail — the card's 默认模型 panel) is exactly one official V4
377
+ * model — Flash XOR Pro. Leading entries (the card's 默认降级链 block) are
378
+ * the ordered walk before that last-resort fallback. An empty chain or a
379
+ * chain whose tail is not an official V4 model keeps slot rows inert and
380
+ * refuses the virtual-row override/delegate; the v0.2.2 failure walk over
381
+ * the raw chain stays verbatim.
382
+ */
383
+ function isAllDayConforming(chain) {
384
+ if (chain.length < 1) return false;
385
+ const tail = chain[chain.length - 1];
386
+ return tail === "deepseek-official/deepseek-v4-flash" || tail === "deepseek-official/deepseek-v4-pro";
387
+ }
388
+ /**
389
+ * Slot winner + display label (P5): drives 分时切换 detection (per-root-agent
390
+ * last-winner marker, in-process only) and the card / `/fallbacks` status
391
+ * strip. `winner` is the matching row or `'all-day'`; `label` names the
392
+ * slot (frozen preset label or `custom HH:mm-HH:mm`).
393
+ *
394
+ * P6 gate (qc1 F-001): without a conforming all-day
395
+ * (`isAllDayConforming(config.rootChain)`) the winner is ALWAYS `'all-day'`
396
+ * — a legacy multi-model (or empty) chain earns no slot rows, so every
397
+ * surface fed by this resolver reports the inert state and routing stays on
398
+ * the raw `rootChain` (the v0.2.2 walk verbatim).
399
+ */
400
+ function resolveSlotState(config, now, tz) {
401
+ if (!isAllDayConforming(config.rootChain)) return {
402
+ winner: "all-day",
403
+ label: "all-day"
404
+ };
405
+ const clock = wallClock(now, tz);
406
+ const seenPresets = /* @__PURE__ */ new Set();
407
+ const rows = Array.isArray(config.timeSlots) ? config.timeSlots : [];
408
+ for (const row of rows) {
409
+ if (typeof row !== "object" || row === null) {
410
+ warnMalformed(row, "row is not an object");
411
+ continue;
412
+ }
413
+ if (row.kind === "preset" && typeof row.preset === "string") {
414
+ if (seenPresets.has(row.preset)) {
415
+ warnMalformed(row, `duplicate preset "${row.preset}" — only the first row takes effect`);
416
+ continue;
417
+ }
418
+ seenPresets.add(row.preset);
419
+ }
420
+ const descriptor = describeRow(row);
421
+ if (descriptor === void 0) continue;
422
+ if (descriptor.complement ? !matchesAnyWindow(descriptor.windows, clock) : matchesAnyWindow(descriptor.windows, clock)) return {
423
+ winner: row,
424
+ label: labelOf(row)
425
+ };
426
+ }
427
+ return {
428
+ winner: "all-day",
429
+ label: "all-day"
430
+ };
431
+ }
41
432
  //#endregion
42
433
  //#region src/config.ts
43
434
  /**
@@ -64,7 +455,9 @@ window.__ModuleLoader__.load({
64
455
  maxSwitchesPerStep: 8,
65
456
  alwaysModeRetryCap: 5,
66
457
  presets: "bundled",
67
- roleAutoMatch: true
458
+ roleAutoMatch: true,
459
+ timeSlots: [],
460
+ tz: "Asia/Shanghai"
68
461
  };
69
462
  /**
70
463
  * Reserved role id: legal as a rule target (`roles.rules[].role`) and as
@@ -84,7 +477,7 @@ window.__ModuleLoader__.load({
84
477
  return typeof value === "object" && value !== null && !Array.isArray(value);
85
478
  }
86
479
  /**
87
- * Read a nested value by path — the `@deepseek-ai/dsh-client-schema-form`
480
+ * Read a nested value by path — the upstream `dsh-client-schema-form`
88
481
  * `getPath` semantics, copied locally so the provider-configured join needs no
89
482
  * new dependency (array indexes as numeric keys, `undefined` along a missing
90
483
  * branch).
@@ -115,6 +508,15 @@ window.__ModuleLoader__.load({
115
508
  return typeof entry.id === "string" && typeof entry.overridden === "boolean";
116
509
  });
117
510
  }
511
+ /** Seed-default persona from a revert-seed wire body (issue #59). */
512
+ function revertOutcomePersona(value) {
513
+ if (value === null || typeof value !== "object" || !("outcome" in value)) return void 0;
514
+ const outcome = value.outcome;
515
+ if (outcome === null || typeof outcome !== "object") return void 0;
516
+ if (!("reverted" in outcome) || outcome.reverted !== true) return void 0;
517
+ if (!("persona" in outcome) || typeof outcome.persona !== "string") return void 0;
518
+ return outcome.persona;
519
+ }
118
520
  /**
119
521
  * The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
120
522
  * settings profile resolves in the describe namespaces — the Models page's
@@ -195,6 +597,23 @@ window.__ModuleLoader__.load({
195
597
  if (enabled !== void 0 && typeof enabled !== "boolean") throw new TypeError("fallbacks descriptor enabled must be a boolean");
196
598
  const roleAutoMatch = value.roleAutoMatch;
197
599
  if (roleAutoMatch !== void 0 && typeof roleAutoMatch !== "boolean") throw new TypeError("fallbacks descriptor roleAutoMatch must be a boolean");
600
+ const timeSlots = value.timeSlots;
601
+ if (timeSlots !== void 0 && (!Array.isArray(timeSlots) || timeSlots.some((row) => {
602
+ if (!isRecord(row)) return true;
603
+ for (const field of [
604
+ "kind",
605
+ "preset",
606
+ "start",
607
+ "end"
608
+ ]) if (row[field] !== void 0 && typeof row[field] !== "string") return true;
609
+ const chain = row.chain;
610
+ if (chain !== void 0 && (!Array.isArray(chain) || chain.some((entry) => typeof entry !== "string"))) return true;
611
+ const days = row.days;
612
+ if (days !== void 0 && (!Array.isArray(days) || days.some((day) => typeof day !== "number"))) return true;
613
+ return false;
614
+ }))) throw new TypeError("fallbacks descriptor timeSlots must be an array of slot rows (kind/preset/start/end strings, chain string array, days number array)");
615
+ const tz = value.tz;
616
+ if (tz !== void 0 && typeof tz !== "string") throw new TypeError("fallbacks descriptor tz must be a string");
198
617
  return {
199
618
  enabled: enabled ?? defaultFallbacksConfig.enabled,
200
619
  triggerCodes: triggerCodes ?? [...defaultFallbacksConfig.triggerCodes],
@@ -208,7 +627,9 @@ window.__ModuleLoader__.load({
208
627
  maxSwitchesPerStep: maxSwitchesPerStep ?? defaultFallbacksConfig.maxSwitchesPerStep,
209
628
  alwaysModeRetryCap: alwaysModeRetryCap ?? defaultFallbacksConfig.alwaysModeRetryCap,
210
629
  presets: presets ?? defaultFallbacksConfig.presets,
211
- roleAutoMatch: roleAutoMatch ?? defaultFallbacksConfig.roleAutoMatch
630
+ roleAutoMatch: roleAutoMatch ?? defaultFallbacksConfig.roleAutoMatch,
631
+ timeSlots: timeSlots ?? [...defaultFallbacksConfig.timeSlots ?? []],
632
+ tz: tz ?? defaultFallbacksConfig.tz
212
633
  };
213
634
  }
214
635
  /** The raw selector string a selection serializes to ('' when empty). */
@@ -310,13 +731,54 @@ window.__ModuleLoader__.load({
310
731
  }
311
732
  return entries;
312
733
  }
734
+ /** Project the time-slot rows into editable rows (chain selectors classified). */
735
+ function timeSlotsToRows(timeSlots, catalog) {
736
+ return timeSlots.map((row) => ({
737
+ kind: row.kind,
738
+ ...row.preset === void 0 ? {} : { preset: row.preset },
739
+ start: row.start ?? "",
740
+ end: row.end ?? "",
741
+ days: [...row.days ?? []],
742
+ name: row.name ?? "",
743
+ collapsed: true,
744
+ selectors: (row.chain ?? []).map((entry) => entryToSelectorRow(entry, catalog))
745
+ }));
746
+ }
747
+ /** Rebuild the time-slot rows from edited rows; blank selectors drop out.
748
+ * `kind` rides verbatim (a hand-written unknown kind reads back unchanged;
749
+ * save validation rejects it) — the cast asserts the trusted editor shape.
750
+ * `days` is ALWAYS serialized ([] included): schemastery composes absent
751
+ * array fields as `[]`, so the composed config every card load accepts
752
+ * carries `days` on every row — the draft must too, or a clean card would
753
+ * read back dirty. */
754
+ function rowsToTimeSlots(rows) {
755
+ return rows.map((row) => {
756
+ const chain = row.selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
757
+ if (row.kind === "preset") return {
758
+ kind: "preset",
759
+ preset: row.preset,
760
+ days: row.days,
761
+ chain
762
+ };
763
+ return {
764
+ kind: row.kind,
765
+ ...row.preset === void 0 ? {} : { preset: row.preset },
766
+ start: row.start,
767
+ end: row.end,
768
+ days: row.days,
769
+ ...row.name === "" ? {} : { name: row.name },
770
+ chain
771
+ };
772
+ });
773
+ }
313
774
  /** Project the declared roles into editable rows (chain selectors classified). */
314
775
  function rolesToRows(roles, catalog) {
315
776
  return roles.map((role) => ({
316
777
  id: role.id,
317
778
  persona: role.persona,
318
779
  selectors: (role.chain ?? []).map((entry) => entryToSelectorRow(entry, catalog)),
319
- fallback: role.fallback ?? "inherit-root"
780
+ fallback: role.fallback ?? "inherit-root",
781
+ collapsed: true
320
782
  }));
321
783
  }
322
784
  /** Rebuild the declared roles from edited rows; empty selectors drop out. */
@@ -365,16 +827,14 @@ window.__ModuleLoader__.load({
365
827
  /** Project the role rules into editable rows (provider/model classified). */
366
828
  function rulesToRows(rules, catalog) {
367
829
  return rules.map((rule) => ({
368
- origin: rule.origin ?? "",
369
830
  provider: classifyProvider(rule.provider ?? "", catalog),
370
831
  model: classifyModel(rule.provider ?? "", rule.model ?? "", catalog),
371
832
  role: rule.role
372
833
  }));
373
834
  }
374
- /** Rebuild the role rules from edited rows; empty origin/provider/model drop out. */
835
+ /** Rebuild the role rules from edited rows; empty provider/model drop out. */
375
836
  function rowsToRules(rows) {
376
837
  return rows.map((row) => ({
377
- ...row.origin === "" ? {} : { origin: row.origin },
378
838
  ...row.provider === null ? {} : { provider: selectionToRaw(row.provider) },
379
839
  ...row.model === null ? {} : { model: selectionToRaw(row.model) },
380
840
  role: row.role.trim()
@@ -658,12 +1118,17 @@ window.__ModuleLoader__.load({
658
1118
  * reason }` outcome is still a successful RPC — the post-write read
659
1119
  * result (config / legacyKeys / seeds) lands either way, and the revert
660
1120
  * button stays disabled while the write is in flight.
1121
+ *
1122
+ * Returns the seed-default persona when the outcome is `{ reverted:
1123
+ * true, persona }` — including the persist no-op (persisted already
1124
+ * equals the seed). The card applies that string to the row's **draft**
1125
+ * so an unsaved persona edit still snaps back (issue #59).
661
1126
  * @param id - the seeded role id; the host matches it by trimmed id
662
1127
  * against the seed registry (spec §9.3).
663
1128
  */
664
1129
  async revertSeed(id) {
665
1130
  const state = this.store.getSnapshot();
666
- if (!state.writable || state.status === "saving") return;
1131
+ if (!state.writable || state.status === "saving") return void 0;
667
1132
  const generation = ++this.writeGeneration;
668
1133
  this.store.update((draft) => {
669
1134
  draft.status = "saving";
@@ -671,7 +1136,7 @@ window.__ModuleLoader__.load({
671
1136
  });
672
1137
  try {
673
1138
  const result = await this.rpc.call("/api", "fallbacks/revert-seed", { args: { id } });
674
- if (generation !== this.writeGeneration) return;
1139
+ if (generation !== this.writeGeneration) return void 0;
675
1140
  if (!result.ok) throw result.error;
676
1141
  const value = result.value;
677
1142
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -686,9 +1151,11 @@ window.__ModuleLoader__.load({
686
1151
  if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
687
1152
  }
688
1153
  this.accept(config, true, legacyKeys, seeds);
1154
+ return revertOutcomePersona(value);
689
1155
  } catch (error) {
690
- if (generation !== this.writeGeneration) return;
1156
+ if (generation !== this.writeGeneration) return void 0;
691
1157
  this.fail(error);
1158
+ return;
692
1159
  }
693
1160
  }
694
1161
  /** Stop in-flight responses from publishing after plugin disposal. */
@@ -817,16 +1284,62 @@ window.__ModuleLoader__.load({
817
1284
  "roleAutoMatch.label": "启用角色自动匹配",
818
1285
  "roleAutoMatch.hint": "规则未命中时,由模型自选最贴近的已声明角色",
819
1286
  "roleAutoMatch.tooltip": "规则未命中时,模型会自动从已声明角色(id + persona)中选择最匹配者并注入该角色的链;关闭后未命中规则时按现状回落(inherit / rootChain)。",
820
- "rootChain.label": "root 主代理降级链",
821
- "rootChain.firstLine": "仅当当前会话所选模型失败后才启用降级——不抢占会话模型",
822
- "rootChain.hint": "优先使用当前会话的模型;失败后才按此链降级",
823
- "rootChain.tooltip": "root 主代理失败时按此有序选择器列表依次降级;未配置时行为与未安装插件一致。",
824
- "rootChain.selector.add": "添加选择器",
1287
+ "rootChain.label": "默认降级链",
1288
+ "rootChain.tooltip": "未命中任何分时槽时先走这条降级链;全部失败后落到下面的默认模型。",
1289
+ "defaultModel.label": "默认模型",
825
1290
  "chains.selector.remove": "删除该选择器",
826
1291
  "chains.selector.providerPlaceholder": "选择 provider",
827
1292
  "chains.selector.modelPlaceholder": "选择 model",
828
1293
  "chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
829
1294
  "chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
1295
+ "mainAgent.label": "主代理",
1296
+ "subagents.label": "子代理",
1297
+ "timeSlots.label": "分时槽设置",
1298
+ "timeSlots.hint": "自上而下第一条命中生效;全时段行固定最后",
1299
+ "timeSlots.tz.label": "时区(UTC±)",
1300
+ "timeSlots.drag": "拖拽排序(或使用上下按钮)",
1301
+ "timeSlots.name": "名称",
1302
+ "timeSlots.expand": "展开该行",
1303
+ "timeSlots.collapse": "收起该行",
1304
+ "timeSlots.tooltip": "命中行的模型链成为 root 生效链(取代全时段链);未命中任何行时使用全时段链。分时切换是路由种子而非失败决策:不消耗冷却、不计入单步切换上限。",
1305
+ "timeSlots.addPreset": "添加预设",
1306
+ "timeSlots.addCustom": "添加自定义时段",
1307
+ "timeSlots.presetPlaceholder": "选择预设",
1308
+ "timeSlots.remove": "删除该时段行",
1309
+ "timeSlots.moveUp": "上移该时段行",
1310
+ "timeSlots.moveDown": "下移该时段行",
1311
+ "timeSlots.start": "开始(HH:mm)",
1312
+ "timeSlots.end": "结束(HH:mm)",
1313
+ "timeSlots.days": "星期",
1314
+ "timeSlots.days.hint": "不勾选 = 每天;可跨午夜",
1315
+ "timeSlots.preset.name": "预设",
1316
+ "timeSlots.preset.windowLabel": "时段(只读)",
1317
+ "timeSlots.preset.chainsOnly": "预设窗口已锁定:仅可编辑模型链",
1318
+ "timeSlots.preset.liang-peak.label": "梁文峰",
1319
+ "timeSlots.preset.liang-peak.window": "09:00–12:00 与 14:00–18:00(每天,UTC+8)",
1320
+ "timeSlots.preset.liang-valley.label": "梁文谷",
1321
+ "timeSlots.preset.liang-valley.window": "Liang Peak 之外的所有时间(每天,UTC+8)",
1322
+ "timeSlots.preset.glm-peak.label": "GLM峰",
1323
+ "timeSlots.preset.glm-peak.window": "周一至周五 14:00–18:00(UTC+8)",
1324
+ "timeSlots.preset.glm-valley.label": "GLM谷",
1325
+ "timeSlots.preset.glm-valley.window": "GLM Peak 之外的所有时间(UTC+8)",
1326
+ "timeSlots.preset.glm.note": "仅配置了 zai-coding-cn 时有效",
1327
+ "timeSlots.preset.highCost": "高消耗",
1328
+ "timeSlots.preset.multiplier": "x{n}",
1329
+ "timeSlots.active": "激活",
1330
+ "timeSlots.preset.glm.unconfigured": "(需配置 zai-coding-cn)",
1331
+ "timeSlots.day.sun": "日",
1332
+ "timeSlots.day.mon": "一",
1333
+ "timeSlots.day.tue": "二",
1334
+ "timeSlots.day.wed": "三",
1335
+ "timeSlots.day.thu": "四",
1336
+ "timeSlots.day.fri": "五",
1337
+ "timeSlots.day.sat": "六",
1338
+ "timeSlots.selector.add": "添加选择器",
1339
+ "allDay.hint": "全天链的最后一档兜底:官方 V4 Flash 或 V4 Pro 二选一",
1340
+ "allDay.flash": "官方 V4 Flash(deepseek-official/deepseek-v4-flash)",
1341
+ "allDay.pro": "官方 V4 Pro(deepseek-official/deepseek-v4-pro)",
1342
+ "allDay.nonconforming": "当前默认模型不合法:请选择官方 V4 Flash 或 V4 Pro 后保存",
830
1343
  "roles.list.label": "角色实体",
831
1344
  "roles.list.hint": "先声明角色,规则才能引用",
832
1345
  "roles.list.tooltip": "角色 id 须匹配 /^[a-z0-9-]{1,32}$/ 且唯一;\"inherit\" 为保留字,不能用作角色 id。",
@@ -844,14 +1357,12 @@ window.__ModuleLoader__.load({
844
1357
  "roles.fallback.none": "仅角色链(不追加 rootChain)",
845
1358
  "roles.add": "添加角色",
846
1359
  "roles.remove": "删除该角色",
1360
+ "roles.expand": "展开该角色",
1361
+ "roles.collapse": "收起该角色",
847
1362
  "roles.selector.add": "添加选择器",
848
1363
  "roles.rules": "角色规则",
849
- "roles.rules.hint": "顺序匹配 origin/provider/model,未命中 → inherit(root 链)",
850
- "roles.rules.tooltip": "规则命中后走对应角色的链;未命中走内置 inherit(rootChain)。",
851
- "roles.rule.origin": "来源",
852
- "roles.rule.origin.any": "任意",
853
- "roles.rule.origin.root": "root",
854
- "roles.rule.origin.subagent": "subagent",
1364
+ "roles.rules.hint": "仅对子代理生效:顺序匹配 provider/model,未命中 → inherit(root 链)",
1365
+ "roles.rules.tooltip": "规则仅对子代理生效(root 请求不匹配规则):命中后走对应角色的链;未命中走内置 inherit(rootChain)。",
855
1366
  "roles.rule.provider": "provider",
856
1367
  "roles.rule.provider.any": "任意",
857
1368
  "roles.rule.model": "model",
@@ -869,6 +1380,14 @@ window.__ModuleLoader__.load({
869
1380
  "validation.ruleRoleUndeclared": "规则引用了未声明的角色 \"{role}\"",
870
1381
  "validation.ruleRoleRequired": "规则未选择角色:请选择目标角色,或删除该行",
871
1382
  "validation.roleChainRequired": "角色 \"{id}\" 未配置模型:请至少添加一条链选择器(模型配置)",
1383
+ "validation.allDayRequired": "默认模型必须二选一:官方 V4 Flash 或 V4 Pro",
1384
+ "validation.slotChainRequired": "分时槽未配置模型:请至少添加一条链选择器",
1385
+ "validation.slotWindow": "分时槽开始/结束时间须为 HH:mm 格式",
1386
+ "validation.slotDays": "星期取值须为 0–6 的整数",
1387
+ "validation.slotKind": "分时槽 kind 须为 \"preset\" 或 \"custom\"",
1388
+ "validation.slotPresetUnknown": "未知的分时槽预设 \"{preset}\"",
1389
+ "validation.slotPresetDuplicate": "预设 \"{preset}\" 已存在:每个预设只能添加一行",
1390
+ "validation.slotPresetFrozen": "预设窗口是冻结代码常量:预设行不能携带 start/end/days",
872
1391
  "validation.selector": "选择器 \"{entry}\" 非法:{message}",
873
1392
  "legacy.banner": "检测到旧格式配置字段({keys}):已按新模型展示,请按 docs/configuration.md 迁移表手工改写;插件不会自动改写配置。",
874
1393
  "catalog.empty": "暂无可用模型:请先在模型页添加模型,添加后此处将自动可选。",
@@ -905,12 +1424,6 @@ window.__ModuleLoader__.load({
905
1424
  "save.saving": "保存中…",
906
1425
  "save.error": "保存失败:{message}",
907
1426
  "close": "关闭",
908
- "reset": "恢复默认",
909
- "reset.confirmTitle": "恢复默认配置",
910
- "reset.confirm": "恢复后 fallbacks 配置将回到插件默认值,当前编辑内容会丢失。",
911
- "reset.confirm.cancel": "取消",
912
- "reset.confirm.action": "恢复默认",
913
- "reset.saving": "恢复中…",
914
1427
  "loading": "加载中…",
915
1428
  "unavailable": "fallbacks 配置通道暂不可达:以下显示默认配置(或上次读取值),可尝试保存;保存失败会在此处如实提示。",
916
1429
  "error.generic": "出错:{message}"
@@ -956,16 +1469,62 @@ window.__ModuleLoader__.load({
956
1469
  "roleAutoMatch.label": "Enable role auto-match",
957
1470
  "roleAutoMatch.hint": "On rules-miss, the model picks the closest declared role",
958
1471
  "roleAutoMatch.tooltip": "When no rule matches, the model auto-selects the best-fit declared role (id + persona) and uses its chain; turn off to keep today's fallback (inherit / rootChain) on a rules-miss.",
959
- "rootChain.label": "Root agent fallback chain",
960
- "rootChain.firstLine": "Engages only after the current session's selected model fails — it never preempts the session model",
961
- "rootChain.hint": "Prefer the current session's model; degrade down this chain only after it fails",
962
- "rootChain.tooltip": "When the root agent fails it falls back down this ordered selector list; unset behaves like an uninstalled plugin.",
963
- "rootChain.selector.add": "Add selector",
1472
+ "rootChain.label": "Default fallback chain",
1473
+ "rootChain.tooltip": "Walked first whenever no time slot matches; if every entry fails, the default model below is the last fallback.",
1474
+ "defaultModel.label": "Default model",
964
1475
  "chains.selector.remove": "Remove this selector",
965
1476
  "chains.selector.providerPlaceholder": "Select provider",
966
1477
  "chains.selector.modelPlaceholder": "Select model",
967
1478
  "chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
968
1479
  "chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
1480
+ "mainAgent.label": "Main agent",
1481
+ "subagents.label": "Subagents",
1482
+ "timeSlots.label": "Time slots",
1483
+ "timeSlots.hint": "First match from top to bottom wins; the all-day row is always last",
1484
+ "timeSlots.tz.label": "Timezone (UTC±)",
1485
+ "timeSlots.drag": "Drag to reorder (or use the up/down buttons)",
1486
+ "timeSlots.name": "Name",
1487
+ "timeSlots.expand": "Expand this row",
1488
+ "timeSlots.collapse": "Collapse this row",
1489
+ "timeSlots.tooltip": "A matched row's model chain becomes the effective root chain (replacing the all-day chain); no match uses the all-day chain. A time-slot switch is a routing seed, not a failure decision: it consumes no cooldown and does not count against the per-step switch cap.",
1490
+ "timeSlots.addPreset": "Add preset",
1491
+ "timeSlots.addCustom": "Add custom time slot",
1492
+ "timeSlots.presetPlaceholder": "Select a preset",
1493
+ "timeSlots.remove": "Remove this time-slot row",
1494
+ "timeSlots.moveUp": "Move this time-slot row up",
1495
+ "timeSlots.moveDown": "Move this time-slot row down",
1496
+ "timeSlots.start": "Start (HH:mm)",
1497
+ "timeSlots.end": "End (HH:mm)",
1498
+ "timeSlots.days": "Days",
1499
+ "timeSlots.days.hint": "None selected = every day; may wrap midnight",
1500
+ "timeSlots.preset.name": "Preset",
1501
+ "timeSlots.preset.windowLabel": "Window (read-only)",
1502
+ "timeSlots.preset.chainsOnly": "Preset windows are frozen: only the model chain is editable",
1503
+ "timeSlots.preset.liang-peak.label": "Liang Peak",
1504
+ "timeSlots.preset.liang-peak.window": "09:00–12:00 & 14:00–18:00 (every day, UTC+8)",
1505
+ "timeSlots.preset.liang-valley.label": "Liang Valley",
1506
+ "timeSlots.preset.liang-valley.window": "All times outside Liang Peak (every day, UTC+8)",
1507
+ "timeSlots.preset.glm-peak.label": "GLM Peak",
1508
+ "timeSlots.preset.glm-peak.window": "Monday–Friday 14:00–18:00 (UTC+8)",
1509
+ "timeSlots.preset.glm-valley.label": "GLM Valley",
1510
+ "timeSlots.preset.glm-valley.window": "All times outside GLM Peak (UTC+8)",
1511
+ "timeSlots.preset.glm.note": "Only effective when zai-coding-cn is configured",
1512
+ "timeSlots.preset.highCost": "High Cost",
1513
+ "timeSlots.preset.multiplier": "x{n}",
1514
+ "timeSlots.active": "Active",
1515
+ "timeSlots.preset.glm.unconfigured": " (requires zai-coding-cn)",
1516
+ "timeSlots.day.sun": "Sun",
1517
+ "timeSlots.day.mon": "Mon",
1518
+ "timeSlots.day.tue": "Tue",
1519
+ "timeSlots.day.wed": "Wed",
1520
+ "timeSlots.day.thu": "Thu",
1521
+ "timeSlots.day.fri": "Fri",
1522
+ "timeSlots.day.sat": "Sat",
1523
+ "timeSlots.selector.add": "Add selector",
1524
+ "allDay.hint": "Last-resort fallback of the all-day chain: official V4 Flash or Pro (pick exactly one)",
1525
+ "allDay.flash": "Official V4 Flash (deepseek-official/deepseek-v4-flash)",
1526
+ "allDay.pro": "Official V4 Pro (deepseek-official/deepseek-v4-pro)",
1527
+ "allDay.nonconforming": "The current default model is not valid: pick official V4 Flash or Pro before saving",
969
1528
  "roles.list.label": "Declared roles",
970
1529
  "roles.list.hint": "Declare roles before rules can reference them",
971
1530
  "roles.list.tooltip": "Role ids must match /^[a-z0-9-]{1,32}$/ and be unique; \"inherit\" is reserved and cannot be used as a role id.",
@@ -983,14 +1542,12 @@ window.__ModuleLoader__.load({
983
1542
  "roles.fallback.none": "Role chain only (no rootChain)",
984
1543
  "roles.add": "Add role",
985
1544
  "roles.remove": "Remove this role",
1545
+ "roles.expand": "Expand this role",
1546
+ "roles.collapse": "Collapse this role",
986
1547
  "roles.selector.add": "Add selector",
987
1548
  "roles.rules": "Role rules",
988
- "roles.rules.hint": "Matches origin/provider/model in order; no match → inherit (root chain)",
989
- "roles.rules.tooltip": "A matched rule uses that role's chain; no match uses the built-in inherit (rootChain).",
990
- "roles.rule.origin": "Origin",
991
- "roles.rule.origin.any": "Any",
992
- "roles.rule.origin.root": "root",
993
- "roles.rule.origin.subagent": "subagent",
1549
+ "roles.rules.hint": "Subagents only: matches provider/model in order; no match → inherit (root chain)",
1550
+ "roles.rules.tooltip": "Rules apply to subagents only (root requests never match): a matched rule uses that role's chain; no match uses the built-in inherit (rootChain).",
994
1551
  "roles.rule.provider": "provider",
995
1552
  "roles.rule.provider.any": "Any",
996
1553
  "roles.rule.model": "model",
@@ -1008,6 +1565,14 @@ window.__ModuleLoader__.load({
1008
1565
  "validation.ruleRoleUndeclared": "Rule references undeclared role \"{role}\"",
1009
1566
  "validation.ruleRoleRequired": "Rule has no role selected: pick a target role, or remove the row",
1010
1567
  "validation.roleChainRequired": "Role \"{id}\" has no model config: add at least one chain entry",
1568
+ "validation.allDayRequired": "The default model must be exactly one official V4 model (V4 Flash or V4 Pro)",
1569
+ "validation.slotChainRequired": "Time-slot row has no models: add at least one chain entry",
1570
+ "validation.slotWindow": "Time-slot start/end must use HH:mm format",
1571
+ "validation.slotDays": "Days must be integers 0–6",
1572
+ "validation.slotKind": "Time-slot kind must be \"preset\" or \"custom\"",
1573
+ "validation.slotPresetUnknown": "Unknown time-slot preset \"{preset}\"",
1574
+ "validation.slotPresetDuplicate": "Preset \"{preset}\" already exists: at most one row per preset",
1575
+ "validation.slotPresetFrozen": "Preset windows are frozen code constants: a preset row cannot carry start/end/days",
1011
1576
  "validation.selector": "Invalid selector \"{entry}\": {message}",
1012
1577
  "legacy.banner": "Legacy config fields detected ({keys}): now shown in the new model — rewrite them manually following the migration table in docs/configuration.md (the plugin will not rewrite them automatically).",
1013
1578
  "catalog.empty": "No models yet: add a model on the Models page first; options will appear here automatically.",
@@ -1044,12 +1609,6 @@ window.__ModuleLoader__.load({
1044
1609
  "save.saving": "Saving…",
1045
1610
  "save.error": "Save failed: {message}",
1046
1611
  "close": "Close",
1047
- "reset": "Reset to defaults",
1048
- "reset.confirmTitle": "Reset to defaults",
1049
- "reset.confirm": "Resetting restores the fallbacks configuration to plugin defaults; your current edits will be lost.",
1050
- "reset.confirm.cancel": "Cancel",
1051
- "reset.confirm.action": "Reset",
1052
- "reset.saving": "Resetting…",
1053
1612
  "loading": "Loading…",
1054
1613
  "unavailable": "The fallbacks config channel is unreachable: showing the default configuration (or the last read value). You can try to save; failures will be reported here.",
1055
1614
  "error.generic": "Error: {message}"
@@ -1092,7 +1651,7 @@ window.__ModuleLoader__.load({
1092
1651
  }
1093
1652
  //#endregion
1094
1653
  //#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/FallbacksCard.module.css.mjs
1095
- const css$2 = "\n\n\n._8827595f_card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n\n._8827595f_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._06704203_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._e488d460_header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n\n._e488d460_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n\n._f5dfe084_headText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._8d39bde6_name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n\n._346f3b69_description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: rotate(180deg);\n}\n\n\n._dbaa7975_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\n}\n\n._4e39cf17_readOnly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._99bf7f3c_pending {\n flex: none;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n\n._114985b2_footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n\n\n._9860a5b1_notice {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n\n._5006d43e_legacyNotice {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n._21918751_error {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._7cea4b2f_noticeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 12px 0 0;\n}\n\n._7cea4b2f_noticeRow ._21918751_error {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n\n\n._4058c747_form {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 0 0;\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n._02c2f6df_checkLabel {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 14px;\n line-height: 22px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._f2d47237_checkLabelTitle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 400;\n}\n\n._c1bed8ea_checkLabelDesc {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._06389b18_checkbox {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n\n._67826267_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding: 0;\n border: none;\n min-width: 0;\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._b116c758_sectionToggle {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n cursor: pointer;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n._b116c758_sectionToggle:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n}\n\n._b116c758_sectionToggle:disabled {\n cursor: default;\n}\n\n._b116c758_sectionToggle:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n._a5313fdf_sectionToggleText {\n flex: 1;\n min-width: 0;\n text-align: left;\n}\n\n._4bc809b8_hint {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._579f813a_defaultNote {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._a6244318_infoHint {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n font-size: 14px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-interactive-bg-hover);\n cursor: help;\n user-select: none;\n}\n\n._a6244318_infoHint:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._e655c840_infoHintDisabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._a2521bb0_optionRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._a2521bb0_optionRow:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n\n._06389b18_checkbox,\n._a2521bb0_optionRow input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:focus-visible,\n._a2521bb0_optionRow input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a2521bb0_optionRow input:disabled {\n cursor: default;\n}\n\n\n._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 6px 10px;\n resize: vertical;\n line-height: 20px;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus {\n border-color: var(--dsw-alias-state-error-primary);\n}\n\n\nselect._f9d86f7b_input {\n max-width: 240px;\n cursor: pointer;\n}\n\n\n._16eb7153_selectInput {\n appearance: none;\n padding-right: 32px;\n \n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n\n\n._3e95886d_numberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n}\n\n._0cfb5881_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n\n._f5c620d6_editorCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px 16px;\n border-radius: 12px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._224b1387_cardFoot {\n display: flex;\n justify-content: flex-end;\n}\n\n._8e220e07_ruleGrid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n._24a12a2f_ruleCell {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 120px;\n flex: 1;\n}\n\n._1014c097_ruleCellLabel {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n._240cbbf8_selectorRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._70effa64_iconButton {\n position: relative;\n box-sizing: border-box;\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n\n._70effa64_iconButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._70effa64_iconButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._70effa64_iconButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._70effa64_iconButton::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity .12s;\n}\n\n._70effa64_iconButton:hover::after,\n._70effa64_iconButton:focus-visible::after {\n opacity: 1;\n}\n\n\n._a0968257_iconButtonDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._747fd56d_primaryButton,\n._7e855445_secondaryButton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: 18px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n._747fd56d_primaryButton {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n._747fd56d_primaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n._7e855445_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n\n._7e855445_secondaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n._747fd56d_primaryButton:disabled,\n._7e855445_secondaryButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._747fd56d_primaryButton:focus-visible,\n._7e855445_secondaryButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._fbd045e0_statusBlock {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 14px;\n margin-top: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n}\n\n._d4f70367_statusTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._8ab300ed_statusLine {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n._e8cedbf5_statusLineLabel {\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._b48dfcba_offNotice {\n margin: 0;\n padding: 12px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n\n._ec8bd970_resetDialog {\n width: min(480px, 100%);\n}\n\n._8677165c_confirmDanger:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-state-error-primary);\n}\n\n._8677165c_confirmDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after {\n transition: none;\n }\n}\n";
1654
+ const css$2 = "\n\n\n._8827595f_card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n\n._8827595f_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._06704203_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._e488d460_header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n\n._e488d460_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n\n._f5dfe084_headText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._8d39bde6_name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n\n._346f3b69_description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: rotate(180deg);\n}\n\n\n._dbaa7975_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\n}\n\n._4e39cf17_readOnly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._99bf7f3c_pending {\n flex: none;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n\n\n\n\n._9860a5b1_notice {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n\n._5006d43e_legacyNotice {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n._21918751_error {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._7cea4b2f_noticeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 12px 0 0;\n}\n\n._7cea4b2f_noticeRow ._21918751_error {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n\n\n._4058c747_form {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 0 0;\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n._02c2f6df_checkLabel {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 14px;\n line-height: 22px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._f2d47237_checkLabelTitle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 400;\n}\n\n._c1bed8ea_checkLabelDesc {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._06389b18_checkbox {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n\n._67826267_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding: 0;\n border: none;\n min-width: 0;\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._ba240d82_sectionHeading {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n._ddbc1b49_sectionHeadingText {\n flex: 1;\n min-width: 0;\n}\n\n\n._c41f9db7_sectionActions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n\n\n._b116c758_sectionToggle {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n cursor: pointer;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n._b116c758_sectionToggle:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n}\n\n._b116c758_sectionToggle:disabled {\n cursor: default;\n}\n\n._b116c758_sectionToggle:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n._a5313fdf_sectionToggleText {\n flex: 1;\n min-width: 0;\n text-align: left;\n}\n\n._4bc809b8_hint {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._579f813a_defaultNote {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._b1962759_slotPresetName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._a6244318_infoHint {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n font-size: 14px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-interactive-bg-hover);\n cursor: help;\n user-select: none;\n}\n\n._a6244318_infoHint:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._e655c840_infoHintDisabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._a2521bb0_optionRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._a2521bb0_optionRow:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n\n._06389b18_checkbox,\n._a2521bb0_optionRow input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:focus-visible,\n._a2521bb0_optionRow input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a2521bb0_optionRow input:disabled {\n cursor: default;\n}\n\n\n._c4ecbdf3_dayRow {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n._0330c1ed_dayCell {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n border-radius: 6px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n._0330c1ed_dayCell input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell input:disabled {\n cursor: default;\n}\n\n._0330c1ed_dayCell input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 6px 10px;\n resize: vertical;\n line-height: 20px;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus {\n border-color: var(--dsw-alias-state-error-primary);\n}\n\n\nselect._f9d86f7b_input {\n max-width: 240px;\n cursor: pointer;\n}\n\n\n._16eb7153_selectInput {\n appearance: none;\n padding-right: 32px;\n \n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n\n\n._3e95886d_numberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n}\n\n._0cfb5881_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n\n._f5c620d6_editorCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px 16px;\n border-radius: 12px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._224b1387_cardFoot {\n display: flex;\n justify-content: flex-end;\n}\n\n._8e220e07_ruleGrid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n._24a12a2f_ruleCell {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 120px;\n flex: 1;\n}\n\n\n._86bc52d2_collapseRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n._a73efcc6_collapseToggle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n font: inherit;\n text-align: left;\n}\n\n._a73efcc6_collapseToggle:disabled {\n cursor: default;\n}\n\n._af017718_collapseTitle {\n \n flex: none;\n max-width: 55%;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n._2cabe603_collapseMeta {\n \n flex: none;\n margin-left: auto;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n._7b04f727_slotTag {\n flex: none;\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n border: 1px solid;\n}\n\n._c381e792_slotTagHighCost {\n color: var(--dsw-alias-state-error-primary);\n border-color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n._23bdb6fc_slotTagMultiplier {\n color: var(--dsw-alias-state-warn-primary);\n border-color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent);\n}\n\n._f000eed9_slotTagActive {\n color: var(--dsw-alias-state-success-primary);\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);\n}\n\n\n._cd905ce5_dragHandle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: grab;\n color: var(--dsw-alias-label-tertiary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 6px;\n flex: none;\n}\n\n._cd905ce5_dragHandle:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._cd905ce5_dragHandle:active {\n cursor: grabbing;\n}\n\n._cd905ce5_dragHandle:disabled {\n cursor: default;\n opacity: 0.4;\n}\n\n._cd905ce5_dragHandle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._49f0c4f0_dragHandleIcon {\n transform: rotate(90deg);\n}\n\n\n._fee63b7e_slotCardDragging {\n opacity: 0.45;\n}\n\n._6f108491_slotCardOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n\n._1014c097_ruleCellLabel {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n._240cbbf8_selectorRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._70effa64_iconButton {\n position: relative;\n box-sizing: border-box;\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n\n._70effa64_iconButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._70effa64_iconButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._70effa64_iconButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._70effa64_iconButton::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity .12s;\n}\n\n._70effa64_iconButton:hover::after,\n._70effa64_iconButton:focus-visible::after {\n opacity: 1;\n}\n\n\n._a0968257_iconButtonDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._6a2bd54c_slotAddRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n\n._4e77523e_rowActions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n\n._747fd56d_primaryButton,\n._7e855445_secondaryButton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: 18px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n._747fd56d_primaryButton {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n._747fd56d_primaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n._7e855445_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n\n._7e855445_secondaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n._747fd56d_primaryButton:disabled,\n._7e855445_secondaryButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._747fd56d_primaryButton:focus-visible,\n._7e855445_secondaryButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._238d19be_sectionAction {\n height: 28px;\n padding: 0 12px;\n border-radius: 14px;\n font-size: 12px;\n line-height: 18px;\n}\n\n\n._fbd045e0_statusBlock {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 14px;\n margin-top: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n}\n\n._d4f70367_statusTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._8ab300ed_statusLine {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n._e8cedbf5_statusLineLabel {\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._b48dfcba_offNotice {\n margin: 0;\n padding: 12px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after {\n transition: none;\n }\n}\n";
1096
1655
  const tagId$2 = "dsh-llm-fallbacks/FallbacksCard.module.css";
1097
1656
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1098
1657
  const tag = document.createElement("style");
@@ -1113,7 +1672,6 @@ window.__ModuleLoader__.load({
1113
1672
  "body": "_dbaa7975_body",
1114
1673
  "readOnly": "_4e39cf17_readOnly",
1115
1674
  "pending": "_99bf7f3c_pending",
1116
- "footer": "_114985b2_footer",
1117
1675
  "notice": "_9860a5b1_notice",
1118
1676
  "legacyNotice": "_5006d43e_legacyNotice",
1119
1677
  "error": "_21918751_error",
@@ -1127,13 +1685,19 @@ window.__ModuleLoader__.load({
1127
1685
  "fieldset": "_94c91bfd_fieldset",
1128
1686
  "field": "_67826267_field",
1129
1687
  "fieldLabel": "_13e68c3f_fieldLabel",
1688
+ "sectionHeading": "_ba240d82_sectionHeading",
1689
+ "sectionHeadingText": "_ddbc1b49_sectionHeadingText",
1690
+ "sectionActions": "_c41f9db7_sectionActions",
1130
1691
  "sectionToggle": "_b116c758_sectionToggle",
1131
1692
  "sectionToggleText": "_a5313fdf_sectionToggleText",
1132
1693
  "hint": "_4bc809b8_hint",
1133
1694
  "defaultNote": "_579f813a_defaultNote",
1695
+ "slotPresetName": "_b1962759_slotPresetName",
1134
1696
  "infoHint": "_a6244318_infoHint",
1135
1697
  "infoHintDisabled": "_e655c840_infoHintDisabled",
1136
1698
  "optionRow": "_a2521bb0_optionRow",
1699
+ "dayRow": "_c4ecbdf3_dayRow",
1700
+ "dayCell": "_0330c1ed_dayCell",
1137
1701
  "input": "_f9d86f7b_input",
1138
1702
  "inputTextarea": "_3a9b42fd_inputTextarea",
1139
1703
  "inputInvalid": "_7b60337a_inputInvalid",
@@ -1144,21 +1708,34 @@ window.__ModuleLoader__.load({
1144
1708
  "cardFoot": "_224b1387_cardFoot",
1145
1709
  "ruleGrid": "_8e220e07_ruleGrid",
1146
1710
  "ruleCell": "_24a12a2f_ruleCell",
1711
+ "collapseRow": "_86bc52d2_collapseRow",
1712
+ "collapseToggle": "_a73efcc6_collapseToggle",
1713
+ "collapseTitle": "_af017718_collapseTitle",
1714
+ "collapseMeta": "_2cabe603_collapseMeta",
1715
+ "slotTag": "_7b04f727_slotTag",
1716
+ "slotTagHighCost": "_c381e792_slotTagHighCost",
1717
+ "slotTagMultiplier": "_23bdb6fc_slotTagMultiplier",
1718
+ "slotTagActive": "_f000eed9_slotTagActive",
1719
+ "dragHandle": "_cd905ce5_dragHandle",
1720
+ "dragHandleIcon": "_49f0c4f0_dragHandleIcon",
1721
+ "slotCardDragging": "_fee63b7e_slotCardDragging",
1722
+ "slotCardOver": "_6f108491_slotCardOver",
1147
1723
  "ruleCellLabel": "_1014c097_ruleCellLabel",
1148
1724
  "chainSelectors": "_15a12620_chainSelectors",
1149
1725
  "selectorRow": "_240cbbf8_selectorRow",
1150
1726
  "iconButton": "_70effa64_iconButton",
1151
1727
  "iconButtonDanger": "_a0968257_iconButtonDanger",
1152
1728
  "addButton": "_28202c90_addButton",
1729
+ "slotAddRow": "_6a2bd54c_slotAddRow",
1730
+ "rowActions": "_4e77523e_rowActions",
1153
1731
  "primaryButton": "_747fd56d_primaryButton",
1154
1732
  "secondaryButton": "_7e855445_secondaryButton",
1733
+ "sectionAction": "_238d19be_sectionAction",
1155
1734
  "statusBlock": "_fbd045e0_statusBlock",
1156
1735
  "statusTitle": "_d4f70367_statusTitle",
1157
1736
  "statusLine": "_8ab300ed_statusLine",
1158
1737
  "statusLineLabel": "_e8cedbf5_statusLineLabel",
1159
- "offNotice": "_b48dfcba_offNotice",
1160
- "resetDialog": "_ec8bd970_resetDialog",
1161
- "confirmDanger": "_8677165c_confirmDanger"
1738
+ "offNotice": "_b48dfcba_offNotice"
1162
1739
  };
1163
1740
  //#endregion
1164
1741
  //#region src/client/FallbacksCard.tsx
@@ -1176,12 +1753,22 @@ window.__ModuleLoader__.load({
1176
1753
  * description, with a dirty "unsaved" pill and a rotating chevron
1177
1754
  * (`IconChevronDownOutline14` from ui-primitives — a CLIENT_EXTERNALS value
1178
1755
  * import), `aria-expanded`/`aria-label` like the upstream header; a divider
1179
- * under the header; then the form content; then a footer with
1180
- * Discard / Reset / Save carrying the upstream disabled semantics — save =
1181
- * `!dirty || saving || !writable`, discard = `!dirty || saving` (KD-U1).
1182
- * Disclosure is card-local state: which card a user has open is a reading
1183
- * gesture, and staged edits outlive collapsing — the pill rides the header
1184
- * (upstream rationale).
1756
+ * under the header; then the form content. PR #62 UX round 2: the card
1757
+ * footer is gone — each big section (主代理 / 子代理 / 高级选项) carries its
1758
+ * own Save/Discard actions beside its heading (高级选项: inside the expanded
1759
+ * body) and its own validation / save-error surface. PR #62 UX round 3:
1760
+ * each section's Save writes ONLY that section's fields — 主代理 owns
1761
+ * rootChain / timeSlots / tz (+ the card-level `enabled`), 子代理 owns
1762
+ * roles, 高级选项 owns the advanced scalars; the patch spreads the last
1763
+ * ACCEPTED config for every other section, so a 主代理 Save can never
1764
+ * ride along an unsaved 子代理 edit (and vice versa) — and validation /
1765
+ * the dirty gate apply per section too (a bad role id never blocks 主代理,
1766
+ * and only the saved section's Discard reverts that section's edits).
1767
+ * Save/discard disabled terms: save = `!sectionDirty || saving ||
1768
+ * !writable`, discard = `!sectionDirty || saving` (KD-U1). Disclosure is
1769
+ * card-local state:
1770
+ * which card a user has open is a reading gesture, and staged edits outlive
1771
+ * collapsing — the pill rides the header (upstream rationale).
1185
1772
  *
1186
1773
  * The form body is the two-block editing surface (spec §8): the `enabled`
1187
1774
  * checkbox row, the 6 top-level scalar fields (trigger codes / revert
@@ -1197,13 +1784,14 @@ window.__ModuleLoader__.load({
1197
1784
  * non-empty `state.legacyKeys` renders the migration banner at the top of
1198
1785
  * the card body. The row editors keep their filled editorCard surface
1199
1786
  * inside the card, with `--dsw-alias-*` tokens throughout. The reset-
1200
- * to-defaults confirmation stays a `Modal` (the delete-confirm pattern of
1201
- * the Models page) — no `window.confirm`.
1787
+ * to-defaults affordance is GONE from the card (PR #62 UX round 3) — the
1788
+ * gateway RPC `fallbacks/reset` and the store `resetToDefaults()` stay as
1789
+ * host APIs (store/gateway tests unchanged), only the card UI was removed.
1202
1790
  *
1203
1791
  * The page-only chrome is gone (720px column wrapper, title/intro banners,
1204
1792
  * page-bottom status block): the AC-7 read-only status (derived effective
1205
- * model + recent-switch summary) is folded into the card body above the
1206
- * footer, and the plugin-config section owns the column width.
1793
+ * model + recent-switch summary) is folded into the card body, and the
1794
+ * plugin-config section owns the column width.
1207
1795
  *
1208
1796
  * Degraded/error/loading states keep the same card chrome (KD-U3): the
1209
1797
  * header always renders title+description+chevron, and the body carries the
@@ -1217,8 +1805,11 @@ window.__ModuleLoader__.load({
1217
1805
  * 'error'`) also forces the body open with an error notice and — when the
1218
1806
  * form is inert (`!writable`, i.e. the load never landed) — a Retry button;
1219
1807
  * a save failure keeps the editable form so the Save action itself is the
1220
- * retry (the single `state.error` surface covers both, unlike the advisor's
1221
- * separate apply-failure hints).
1808
+ * retry. PR #62 UX round 2: the single `state.error` surface is split by
1809
+ * origin — a LOAD failure keeps the card-top notice (with Retry when
1810
+ * inert), while a WRITE failure renders under the section whose Save was
1811
+ * last clicked (`lastSaveSection`), unlike the advisor's separate
1812
+ * apply-failure hints.
1222
1813
  *
1223
1814
  * The degraded derivation is latched in the card (the store stays untouched):
1224
1815
  * `present` only ever changes inside the store's `accept()`, so the settled
@@ -1228,6 +1819,77 @@ window.__ModuleLoader__.load({
1228
1819
  * implemented without a store change); on a first mount the latch is false,
1229
1820
  * so the healthy card starts (and stays) collapsed through its first load.
1230
1821
  */
1822
+ const ALL_DAY_FLASH = "deepseek-official/deepseek-v4-flash";
1823
+ const ALL_DAY_PRO = "deepseek-official/deepseek-v4-pro";
1824
+ const SLOT_PRESET_IDS = [
1825
+ "liang-peak",
1826
+ "liang-valley",
1827
+ "glm-peak",
1828
+ "glm-valley"
1829
+ ];
1830
+ /** Custom-row day toggle order (index = weekday, 0=Sunday); display copy lives in the dictionaries. */
1831
+ const SLOT_WEEKDAYS = [
1832
+ "sun",
1833
+ "mon",
1834
+ "tue",
1835
+ "wed",
1836
+ "thu",
1837
+ "fri",
1838
+ "sat"
1839
+ ];
1840
+ /** IANA timezone of this renderer (browser / host). */
1841
+ function hostTimeZone() {
1842
+ try {
1843
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
1844
+ return typeof tz === "string" && tz !== "" ? tz : "UTC";
1845
+ } catch {
1846
+ return "UTC";
1847
+ }
1848
+ }
1849
+ /** `UTC+8` / `UTC-4` for an IANA id (current offset, DST-honest). */
1850
+ function tzUtcOffset(tz) {
1851
+ try {
1852
+ const name = new Intl.DateTimeFormat("en-US", {
1853
+ timeZone: tz,
1854
+ timeZoneName: "shortOffset"
1855
+ }).formatToParts(/* @__PURE__ */ new Date()).find((part) => part.type === "timeZoneName")?.value;
1856
+ if (name === void 0 || name === "") return "";
1857
+ return name.replace(/^GMT/, "UTC");
1858
+ } catch {
1859
+ return "";
1860
+ }
1861
+ }
1862
+ /** Read-only custom-row copy: `Asia/Shanghai (UTC+8)`. */
1863
+ function tzDisplayLabel(tz) {
1864
+ const offset = tzUtcOffset(tz);
1865
+ return offset === "" ? tz : `${tz} (${offset})`;
1866
+ }
1867
+ /** Persist tz: presets lock UTC+8; custom-only uses the host zone; else keep the accepted value. */
1868
+ function resolvedSlotTz(rows, fallback) {
1869
+ if (rows.some((row) => row.kind === "preset")) return "Asia/Shanghai";
1870
+ if (rows.some((row) => row.kind === "custom")) return hostTimeZone();
1871
+ return fallback === "" ? "Asia/Shanghai" : fallback;
1872
+ }
1873
+ /** Strict 24h `HH:mm` — the resolver's HHMM_RE twin (drift-guarded by the gateway reject-on-save). */
1874
+ const HHMM_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
1875
+ /**
1876
+ * The 默认模型 value for a chain: the official V4 id when the chain TAIL
1877
+ * is that model (Flash XOR Pro — leading 默认降级链 entries allowed);
1878
+ * `''` for an empty chain or a chain whose last entry is not official
1879
+ * (the panel reads back unselected and save validation blocks the value).
1880
+ */
1881
+ function allDayModelOf(chain) {
1882
+ const tail = chain.length >= 1 ? chain[chain.length - 1] : void 0;
1883
+ return tail === ALL_DAY_FLASH || tail === ALL_DAY_PRO ? tail : "";
1884
+ }
1885
+ /**
1886
+ * The 默认降级链 editor row: the leading entries BEFORE the official-V4
1887
+ * tail, or the whole chain while the tail is not official (the draft
1888
+ * rides the accepted value until a 默认模型 pick).
1889
+ */
1890
+ function allDayChainRowOf(chain, catalog) {
1891
+ return rootChainToRows(allDayModelOf(chain) === "" ? chain : chain.slice(0, -1), catalog)[0];
1892
+ }
1231
1893
  /** Split scalars from the row editors (rootChain / role entities / role rules). */
1232
1894
  function scalarsOf(config) {
1233
1895
  return {
@@ -1237,7 +1899,8 @@ window.__ModuleLoader__.load({
1237
1899
  revertPolicy: config.revertPolicy,
1238
1900
  maxSwitchesPerStep: config.maxSwitchesPerStep,
1239
1901
  alwaysModeRetryCap: config.alwaysModeRetryCap,
1240
- roleAutoMatch: config.roleAutoMatch
1902
+ roleAutoMatch: config.roleAutoMatch,
1903
+ tz: config.tz ?? "Asia/Shanghai"
1241
1904
  };
1242
1905
  }
1243
1906
  /**
@@ -1255,13 +1918,22 @@ window.__ModuleLoader__.load({
1255
1918
  * `true` even for a legacy config that never declared the key — so the
1256
1919
  * toggle always renders (default on) and a save persists the resolved value
1257
1920
  * (AC-7 re-scope, PM decision 2026-08-17 Option A).
1921
+ *
1922
+ * All-day: rootChain is composed from the 默认降级链 editor's leading
1923
+ * selectors plus the 默认模型 tail (exactly one official V4 — Flash XOR
1924
+ * Pro). While no tail is selected the ACCEPTED chain rides through
1925
+ * untouched. `timeSlots` is rebuilt from the slot rows every render. `tz`
1926
+ * is a card scalar: preset rows lock it to Asia/Shanghai; custom rows
1927
+ * follow the selected timezone.
1258
1928
  */
1259
- function assembleConfig(scalars, rootChainRows, roleRows, ruleRows, originalRoles, presets, roleAutoMatch) {
1929
+ function assembleConfig(scalars, allDayModel, acceptedRootChain, allDayChainRow, roleRows, ruleRows, originalRoles, presets, roleAutoMatch, timeSlotRows) {
1260
1930
  const list = mergeRoleExtras(roleRows, originalRoles);
1931
+ const trailingChain = rowsToRootChain([allDayChainRow]);
1932
+ const tz = resolvedSlotTz(timeSlotRows, scalars.tz);
1261
1933
  return {
1262
1934
  enabled: scalars.enabled,
1263
1935
  triggerCodes: [...scalars.triggerCodes],
1264
- rootChain: rowsToRootChain(rootChainRows),
1936
+ rootChain: allDayModel === "" ? [...acceptedRootChain] : [...trailingChain, allDayModel],
1265
1937
  roles: {
1266
1938
  list,
1267
1939
  rules: rowsToRules(ruleRows)
@@ -1271,7 +1943,17 @@ window.__ModuleLoader__.load({
1271
1943
  maxSwitchesPerStep: scalars.maxSwitchesPerStep,
1272
1944
  alwaysModeRetryCap: scalars.alwaysModeRetryCap,
1273
1945
  ...presets === void 0 ? {} : { presets },
1274
- roleAutoMatch
1946
+ roleAutoMatch,
1947
+ timeSlots: rowsToTimeSlots(timeSlotRows),
1948
+ tz
1949
+ };
1950
+ }
1951
+ /** An empty per-section validation-error record (the clean-draft shape). */
1952
+ function emptyValidationErrors() {
1953
+ return {
1954
+ main: [],
1955
+ sub: [],
1956
+ advanced: []
1275
1957
  };
1276
1958
  }
1277
1959
  /**
@@ -1279,9 +1961,14 @@ window.__ModuleLoader__.load({
1279
1961
  * role id format/reserved word/duplicates, undeclared rule role references
1280
1962
  * (only reachable through the synthetic outside option — the dropdown
1281
1963
  * itself constrains normal edits), and illegal selector entries in
1282
- * rootChain and role chains. Returns one localized message per violation;
1283
- * a non-empty result blocks {@link save} — the draft is never written.
1284
- * `persona` is free text and never validated.
1964
+ * rootChain and role chains. Returns one localized message per violation,
1965
+ * bucketed by the section that owns the offending field (PR #62 UX round
1966
+ * 2 — 主代理: allDay / timeSlots / slot* / tz / default model / default
1967
+ * chain; 子代理: role* / rule*; 高级选项: trigger / cooldown / revert /
1968
+ * always / roleAutoMatch — the scalars are never validated, so the
1969
+ * advanced bucket stays empty today). A non-empty result blocks
1970
+ * {@link save} — the draft is never written. `persona` is free text and
1971
+ * never validated.
1285
1972
  *
1286
1973
  * `seededIds` is the live trimmed-id → overridden map derived from
1287
1974
  * `state.seeds` (spec §9.4): the empty-chain block relaxes for seeded ids
@@ -1290,33 +1977,57 @@ window.__ModuleLoader__.load({
1290
1977
  * behavior is byte-identical.
1291
1978
  */
1292
1979
  function validateDraft(draft, t, seededIds) {
1293
- const errors = [];
1980
+ const errors = emptyValidationErrors();
1294
1981
  const declaredIds = /* @__PURE__ */ new Set();
1295
1982
  for (const role of draft.roles.list) {
1296
- if (!ROLE_ID_PATTERN.test(role.id)) errors.push(t("validation.roleIdFormat", { id: role.id }));
1297
- if (role.id === "inherit") errors.push(t("validation.roleIdReserved"));
1298
- if (declaredIds.has(role.id)) errors.push(t("validation.roleIdDuplicate", { id: role.id }));
1983
+ if (!ROLE_ID_PATTERN.test(role.id)) errors.sub.push(t("validation.roleIdFormat", { id: role.id }));
1984
+ if (role.id === "inherit") errors.sub.push(t("validation.roleIdReserved"));
1985
+ if (declaredIds.has(role.id)) errors.sub.push(t("validation.roleIdDuplicate", { id: role.id }));
1299
1986
  declaredIds.add(role.id);
1300
1987
  for (const entry of role.chain ?? []) try {
1301
1988
  parseSelector(entry);
1302
1989
  } catch (error) {
1303
- errors.push(t("validation.selector", {
1990
+ errors.sub.push(t("validation.selector", {
1304
1991
  entry,
1305
1992
  message: error.message
1306
1993
  }));
1307
1994
  }
1308
- if ((role.chain ?? []).length === 0 && !seededIds.has(role.id.trim())) errors.push(t("validation.roleChainRequired", { id: role.id }));
1995
+ if ((role.chain ?? []).length === 0 && !seededIds.has(role.id.trim())) errors.sub.push(t("validation.roleChainRequired", { id: role.id }));
1309
1996
  }
1997
+ const allDayTail = draft.rootChain.length >= 1 ? draft.rootChain[draft.rootChain.length - 1] : void 0;
1998
+ if (allDayTail !== ALL_DAY_FLASH && allDayTail !== ALL_DAY_PRO) errors.main.push(t("validation.allDayRequired"));
1310
1999
  for (const entry of draft.rootChain) try {
1311
2000
  parseSelector(entry);
1312
2001
  } catch (error) {
1313
- errors.push(t("validation.selector", {
2002
+ errors.main.push(t("validation.selector", {
1314
2003
  entry,
1315
2004
  message: error.message
1316
2005
  }));
1317
2006
  }
2007
+ const seenSlotPresets = /* @__PURE__ */ new Set();
2008
+ for (const row of draft.timeSlots ?? []) {
2009
+ if (row.kind !== "preset" && row.kind !== "custom") errors.main.push(t("validation.slotKind"));
2010
+ if (row.kind === "preset") {
2011
+ if (typeof row.preset !== "string" || !SLOT_PRESET_IDS.includes(row.preset)) errors.main.push(t("validation.slotPresetUnknown", { preset: row.preset }));
2012
+ else if (seenSlotPresets.has(row.preset)) errors.main.push(t("validation.slotPresetDuplicate", { preset: row.preset }));
2013
+ else seenSlotPresets.add(row.preset);
2014
+ if (row.start !== void 0 || row.end !== void 0 || row.days !== void 0 && row.days.length > 0) errors.main.push(t("validation.slotPresetFrozen"));
2015
+ } else if (row.kind === "custom") {
2016
+ if (typeof row.start !== "string" || typeof row.end !== "string" || !HHMM_RE.test(row.start) || !HHMM_RE.test(row.end)) errors.main.push(t("validation.slotWindow"));
2017
+ if (row.days !== void 0 && row.days.some((day) => !Number.isInteger(day) || day < 0 || day > 6)) errors.main.push(t("validation.slotDays"));
2018
+ }
2019
+ for (const entry of row.chain) try {
2020
+ parseSelector(entry);
2021
+ } catch (error) {
2022
+ errors.main.push(t("validation.selector", {
2023
+ entry,
2024
+ message: error.message
2025
+ }));
2026
+ }
2027
+ if (row.chain.length === 0) errors.main.push(t("validation.slotChainRequired"));
2028
+ }
1318
2029
  const validTargets = /* @__PURE__ */ new Set([...declaredIds, INHERIT_ROLE_ID]);
1319
- for (const rule of draft.roles.rules) if (!validTargets.has(rule.role)) errors.push(t("validation.ruleRoleUndeclared", { role: rule.role }));
2030
+ for (const rule of draft.roles.rules) if (!validTargets.has(rule.role)) errors.sub.push(t("validation.ruleRoleUndeclared", { role: rule.role }));
1320
2031
  return errors;
1321
2032
  }
1322
2033
  /**
@@ -1345,6 +2056,20 @@ window.__ModuleLoader__.load({
1345
2056
  const parsed = Number.parseInt(raw, 10);
1346
2057
  return Number.isNaN(parsed) ? 0 : Math.max(0, parsed);
1347
2058
  }
2059
+ /**
2060
+ * Custom time-slot rows whose window is not valid `HH:mm` — drives the
2061
+ * inline red border after a blocked save attempt (same derivation pattern
2062
+ * as {@link collectInvalidRoleIds}: one pass per render, index lookup per
2063
+ * row).
2064
+ */
2065
+ function collectInvalidSlotRows(rows) {
2066
+ const invalid = /* @__PURE__ */ new Set();
2067
+ rows.forEach((row, index) => {
2068
+ if (row.kind === "preset") return;
2069
+ if (!HHMM_RE.test(row.start) || !HHMM_RE.test(row.end)) invalid.add(index);
2070
+ });
2071
+ return invalid;
2072
+ }
1348
2073
  /** The catalog faces the dropdowns classify against; undefined while unready. */
1349
2074
  function catalogOf(state) {
1350
2075
  return state.catalogStatus === "ready" ? {
@@ -1533,24 +2258,79 @@ window.__ModuleLoader__.load({
1533
2258
  if (snapshot.switchesStatus === "idle") controller.loadSwitches();
1534
2259
  }, [controller]);
1535
2260
  const [scalars, setScalars] = (0, react.useState)(() => scalarsOf(defaultFallbacksConfig));
1536
- const [rootChainRows, setRootChainRows] = (0, react.useState)(() => rootChainToRows(defaultFallbacksConfig.rootChain));
2261
+ const [allDayModel, setAllDayModel] = (0, react.useState)(() => allDayModelOf(defaultFallbacksConfig.rootChain));
2262
+ const [allDayChainRow, setAllDayChainRow] = (0, react.useState)(() => allDayChainRowOf(defaultFallbacksConfig.rootChain, void 0));
2263
+ const [timeSlotRows, setTimeSlotRows] = (0, react.useState)(() => timeSlotsToRows(defaultFallbacksConfig.timeSlots ?? []));
1537
2264
  const [roleRows, setRoleRows] = (0, react.useState)(() => rolesToRows(defaultFallbacksConfig.roles.list));
1538
2265
  const [ruleRows, setRuleRows] = (0, react.useState)(() => rulesToRows(defaultFallbacksConfig.roles.rules));
1539
- const [validationErrors, setValidationErrors] = (0, react.useState)([]);
2266
+ const [presetToAdd, setPresetToAdd] = (0, react.useState)("");
2267
+ const [validationErrors, setValidationErrors] = (0, react.useState)(emptyValidationErrors);
1540
2268
  const [validationAttempted, setValidationAttempted] = (0, react.useState)(false);
2269
+ const [lastSaveSection, setLastSaveSection] = (0, react.useState)(null);
1541
2270
  const seededConfigKey = (0, react.useRef)(null);
2271
+ const draft = assembleConfig(scalars, allDayModel, state.config.rootChain, allDayChainRow, roleRows, ruleRows, state.config.roles.list, state.config.presets, scalars.roleAutoMatch, timeSlotRows);
2272
+ const hasEmptyRuleRows = ruleRows.some((row) => row.role === "");
2273
+ const enabledDirty = scalars.enabled !== state.config.enabled;
2274
+ const mainDirty = enabledDirty || JSON.stringify([...draft.rootChain, draft.timeSlots]) !== JSON.stringify([...state.config.rootChain, state.config.timeSlots ?? []]);
2275
+ const subDirty = hasEmptyRuleRows || JSON.stringify(draft.roles) !== JSON.stringify(state.config.roles);
2276
+ const advancedDirty = JSON.stringify([
2277
+ draft.triggerCodes,
2278
+ draft.cooldownMs,
2279
+ draft.revertPolicy,
2280
+ draft.maxSwitchesPerStep,
2281
+ draft.alwaysModeRetryCap,
2282
+ draft.roleAutoMatch
2283
+ ]) !== JSON.stringify([
2284
+ state.config.triggerCodes,
2285
+ state.config.cooldownMs,
2286
+ state.config.revertPolicy,
2287
+ state.config.maxSwitchesPerStep,
2288
+ state.config.alwaysModeRetryCap,
2289
+ state.config.roleAutoMatch
2290
+ ]);
2291
+ const dirty = mainDirty || subDirty || advancedDirty;
2292
+ const firstSeedDone = (0, react.useRef)(false);
2293
+ const forceReseed = (0, react.useRef)(false);
1542
2294
  (0, react.useEffect)(() => {
1543
2295
  if (state.status !== "ready") return;
1544
2296
  const key = JSON.stringify(state.config);
1545
2297
  if (seededConfigKey.current === key) return;
1546
2298
  seededConfigKey.current = key;
1547
- setScalars(scalarsOf(state.config));
1548
- setRootChainRows(rootChainToRows(state.config.rootChain, catalogOf(state)));
1549
- setRoleRows(rolesToRows(state.config.roles.list, catalogOf(state)));
1550
- setRuleRows(rulesToRows(state.config.roles.rules, catalogOf(state)));
1551
- }, [state.status, state.config]);
1552
- const [confirmingReset, setConfirmingReset] = (0, react.useState)(false);
1553
- const [resetting, setResetting] = (0, react.useState)(false);
2299
+ const firstSeed = !firstSeedDone.current;
2300
+ firstSeedDone.current = true;
2301
+ const force = forceReseed.current;
2302
+ forceReseed.current = false;
2303
+ const catalog = catalogOf(state);
2304
+ if (firstSeed || force || !mainDirty) {
2305
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2306
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
2307
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
2308
+ setScalars((prev) => ({
2309
+ ...prev,
2310
+ enabled: state.config.enabled,
2311
+ tz: state.config.tz ?? "Asia/Shanghai"
2312
+ }));
2313
+ }
2314
+ if (firstSeed || force || !subDirty) {
2315
+ setRoleRows(rolesToRows(state.config.roles.list, catalog));
2316
+ setRuleRows(rulesToRows(state.config.roles.rules, catalog));
2317
+ }
2318
+ if (firstSeed || force || !advancedDirty) setScalars((prev) => ({
2319
+ ...prev,
2320
+ triggerCodes: [...state.config.triggerCodes],
2321
+ cooldownMs: state.config.cooldownMs,
2322
+ revertPolicy: state.config.revertPolicy,
2323
+ maxSwitchesPerStep: state.config.maxSwitchesPerStep,
2324
+ alwaysModeRetryCap: state.config.alwaysModeRetryCap,
2325
+ roleAutoMatch: state.config.roleAutoMatch
2326
+ }));
2327
+ }, [
2328
+ state.status,
2329
+ state.config,
2330
+ mainDirty,
2331
+ subDirty,
2332
+ advancedDirty
2333
+ ]);
1554
2334
  const updateScalars = (mutator) => {
1555
2335
  setScalars((prev) => {
1556
2336
  const next = {
@@ -1561,17 +2341,32 @@ window.__ModuleLoader__.load({
1561
2341
  return next;
1562
2342
  });
1563
2343
  };
1564
- const updateRootChainSelector = (selectorIndex, patch) => {
1565
- setRootChainRows((rows) => rows.map((row, index) => index === 0 ? {
1566
- ...row,
1567
- selectors: row.selectors.map((selector, sIndex) => sIndex === selectorIndex ? {
1568
- ...selector,
2344
+ const updateTimeSlotRow = (index, patch) => {
2345
+ setTimeSlotRows((rows) => {
2346
+ const next = rows.map((row) => ({ ...row }));
2347
+ next[index] = {
2348
+ ...next[index],
1569
2349
  ...patch
1570
- } : selector)
1571
- } : row));
2350
+ };
2351
+ return next;
2352
+ });
2353
+ };
2354
+ const updateTimeSlotSelector = (rowIndex, selectorIndex, patch) => {
2355
+ setTimeSlotRows((rows) => {
2356
+ const next = rows.map((row) => ({
2357
+ ...row,
2358
+ selectors: row.selectors.map((selector) => ({ ...selector }))
2359
+ }));
2360
+ const selectors = next[rowIndex].selectors;
2361
+ selectors[selectorIndex] = {
2362
+ ...selectors[selectorIndex],
2363
+ ...patch
2364
+ };
2365
+ return next;
2366
+ });
1572
2367
  };
1573
- const addRootChainSelector = () => {
1574
- setRootChainRows((rows) => rows.map((row, index) => index === 0 ? {
2368
+ const addTimeSlotSelector = (rowIndex) => {
2369
+ setTimeSlotRows((rows) => rows.map((row, index) => index === rowIndex ? {
1575
2370
  ...row,
1576
2371
  selectors: [...row.selectors, {
1577
2372
  wildcard: false,
@@ -1580,12 +2375,88 @@ window.__ModuleLoader__.load({
1580
2375
  }]
1581
2376
  } : row));
1582
2377
  };
1583
- const removeRootChainSelector = (selectorIndex) => {
1584
- setRootChainRows((rows) => rows.map((row, index) => index === 0 ? {
2378
+ const removeTimeSlotSelector = (rowIndex, selectorIndex) => {
2379
+ setTimeSlotRows((rows) => rows.map((row, index) => index === rowIndex ? {
1585
2380
  ...row,
1586
2381
  selectors: row.selectors.filter((_, sIndex) => sIndex !== selectorIndex)
1587
2382
  } : row));
1588
2383
  };
2384
+ const addPresetSlotRow = () => {
2385
+ if (presetToAdd === "") return;
2386
+ if ((presetToAdd === "glm-peak" || presetToAdd === "glm-valley") && !glmConfigured) return;
2387
+ setTimeSlotRows((rows) => [...rows, {
2388
+ kind: "preset",
2389
+ preset: presetToAdd,
2390
+ start: "",
2391
+ end: "",
2392
+ days: [],
2393
+ name: "",
2394
+ collapsed: false,
2395
+ selectors: []
2396
+ }]);
2397
+ setPresetToAdd("");
2398
+ };
2399
+ const addCustomSlotRow = () => {
2400
+ setTimeSlotRows((rows) => [...rows, {
2401
+ kind: "custom",
2402
+ start: "",
2403
+ end: "",
2404
+ days: [],
2405
+ name: "",
2406
+ collapsed: false,
2407
+ selectors: []
2408
+ }]);
2409
+ };
2410
+ const removeTimeSlotRow = (index) => {
2411
+ setTimeSlotRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
2412
+ };
2413
+ const moveTimeSlotRow = (index, delta) => {
2414
+ setTimeSlotRows((rows) => {
2415
+ const target = index + delta;
2416
+ if (target < 0 || target >= rows.length) return rows;
2417
+ const next = rows.map((row) => ({ ...row }));
2418
+ const moved = next[index];
2419
+ next[index] = next[target];
2420
+ next[target] = moved;
2421
+ return next;
2422
+ });
2423
+ };
2424
+ const [draggedSlotIndex, setDraggedSlotIndex] = (0, react.useState)(null);
2425
+ const [overSlotIndex, setOverSlotIndex] = (0, react.useState)(null);
2426
+ const reorderTimeSlotRow = (from, to) => {
2427
+ setTimeSlotRows((rows) => {
2428
+ if (from === to || from < 0 || to < 0 || from >= rows.length || to >= rows.length) return rows;
2429
+ const next = rows.map((row) => ({ ...row }));
2430
+ const [moved] = next.splice(from, 1);
2431
+ next.splice(to, 0, moved);
2432
+ return next;
2433
+ });
2434
+ };
2435
+ const updateAllDayChainSelector = (selectorIndex, patch) => {
2436
+ setAllDayChainRow((row) => ({
2437
+ ...row,
2438
+ selectors: row.selectors.map((selector, index) => index === selectorIndex ? {
2439
+ ...selector,
2440
+ ...patch
2441
+ } : selector)
2442
+ }));
2443
+ };
2444
+ const addAllDayChainSelector = () => {
2445
+ setAllDayChainRow((row) => ({
2446
+ ...row,
2447
+ selectors: [...row.selectors, {
2448
+ wildcard: false,
2449
+ provider: null,
2450
+ model: null
2451
+ }]
2452
+ }));
2453
+ };
2454
+ const removeAllDayChainSelector = (selectorIndex) => {
2455
+ setAllDayChainRow((row) => ({
2456
+ ...row,
2457
+ selectors: row.selectors.filter((_, index) => index !== selectorIndex)
2458
+ }));
2459
+ };
1589
2460
  const updateRoleRow = (index, patch) => {
1590
2461
  setRoleRows((rows) => {
1591
2462
  const next = rows.map((row) => ({ ...row }));
@@ -1631,7 +2502,8 @@ window.__ModuleLoader__.load({
1631
2502
  id: "",
1632
2503
  persona: "",
1633
2504
  selectors: [],
1634
- fallback: "inherit-root"
2505
+ fallback: "inherit-root",
2506
+ collapsed: true
1635
2507
  }]);
1636
2508
  };
1637
2509
  const removeRole = (index) => {
@@ -1647,15 +2519,21 @@ window.__ModuleLoader__.load({
1647
2519
  return next;
1648
2520
  });
1649
2521
  };
1650
- const draft = assembleConfig(scalars, rootChainRows, roleRows, ruleRows, state.config.roles.list, state.config.presets, scalars.roleAutoMatch);
1651
- const hasEmptyRuleRows = ruleRows.some((row) => row.role === "");
1652
- const dirty = JSON.stringify(draft) !== JSON.stringify(state.config) || hasEmptyRuleRows;
2522
+ const allValidationErrors = [
2523
+ ...validationErrors.main,
2524
+ ...validationErrors.sub,
2525
+ ...validationErrors.advanced
2526
+ ];
1653
2527
  const saving = state.status === "saving";
1654
2528
  const writable = state.writable;
1655
2529
  const unknownCodes = scalars.triggerCodes.filter((code) => !KNOWN_TRIGGER_CODES.includes(code));
1656
- const rootChainConfigured = rowsToRootChain(rootChainRows).length > 0;
2530
+ const presetsPresent = timeSlotRows.some((row) => row.kind === "preset");
2531
+ const glmConfigured = state.configuredProviders.some((entry) => entry.provider === "zai-coding-cn");
2532
+ const slotState = resolveSlotState(state.config, /* @__PURE__ */ new Date(), scalars.tz);
2533
+ const activeSlotIndex = slotState.winner === "all-day" ? -1 : state.config.timeSlots?.indexOf(slotState.winner) ?? -1;
1657
2534
  const roleOptions = ruleRoleOptions({ list: roleRows });
1658
2535
  const invalidRoleIds = validationAttempted ? collectInvalidRoleIds(roleRows) : null;
2536
+ const invalidSlotRows = validationAttempted ? collectInvalidSlotRows(timeSlotRows) : null;
1659
2537
  const seededIds = /* @__PURE__ */ new Map();
1660
2538
  for (const seed of state.seeds) seededIds.set(seed.id.trim(), seed.overridden);
1661
2539
  const latestSwitch = state.switches[0];
@@ -1674,45 +2552,129 @@ window.__ModuleLoader__.load({
1674
2552
  };
1675
2553
  switchesLine = latestSwitch.reason === "role-inject" ? t("status.switches.compact.roleInject", params) : t("status.switches.compact", params);
1676
2554
  }
1677
- const catalogSeededEpoch = (0, react.useRef)(null);
2555
+ const catalogSeededSections = (0, react.useRef)({
2556
+ epoch: null,
2557
+ main: false,
2558
+ sub: false
2559
+ });
1678
2560
  (0, react.useEffect)(() => {
1679
2561
  if (state.catalogStatus !== "ready") return;
1680
- if (catalogSeededEpoch.current === state.catalogEpoch) return;
1681
- if (dirty) return;
1682
- catalogSeededEpoch.current = state.catalogEpoch;
1683
- setRootChainRows(rootChainToRows(state.config.rootChain, catalogOf(state)));
1684
- setRoleRows(rolesToRows(state.config.roles.list, catalogOf(state)));
1685
- setRuleRows(rulesToRows(state.config.roles.rules, catalogOf(state)));
2562
+ const seed = catalogSeededSections.current;
2563
+ if (seed.epoch !== state.catalogEpoch) {
2564
+ seed.epoch = state.catalogEpoch;
2565
+ seed.main = false;
2566
+ seed.sub = false;
2567
+ }
2568
+ const catalog = catalogOf(state);
2569
+ if (!mainDirty && !seed.main) {
2570
+ seed.main = true;
2571
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2572
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
2573
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
2574
+ }
2575
+ if (!subDirty && !seed.sub) {
2576
+ seed.sub = true;
2577
+ setRoleRows(rolesToRows(state.config.roles.list, catalog));
2578
+ setRuleRows(rulesToRows(state.config.roles.rules, catalog));
2579
+ }
1686
2580
  }, [
1687
2581
  state.catalogStatus,
1688
2582
  state.catalogEpoch,
1689
2583
  state.config,
1690
- dirty
2584
+ mainDirty,
2585
+ subDirty
1691
2586
  ]);
1692
- const save = () => {
2587
+ const sectionPatch = (section) => {
2588
+ const base = {
2589
+ ...state.config,
2590
+ enabled: scalars.enabled
2591
+ };
2592
+ switch (section) {
2593
+ case "main": return {
2594
+ ...base,
2595
+ rootChain: draft.rootChain,
2596
+ timeSlots: draft.timeSlots,
2597
+ tz: draft.tz
2598
+ };
2599
+ case "sub": return {
2600
+ ...base,
2601
+ roles: draft.roles
2602
+ };
2603
+ case "advanced": return {
2604
+ ...base,
2605
+ triggerCodes: draft.triggerCodes,
2606
+ cooldownMs: draft.cooldownMs,
2607
+ revertPolicy: draft.revertPolicy,
2608
+ maxSwitchesPerStep: draft.maxSwitchesPerStep,
2609
+ alwaysModeRetryCap: draft.alwaysModeRetryCap,
2610
+ roleAutoMatch: draft.roleAutoMatch
2611
+ };
2612
+ }
2613
+ };
2614
+ const save = (section) => {
2615
+ setLastSaveSection(section);
1693
2616
  const errors = validateDraft(draft, t, seededIds);
1694
- if (hasEmptyRuleRows) errors.push(t("validation.ruleRoleRequired"));
1695
- if (errors.length > 0) {
2617
+ if (section === "sub" && hasEmptyRuleRows) errors.sub.push(t("validation.ruleRoleRequired"));
2618
+ if (errors[section].length > 0) {
1696
2619
  setValidationErrors(errors);
1697
2620
  setValidationAttempted(true);
1698
2621
  return;
1699
2622
  }
1700
- setValidationErrors([]);
2623
+ setValidationErrors(emptyValidationErrors());
2624
+ setValidationAttempted(false);
2625
+ controller.save(sectionPatch(section));
2626
+ };
2627
+ const saveEnabled = () => {
2628
+ setLastSaveSection("main");
2629
+ setValidationErrors(emptyValidationErrors());
2630
+ setValidationAttempted(false);
2631
+ controller.save({
2632
+ ...state.config,
2633
+ enabled: scalars.enabled
2634
+ });
2635
+ };
2636
+ const discardSection = (section) => {
2637
+ switch (section) {
2638
+ case "main":
2639
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2640
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalogOf(state)));
2641
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalogOf(state)));
2642
+ setScalars((prev) => ({
2643
+ ...prev,
2644
+ enabled: state.config.enabled,
2645
+ tz: state.config.tz ?? "Asia/Shanghai"
2646
+ }));
2647
+ break;
2648
+ case "sub":
2649
+ setRoleRows(rolesToRows(state.config.roles.list, catalogOf(state)));
2650
+ setRuleRows(rulesToRows(state.config.roles.rules, catalogOf(state)));
2651
+ break;
2652
+ case "advanced": setScalars((prev) => ({
2653
+ ...prev,
2654
+ triggerCodes: [...state.config.triggerCodes],
2655
+ cooldownMs: state.config.cooldownMs,
2656
+ revertPolicy: state.config.revertPolicy,
2657
+ maxSwitchesPerStep: state.config.maxSwitchesPerStep,
2658
+ alwaysModeRetryCap: state.config.alwaysModeRetryCap,
2659
+ roleAutoMatch: state.config.roleAutoMatch
2660
+ }));
2661
+ }
2662
+ setValidationErrors(emptyValidationErrors());
1701
2663
  setValidationAttempted(false);
1702
- controller.save(draft);
1703
2664
  };
1704
- const discard = () => {
1705
- setScalars(scalarsOf(state.config));
1706
- setRootChainRows(rootChainToRows(state.config.rootChain, catalogOf(state)));
1707
- setRoleRows(rolesToRows(state.config.roles.list, catalogOf(state)));
1708
- setRuleRows(rulesToRows(state.config.roles.rules, catalogOf(state)));
1709
- setValidationErrors([]);
2665
+ const discardEnabled = () => {
2666
+ setScalars((prev) => ({
2667
+ ...prev,
2668
+ enabled: state.config.enabled
2669
+ }));
2670
+ setValidationErrors(emptyValidationErrors());
1710
2671
  setValidationAttempted(false);
1711
2672
  };
1712
2673
  (0, react.useEffect)(() => {
1713
2674
  if (!validationAttempted) return;
1714
- if (validateDraft(draft, t, seededIds).length === 0 && !ruleRows.some((row) => row.role === "")) {
1715
- setValidationErrors([]);
2675
+ const errors = validateDraft(draft, t, seededIds);
2676
+ if (errors.main.length === 0 && errors.sub.length === 0 && errors.advanced.length === 0 && !ruleRows.some((row) => row.role === "")) {
2677
+ setValidationErrors(emptyValidationErrors());
1716
2678
  setValidationAttempted(false);
1717
2679
  }
1718
2680
  }, [
@@ -1721,13 +2683,10 @@ window.__ModuleLoader__.load({
1721
2683
  ruleRows,
1722
2684
  t
1723
2685
  ]);
1724
- const confirmReset = () => {
1725
- setResetting(true);
1726
- controller.resetToDefaults().finally(() => {
1727
- setResetting(false);
1728
- setConfirmingReset(false);
1729
- });
1730
- };
2686
+ (0, react.useEffect)(() => {
2687
+ if (state.status === "ready") setLastSaveSection(null);
2688
+ if (state.status === "error") forceReseed.current = false;
2689
+ }, [state.status]);
1731
2690
  const [userOpen, setUserOpen] = (0, react.useState)(false);
1732
2691
  const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
1733
2692
  const degradedLatch = (0, react.useRef)(false);
@@ -1768,871 +2727,1323 @@ window.__ModuleLoader__.load({
1768
2727
  });
1769
2728
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1770
2729
  className: open ? `${FallbacksCard_module_css_default.card} ${FallbacksCard_module_css_default.cardOpen}` : FallbacksCard_module_css_default.card,
1771
- children: [
1772
- header,
1773
- open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1774
- className: FallbacksCard_module_css_default.body,
1775
- children: [
1776
- state.legacyKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1777
- className: FallbacksCard_module_css_default.legacyNotice,
1778
- role: "status",
1779
- children: t("legacy.banner", { keys: state.legacyKeys.join(", ") })
1780
- }),
1781
- state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1782
- className: FallbacksCard_module_css_default.noticeRow,
1783
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1784
- className: FallbacksCard_module_css_default.error,
1785
- role: "alert",
1786
- children: t("error.generic", { message: state.error })
1787
- }), !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1788
- variant: "outline",
1789
- size: "sm",
1790
- onClick: () => {
1791
- controller.load();
1792
- },
1793
- children: t("retry")
1794
- })]
1795
- }),
1796
- validationErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2730
+ children: [header, open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2731
+ className: FallbacksCard_module_css_default.body,
2732
+ children: [
2733
+ state.legacyKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2734
+ className: FallbacksCard_module_css_default.legacyNotice,
2735
+ role: "status",
2736
+ children: t("legacy.banner", { keys: state.legacyKeys.join(", ") })
2737
+ }),
2738
+ state.status === "error" && state.error !== null && lastSaveSection === null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2739
+ className: FallbacksCard_module_css_default.noticeRow,
2740
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1797
2741
  className: FallbacksCard_module_css_default.error,
1798
2742
  role: "alert",
1799
- children: `${t("validation.blocked")}${validationErrors.join("; ")}`
1800
- }),
1801
- degraded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1802
- className: FallbacksCard_module_css_default.notice,
1803
- role: "status",
1804
- children: t("unavailable")
1805
- }),
1806
- state.status === "ready" && !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1807
- className: FallbacksCard_module_css_default.readOnly,
1808
- role: "status",
1809
- children: t("readOnly")
1810
- }),
1811
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1812
- className: FallbacksCard_module_css_default.form,
1813
- children: [
1814
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1815
- className: FallbacksCard_module_css_default.checkboxRow,
1816
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1817
- className: FallbacksCard_module_css_default.checkLabel,
1818
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1819
- className: FallbacksCard_module_css_default.checkLabelTitle,
1820
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1821
- htmlFor: "fallbacks-enabled",
1822
- children: t("enabled.label")
1823
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
1824
- label: t("enabled.tooltip"),
1825
- disabled: !writable
1826
- })]
1827
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1828
- className: FallbacksCard_module_css_default.checkLabelDesc,
1829
- children: t("enabled.hint")
2743
+ children: t("error.generic", { message: state.error })
2744
+ }), !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2745
+ variant: "outline",
2746
+ size: "sm",
2747
+ onClick: () => {
2748
+ controller.load();
2749
+ },
2750
+ children: t("retry")
2751
+ })]
2752
+ }),
2753
+ degraded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2754
+ className: FallbacksCard_module_css_default.notice,
2755
+ role: "status",
2756
+ children: t("unavailable")
2757
+ }),
2758
+ state.status === "ready" && !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2759
+ className: FallbacksCard_module_css_default.readOnly,
2760
+ role: "status",
2761
+ children: t("readOnly")
2762
+ }),
2763
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2764
+ className: FallbacksCard_module_css_default.form,
2765
+ children: [
2766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2767
+ className: FallbacksCard_module_css_default.checkboxRow,
2768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2769
+ className: FallbacksCard_module_css_default.checkLabel,
2770
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2771
+ className: FallbacksCard_module_css_default.checkLabelTitle,
2772
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2773
+ htmlFor: "fallbacks-enabled",
2774
+ children: t("enabled.label")
2775
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2776
+ label: t("enabled.tooltip"),
2777
+ disabled: !writable
1830
2778
  })]
1831
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1832
- id: "fallbacks-enabled",
1833
- type: "checkbox",
1834
- className: FallbacksCard_module_css_default.checkbox,
1835
- checked: scalars.enabled,
1836
- disabled: !writable,
1837
- onChange: (event) => {
1838
- updateScalars((draft) => {
1839
- draft.enabled = event.target.checked;
1840
- });
1841
- }
2779
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2780
+ className: FallbacksCard_module_css_default.checkLabelDesc,
2781
+ children: t("enabled.hint")
1842
2782
  })]
1843
- }),
1844
- !scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2783
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2784
+ id: "fallbacks-enabled",
2785
+ type: "checkbox",
2786
+ className: FallbacksCard_module_css_default.checkbox,
2787
+ checked: scalars.enabled,
2788
+ disabled: !writable,
2789
+ onChange: (event) => {
2790
+ updateScalars((draft) => {
2791
+ draft.enabled = event.target.checked;
2792
+ });
2793
+ }
2794
+ })]
2795
+ }),
2796
+ !scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2797
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1845
2798
  className: FallbacksCard_module_css_default.offNotice,
1846
2799
  children: t("enabled.off")
1847
2800
  }),
1848
- scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
1849
- className: FallbacksCard_module_css_default.fieldset,
1850
- disabled: !writable,
1851
- children: [
1852
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1853
- className: FallbacksCard_module_css_default.field,
1854
- role: "group",
1855
- "aria-labelledby": "fallbacks-root-chain",
1856
- children: [
1857
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1858
- className: FallbacksCard_module_css_default.fieldLabel,
1859
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1860
- id: "fallbacks-root-chain",
1861
- children: t("rootChain.label")
1862
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
1863
- label: t("rootChain.tooltip"),
1864
- disabled: !writable
1865
- })]
1866
- }),
1867
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1868
- className: FallbacksCard_module_css_default.hint,
1869
- children: t("rootChain.firstLine")
1870
- }),
1871
- scalars.enabled && rootChainConfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1872
- className: FallbacksCard_module_css_default.hint,
1873
- children: t("rootChain.hint")
1874
- }),
1875
- state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1876
- className: FallbacksCard_module_css_default.hint,
1877
- children: t("catalog.error", { message: state.catalogError })
1878
- }),
1879
- state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1880
- className: FallbacksCard_module_css_default.hint,
1881
- children: t("catalog.partial", { message: state.catalogError })
1882
- }),
1883
- state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1884
- className: FallbacksCard_module_css_default.hint,
1885
- children: t("catalog.empty")
1886
- }),
1887
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1888
- className: FallbacksCard_module_css_default.list,
1889
- children: rootChainRows.map((row, rowIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1890
- className: FallbacksCard_module_css_default.editorCard,
1891
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1892
- className: FallbacksCard_module_css_default.chainSelectors,
1893
- children: row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
1894
- selector,
1895
- catalog: catalogOf(state),
1896
- configuredProviders: state.configuredProviders,
1897
- disabled: !writable,
1898
- t,
1899
- onChange: (patch) => {
1900
- updateRootChainSelector(selectorIndex, patch);
1901
- },
1902
- onRemove: () => {
1903
- removeRootChainSelector(selectorIndex);
1904
- }
1905
- }, selectorIndex))
1906
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1907
- variant: "outline",
1908
- size: "sm",
1909
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
1910
- className: FallbacksCard_module_css_default.addButton,
1911
- onClick: addRootChainSelector,
1912
- children: t("rootChain.selector.add")
1913
- })]
1914
- }, rowIndex))
1915
- })
1916
- ]
1917
- }),
1918
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1919
- className: FallbacksCard_module_css_default.field,
1920
- role: "group",
1921
- "aria-labelledby": "fallbacks-roles-list",
1922
- children: [
1923
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1924
- className: FallbacksCard_module_css_default.fieldLabel,
1925
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1926
- id: "fallbacks-roles-list",
1927
- children: t("roles.list.label")
1928
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
1929
- label: t("roles.list.tooltip"),
1930
- disabled: !writable
1931
- })]
1932
- }),
1933
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1934
- className: FallbacksCard_module_css_default.hint,
1935
- children: t("roles.list.hint")
1936
- }),
1937
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1938
- className: FallbacksCard_module_css_default.list,
1939
- children: roleRows.map((row, index) => {
1940
- const invalid = invalidRoleIds?.has(row.id.trim()) ?? false;
1941
- const seed = seededIds.get(row.id.trim());
1942
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1943
- className: FallbacksCard_module_css_default.editorCard,
1944
- children: [
1945
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1946
- className: FallbacksCard_module_css_default.ruleGrid,
1947
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1948
- className: FallbacksCard_module_css_default.ruleCell,
1949
- children: [
1950
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1951
- className: FallbacksCard_module_css_default.ruleCellLabel,
1952
- children: t("roles.id")
1953
- }),
1954
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1955
- className: `${FallbacksCard_module_css_default.input} ${invalid ? FallbacksCard_module_css_default.inputInvalid : ""}`,
1956
- value: row.id,
1957
- placeholder: t("roles.idPlaceholder"),
1958
- "aria-label": t("roles.id"),
1959
- "aria-invalid": invalid ? true : void 0,
1960
- disabled: !writable || seed !== void 0,
1961
- onChange: (event) => {
1962
- updateRoleRow(index, { id: event.target.value });
1963
- }
1964
- }),
1965
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1966
- className: FallbacksCard_module_css_default.hint,
1967
- children: t("roles.id.hint")
1968
- })
1969
- ]
1970
- })
1971
- }),
1972
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2801
+ allValidationErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2802
+ className: FallbacksCard_module_css_default.error,
2803
+ role: "alert",
2804
+ children: `${t("validation.blocked")}${allValidationErrors.join("; ")}`
2805
+ }),
2806
+ lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2807
+ className: FallbacksCard_module_css_default.error,
2808
+ role: "alert",
2809
+ children: t("error.generic", { message: state.error })
2810
+ }),
2811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2812
+ className: FallbacksCard_module_css_default.sectionActions,
2813
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2814
+ type: "button",
2815
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2816
+ disabled: !enabledDirty || saving,
2817
+ onClick: discardEnabled,
2818
+ children: t("discard")
2819
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2820
+ type: "button",
2821
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2822
+ disabled: !writable || saving || !enabledDirty,
2823
+ onClick: saveEnabled,
2824
+ children: saving ? t("save.saving") : t("save")
2825
+ })]
2826
+ })
2827
+ ] }),
2828
+ scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2829
+ className: FallbacksCard_module_css_default.fieldset,
2830
+ disabled: !writable,
2831
+ children: [
2832
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2833
+ className: FallbacksCard_module_css_default.sectionHeading,
2834
+ id: "fallbacks-main-agent",
2835
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2836
+ className: FallbacksCard_module_css_default.sectionHeadingText,
2837
+ children: t("mainAgent.label")
2838
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2839
+ className: FallbacksCard_module_css_default.sectionActions,
2840
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2841
+ type: "button",
2842
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2843
+ disabled: !mainDirty || saving,
2844
+ onClick: () => {
2845
+ discardSection("main");
2846
+ },
2847
+ children: t("discard")
2848
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2849
+ type: "button",
2850
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2851
+ disabled: !writable || saving || !mainDirty,
2852
+ onClick: () => {
2853
+ save("main");
2854
+ },
2855
+ children: saving ? t("save.saving") : t("save")
2856
+ })]
2857
+ })]
2858
+ }),
2859
+ validationErrors.main.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2860
+ className: FallbacksCard_module_css_default.error,
2861
+ role: "alert",
2862
+ children: `${t("validation.blocked")}${validationErrors.main.join("; ")}`
2863
+ }),
2864
+ lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2865
+ className: FallbacksCard_module_css_default.error,
2866
+ role: "alert",
2867
+ children: t("error.generic", { message: state.error })
2868
+ }),
2869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2870
+ className: FallbacksCard_module_css_default.field,
2871
+ role: "group",
2872
+ "aria-labelledby": "fallbacks-time-slots",
2873
+ children: [
2874
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2875
+ className: FallbacksCard_module_css_default.fieldLabel,
2876
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2877
+ id: "fallbacks-time-slots",
2878
+ children: t("timeSlots.label")
2879
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2880
+ label: t("timeSlots.tooltip"),
2881
+ disabled: !writable
2882
+ })]
2883
+ }),
2884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2885
+ className: FallbacksCard_module_css_default.hint,
2886
+ children: t("timeSlots.hint")
2887
+ }),
2888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2889
+ className: FallbacksCard_module_css_default.list,
2890
+ children: timeSlotRows.map((row, index) => {
2891
+ const invalidWindow = invalidSlotRows?.has(index) ?? false;
2892
+ const chainEmpty = row.selectors.every((selector) => selectorRowToRaw(selector) === "");
2893
+ const firstModel = row.selectors.map(selectorRowToRaw).find((entry) => entry !== "");
2894
+ const slotExpanded = !row.collapsed || !writable;
2895
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2896
+ className: `${FallbacksCard_module_css_default.editorCard} ${draggedSlotIndex === index ? FallbacksCard_module_css_default.slotCardDragging : ""} ${overSlotIndex === index && draggedSlotIndex !== null && draggedSlotIndex !== index ? FallbacksCard_module_css_default.slotCardOver : ""}`,
2897
+ onDragOver: (event) => {
2898
+ if (draggedSlotIndex === null) return;
2899
+ event.preventDefault();
2900
+ if (overSlotIndex !== index) setOverSlotIndex(index);
2901
+ },
2902
+ onDrop: (event) => {
2903
+ event.preventDefault();
2904
+ const from = draggedSlotIndex;
2905
+ setDraggedSlotIndex(null);
2906
+ setOverSlotIndex(null);
2907
+ if (from !== null && from !== index) reorderTimeSlotRow(from, index);
2908
+ },
2909
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2910
+ className: FallbacksCard_module_css_default.collapseRow,
2911
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2912
+ type: "button",
2913
+ className: FallbacksCard_module_css_default.collapseToggle,
2914
+ "aria-expanded": slotExpanded,
2915
+ "aria-label": t(slotExpanded ? "timeSlots.collapse" : "timeSlots.expand"),
2916
+ disabled: !writable,
2917
+ onClick: () => {
2918
+ updateTimeSlotRow(index, { collapsed: !row.collapsed });
2919
+ },
2920
+ children: [
2921
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: slotExpanded ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron }),
2922
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2923
+ className: FallbacksCard_module_css_default.collapseTitle,
2924
+ children: row.kind === "preset" ? t(`timeSlots.preset.${row.preset}.label`) : row.name !== "" ? row.name : `custom ${row.start}-${row.end}`
2925
+ }),
2926
+ row.kind === "preset" && (row.preset === "liang-peak" || row.preset === "glm-peak") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2927
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagHighCost}`,
2928
+ children: t("timeSlots.preset.highCost")
2929
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2930
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagMultiplier}`,
2931
+ children: t("timeSlots.preset.multiplier", { n: row.preset === "liang-peak" ? "2" : "3" })
2932
+ })] }),
2933
+ activeSlotIndex === index && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2934
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagActive}`,
2935
+ children: t("timeSlots.active")
2936
+ }),
2937
+ firstModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2938
+ className: FallbacksCard_module_css_default.collapseMeta,
2939
+ children: firstModel
2940
+ })
2941
+ ]
2942
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2943
+ type: "button",
2944
+ className: FallbacksCard_module_css_default.dragHandle,
2945
+ draggable: writable,
2946
+ "data-tip": t("timeSlots.drag"),
2947
+ "aria-label": t("timeSlots.drag"),
2948
+ disabled: !writable,
2949
+ onDragStart: () => {
2950
+ if (!writable) return;
2951
+ setDraggedSlotIndex(index);
2952
+ setOverSlotIndex(index);
2953
+ },
2954
+ onDragEnd: () => {
2955
+ setDraggedSlotIndex(null);
2956
+ setOverSlotIndex(null);
2957
+ },
2958
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, { className: FallbacksCard_module_css_default.dragHandleIcon })
2959
+ })]
2960
+ }), slotExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2961
+ row.kind === "preset" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1973
2963
  className: FallbacksCard_module_css_default.ruleGrid,
1974
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2964
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1975
2965
  className: FallbacksCard_module_css_default.ruleCell,
1976
- children: [
1977
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1978
- className: FallbacksCard_module_css_default.ruleCellLabel,
1979
- children: t("roles.persona")
1980
- }),
1981
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1982
- rows: 3,
1983
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.inputTextarea}`,
1984
- value: row.persona,
1985
- placeholder: t("roles.personaPlaceholder"),
1986
- "aria-label": t("roles.persona"),
1987
- disabled: !writable,
1988
- onChange: (event) => {
1989
- updateRoleRow(index, { persona: event.target.value });
1990
- }
1991
- }),
1992
- seed !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1993
- className: FallbacksCard_module_css_default.hint,
1994
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1995
- className: FallbacksCard_module_css_default.pending,
1996
- children: t(seed ? "roles.seedOverride" : "roles.seedDefault")
1997
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1998
- variant: "outline",
1999
- size: "sm",
2000
- disabled: !writable || saving,
2001
- onClick: () => {
2002
- controller.revertSeed(row.id.trim());
2003
- },
2004
- children: t("roles.revertPersona")
2005
- })]
2006
- })
2007
- ]
2008
- })
2966
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2967
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2968
+ children: t("timeSlots.preset.name")
2969
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2970
+ className: FallbacksCard_module_css_default.slotPresetName,
2971
+ children: t(`timeSlots.preset.${row.preset}.label`)
2972
+ })]
2973
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2974
+ className: FallbacksCard_module_css_default.ruleCell,
2975
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2976
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2977
+ children: t("timeSlots.preset.windowLabel")
2978
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2979
+ className: FallbacksCard_module_css_default.hint,
2980
+ children: t(`timeSlots.preset.${row.preset}.window`)
2981
+ })]
2982
+ })]
2983
+ }),
2984
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2985
+ className: FallbacksCard_module_css_default.hint,
2986
+ children: t("timeSlots.preset.chainsOnly")
2009
2987
  }),
2988
+ (row.preset === "glm-peak" || row.preset === "glm-valley") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2989
+ className: FallbacksCard_module_css_default.hint,
2990
+ children: t("timeSlots.preset.glm.note")
2991
+ })
2992
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2010
2993
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2011
- className: FallbacksCard_module_css_default.chainSelectors,
2012
- children: [row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
2013
- selector,
2014
- catalog: catalogOf(state),
2015
- configuredProviders: state.configuredProviders,
2994
+ className: FallbacksCard_module_css_default.field,
2995
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2996
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2997
+ children: t("timeSlots.name")
2998
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2999
+ className: FallbacksCard_module_css_default.input,
3000
+ value: row.name,
3001
+ placeholder: t("timeSlots.name"),
3002
+ "aria-label": t("timeSlots.name"),
2016
3003
  disabled: !writable,
2017
- t,
2018
- onChange: (patch) => {
2019
- updateRoleSelector(index, selectorIndex, patch);
2020
- },
2021
- onRemove: () => {
2022
- removeRoleSelector(index, selectorIndex);
3004
+ onChange: (event) => {
3005
+ updateTimeSlotRow(index, { name: event.target.value });
2023
3006
  }
2024
- }, selectorIndex)), row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3007
+ })]
3008
+ }),
3009
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3010
+ className: FallbacksCard_module_css_default.field,
3011
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3012
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3013
+ children: t("timeSlots.tz.label")
3014
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2025
3015
  className: FallbacksCard_module_css_default.hint,
2026
- children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
3016
+ "aria-label": t("timeSlots.tz.label"),
3017
+ children: tzDisplayLabel(presetsPresent ? "Asia/Shanghai" : hostTimeZone())
2027
3018
  })]
2028
3019
  }),
2029
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3020
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2030
3021
  className: FallbacksCard_module_css_default.ruleGrid,
2031
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3022
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2032
3023
  className: FallbacksCard_module_css_default.ruleCell,
2033
3024
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2034
3025
  className: FallbacksCard_module_css_default.ruleCellLabel,
2035
- children: t("roles.fallback")
2036
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2037
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
2038
- value: row.fallback,
2039
- "aria-label": t("roles.fallback"),
3026
+ children: t("timeSlots.start")
3027
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3028
+ className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
3029
+ value: row.start,
3030
+ placeholder: "09:00",
3031
+ "aria-label": t("timeSlots.start"),
2040
3032
  disabled: !writable,
2041
3033
  onChange: (event) => {
2042
- updateRoleRow(index, { fallback: event.target.value });
2043
- },
2044
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2045
- value: "inherit-root",
2046
- children: t("roles.fallback.inherit-root")
2047
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2048
- value: "none",
2049
- children: t("roles.fallback.none")
2050
- })]
3034
+ updateTimeSlotRow(index, { start: event.target.value });
3035
+ }
2051
3036
  })]
2052
- })
2053
- }),
2054
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2055
- variant: "outline",
2056
- size: "sm",
2057
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
2058
- className: FallbacksCard_module_css_default.addButton,
2059
- onClick: () => {
2060
- addRoleSelector(index);
2061
- },
2062
- children: t("roles.selector.add")
3037
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3038
+ className: FallbacksCard_module_css_default.ruleCell,
3039
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3040
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3041
+ children: t("timeSlots.end")
3042
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3043
+ className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
3044
+ value: row.end,
3045
+ placeholder: "18:00",
3046
+ "aria-label": t("timeSlots.end"),
3047
+ disabled: !writable,
3048
+ onChange: (event) => {
3049
+ updateTimeSlotRow(index, { end: event.target.value });
3050
+ }
3051
+ })]
3052
+ })]
2063
3053
  }),
2064
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2065
- className: FallbacksCard_module_css_default.cardFoot,
2066
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2067
- type: "button",
2068
- className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
2069
- "data-tip": t("roles.remove"),
2070
- "aria-label": t("roles.remove"),
2071
- onClick: () => {
2072
- removeRole(index);
2073
- },
2074
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
2075
- })
2076
- })
2077
- ]
2078
- }, index);
2079
- })
2080
- }),
2081
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2082
- variant: "outline",
2083
- size: "sm",
2084
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
2085
- className: FallbacksCard_module_css_default.addButton,
2086
- onClick: addRole,
2087
- children: t("roles.add")
2088
- })
2089
- ]
2090
- }),
2091
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2092
- className: FallbacksCard_module_css_default.field,
2093
- role: "group",
2094
- "aria-labelledby": "fallbacks-roles-rules",
2095
- children: [
2096
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2097
- className: FallbacksCard_module_css_default.fieldLabel,
2098
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2099
- id: "fallbacks-roles-rules",
2100
- children: t("roles.rules")
2101
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2102
- label: t("roles.rules.tooltip"),
2103
- disabled: !writable
2104
- })]
2105
- }),
2106
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2107
- className: FallbacksCard_module_css_default.hint,
2108
- children: t("roles.rules.hint")
2109
- }),
2110
- state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2111
- className: FallbacksCard_module_css_default.hint,
2112
- children: t("catalog.error", { message: state.catalogError })
2113
- }),
2114
- state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2115
- className: FallbacksCard_module_css_default.hint,
2116
- children: t("catalog.partial", { message: state.catalogError })
2117
- }),
2118
- state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2119
- className: FallbacksCard_module_css_default.hint,
2120
- children: t("catalog.empty")
2121
- }),
2122
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2123
- className: FallbacksCard_module_css_default.list,
2124
- children: ruleRows.map((row, index) => {
2125
- const catalog = catalogOf(state);
2126
- const providerRaw = selectionToRaw(row.provider);
2127
- const group = catalog?.groups.find((entry) => entry.id === providerRaw);
2128
- const providerOutside = row.provider?.kind === "outside";
2129
- const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !state.configuredProviders.some((entry) => entry.provider === providerRaw);
2130
- const modelOutside = row.model?.kind === "outside";
2131
- const roleOutside = row.role !== "" && !roleOptions.includes(row.role);
2132
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2133
- className: FallbacksCard_module_css_default.editorCard,
2134
- children: [
2135
3054
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2136
- className: FallbacksCard_module_css_default.ruleGrid,
3055
+ className: FallbacksCard_module_css_default.field,
2137
3056
  children: [
2138
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2139
- className: FallbacksCard_module_css_default.ruleCell,
2140
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2141
- className: FallbacksCard_module_css_default.ruleCellLabel,
2142
- children: t("roles.rule.origin")
2143
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2144
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
2145
- value: row.origin,
2146
- onChange: (event) => {
2147
- updateRuleRow(index, { origin: event.target.value });
2148
- },
2149
- children: [
2150
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2151
- value: "",
2152
- children: t("roles.rule.origin.any")
2153
- }),
2154
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2155
- value: "root",
2156
- children: t("roles.rule.origin.root")
2157
- }),
2158
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2159
- value: "subagent",
2160
- children: t("roles.rule.origin.subagent")
2161
- })
2162
- ]
2163
- })]
2164
- }),
2165
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2166
- className: FallbacksCard_module_css_default.ruleCell,
2167
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2168
- className: FallbacksCard_module_css_default.ruleCellLabel,
2169
- children: t("roles.rule.provider")
2170
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2171
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
2172
- value: providerRaw,
2173
- onChange: (event) => {
2174
- if (event.target.value === providerRaw) return;
2175
- updateRuleRow(index, {
2176
- provider: classifyProvider(event.target.value, catalog),
2177
- model: null
2178
- });
2179
- },
2180
- children: [
2181
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2182
- value: "",
2183
- children: t("roles.rule.provider.any")
2184
- }),
2185
- state.configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2186
- value: entry.provider,
2187
- children: entry.displayName
2188
- }, entry.provider)),
2189
- providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2190
- value: providerRaw,
2191
- children: `${providerRaw}${t("catalog.unconfigured.short")}`
2192
- }),
2193
- providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2194
- value: providerRaw,
2195
- children: `${providerRaw}${t("catalog.outside.short")}`
2196
- })
2197
- ]
2198
- })]
3057
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3058
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3059
+ children: t("timeSlots.days")
2199
3060
  }),
2200
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2201
- className: FallbacksCard_module_css_default.ruleCell,
2202
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2203
- className: FallbacksCard_module_css_default.ruleCellLabel,
2204
- children: t("roles.rule.model")
2205
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2206
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
2207
- value: selectionToRaw(row.model),
2208
- onChange: (event) => {
2209
- updateRuleRow(index, { model: classifyModel(providerRaw, event.target.value, catalog) });
2210
- },
2211
- children: [
2212
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2213
- value: "",
2214
- children: t("roles.rule.model.any")
2215
- }),
2216
- (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2217
- value: model.id,
2218
- children: model.name
2219
- }, model.id)),
2220
- modelOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2221
- value: selectionToRaw(row.model),
2222
- children: `${selectionToRaw(row.model)}${t("catalog.outside.short")}`
2223
- })
2224
- ]
2225
- })]
3061
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3062
+ className: FallbacksCard_module_css_default.dayRow,
3063
+ children: SLOT_WEEKDAYS.map((day, dayIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3064
+ className: FallbacksCard_module_css_default.dayCell,
3065
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3066
+ type: "checkbox",
3067
+ checked: row.days.includes(dayIndex),
3068
+ disabled: !writable,
3069
+ onChange: () => {
3070
+ updateTimeSlotRow(index, { days: row.days.includes(dayIndex) ? row.days.filter((existing) => existing !== dayIndex) : [...row.days, dayIndex] });
3071
+ }
3072
+ }), t(`timeSlots.day.${day}`)]
3073
+ }, day))
2226
3074
  }),
2227
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2228
- className: FallbacksCard_module_css_default.ruleCell,
2229
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2230
- className: FallbacksCard_module_css_default.ruleCellLabel,
2231
- children: t("roles.rule.role")
2232
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2233
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
2234
- value: row.role,
2235
- disabled: !writable,
2236
- onChange: (event) => {
2237
- updateRuleRow(index, { role: event.target.value });
2238
- },
2239
- children: [
2240
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2241
- value: "",
2242
- children: t("roles.rule.roleSelectPlaceholder")
2243
- }),
2244
- roleOptions.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2245
- value: id,
2246
- children: id === "inherit" ? t("roles.rule.role.inherit") : id
2247
- }, id)),
2248
- roleOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2249
- value: row.role,
2250
- children: `${row.role}${t("roles.rule.roleUndeclared.short")}`
2251
- })
2252
- ]
2253
- })]
3075
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3076
+ className: FallbacksCard_module_css_default.hint,
3077
+ children: t("timeSlots.days.hint")
2254
3078
  })
2255
3079
  ]
2256
3080
  }),
2257
- (providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2258
- className: FallbacksCard_module_css_default.hint,
2259
- children: [t("catalog.outside.hint"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2260
- label: t("catalog.outside.tooltip"),
2261
- disabled: !writable
2262
- })]
2263
- }),
2264
- row.role === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3081
+ (row.start !== "" || row.end !== "") && !(HHMM_RE.test(row.start) && HHMM_RE.test(row.end)) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2265
3082
  className: FallbacksCard_module_css_default.hint,
2266
- children: t("validation.ruleRoleRequired")
2267
- }),
2268
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2269
- className: FallbacksCard_module_css_default.cardFoot,
2270
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2271
- type: "button",
2272
- className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
2273
- "data-tip": t("roles.removeRule"),
2274
- "aria-label": t("roles.removeRule"),
2275
- onClick: () => {
2276
- setRuleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
2277
- },
2278
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
2279
- })
3083
+ children: t("validation.slotWindow")
2280
3084
  })
2281
- ]
2282
- }, index);
2283
- })
2284
- }),
2285
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2286
- variant: "outline",
2287
- size: "sm",
2288
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
2289
- className: FallbacksCard_module_css_default.addButton,
2290
- onClick: () => {
2291
- setRuleRows((rows) => [...rows, {
2292
- origin: "",
2293
- provider: null,
2294
- model: null,
2295
- role: ""
2296
- }]);
2297
- },
2298
- children: t("roles.addRule")
2299
- })
2300
- ]
2301
- }),
2302
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2303
- className: FallbacksCard_module_css_default.field,
2304
- role: "group",
2305
- "aria-labelledby": "fallbacks-advanced",
2306
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2307
- type: "button",
2308
- className: FallbacksCard_module_css_default.sectionToggle,
2309
- disabled: !writable,
2310
- "aria-expanded": advancedVisible,
2311
- "aria-controls": advancedVisible ? "fallbacks-advanced-body" : void 0,
2312
- "aria-label": t(advancedVisible ? "advanced.collapse" : "advanced.expand"),
2313
- onClick: () => {
2314
- if (writable) setAdvancedOpen(!advancedOpen);
2315
- },
2316
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2317
- id: "fallbacks-advanced",
2318
- className: FallbacksCard_module_css_default.sectionToggleText,
2319
- children: t("advanced.label")
2320
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: advancedVisible ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron })]
2321
- }), advancedVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2322
- id: "fallbacks-advanced-body",
2323
- children: [
2324
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2325
- className: FallbacksCard_module_css_default.checkboxRow,
2326
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2327
- className: FallbacksCard_module_css_default.checkLabel,
2328
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2329
- className: FallbacksCard_module_css_default.checkLabelTitle,
2330
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2331
- htmlFor: "fallbacks-role-automatch",
2332
- children: t("roleAutoMatch.label")
2333
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2334
- label: t("roleAutoMatch.tooltip"),
2335
- disabled: !writable
2336
- })]
2337
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2338
- className: FallbacksCard_module_css_default.checkLabelDesc,
2339
- children: t("roleAutoMatch.hint")
2340
- })]
2341
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2342
- id: "fallbacks-role-automatch",
2343
- type: "checkbox",
2344
- className: FallbacksCard_module_css_default.checkbox,
2345
- checked: scalars.roleAutoMatch,
2346
- disabled: !writable,
2347
- onChange: (event) => {
2348
- updateScalars((draft) => {
2349
- draft.roleAutoMatch = event.target.checked;
2350
- });
2351
- }
2352
- })]
2353
- }),
2354
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2355
- className: FallbacksCard_module_css_default.field,
2356
- role: "group",
2357
- "aria-labelledby": "fallbacks-trigger-codes",
2358
- children: [
2359
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2360
- className: FallbacksCard_module_css_default.fieldLabel,
2361
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2362
- id: "fallbacks-trigger-codes",
2363
- children: t("triggerCodes.label")
2364
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2365
- label: t("triggerCodes.tooltip"),
2366
- disabled: !writable
2367
- })]
2368
- }),
2369
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3085
+ ] }),
3086
+ chainEmpty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2370
3087
  className: FallbacksCard_module_css_default.hint,
2371
- children: t("triggerCodes.hint")
3088
+ children: t("validation.slotChainRequired")
2372
3089
  }),
2373
- KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2374
- className: FallbacksCard_module_css_default.optionRow,
2375
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2376
- type: "checkbox",
2377
- checked: scalars.triggerCodes.includes(code),
2378
- onChange: (event) => {
2379
- updateScalars((draft) => {
2380
- draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
2381
- });
3090
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3091
+ className: FallbacksCard_module_css_default.chainSelectors,
3092
+ children: row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
3093
+ selector,
3094
+ catalog: catalogOf(state),
3095
+ configuredProviders: state.configuredProviders,
3096
+ disabled: !writable,
3097
+ t,
3098
+ onChange: (patch) => {
3099
+ updateTimeSlotSelector(index, selectorIndex, patch);
3100
+ },
3101
+ onRemove: () => {
3102
+ removeTimeSlotSelector(index, selectorIndex);
2382
3103
  }
2383
- }), t(TRIGGER_CODE_LABELS[code])]
2384
- }, code)),
2385
- unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2386
- className: FallbacksCard_module_css_default.hint,
2387
- children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
2388
- })
2389
- ]
2390
- }),
2391
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2392
- className: FallbacksCard_module_css_default.field,
2393
- role: "group",
2394
- "aria-labelledby": "fallbacks-revert-policy",
2395
- children: [
2396
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2397
- className: FallbacksCard_module_css_default.fieldLabel,
2398
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2399
- id: "fallbacks-revert-policy",
2400
- children: t("revertPolicy.label")
2401
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2402
- label: t("revertPolicy.tooltip"),
2403
- disabled: !writable
2404
- })]
3104
+ }, selectorIndex))
2405
3105
  }),
2406
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2407
- className: FallbacksCard_module_css_default.hint,
2408
- children: t("revertPolicy.hint")
3106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3107
+ variant: "outline",
3108
+ size: "sm",
3109
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3110
+ className: FallbacksCard_module_css_default.addButton,
3111
+ onClick: () => {
3112
+ addTimeSlotSelector(index);
3113
+ },
3114
+ children: t("timeSlots.selector.add")
2409
3115
  }),
2410
- ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2411
- className: FallbacksCard_module_css_default.optionRow,
2412
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2413
- type: "radio",
2414
- name: "fallbacks-revert-policy",
2415
- checked: scalars.revertPolicy === policy,
2416
- onChange: () => {
2417
- updateScalars((draft) => {
2418
- draft.revertPolicy = policy;
2419
- });
2420
- }
2421
- }), t(`revertPolicy.${policy}`)]
2422
- }, policy))
2423
- ]
3116
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3117
+ className: FallbacksCard_module_css_default.cardFoot,
3118
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3119
+ className: FallbacksCard_module_css_default.rowActions,
3120
+ children: [
3121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3122
+ type: "button",
3123
+ className: FallbacksCard_module_css_default.iconButton,
3124
+ "data-tip": t("timeSlots.moveUp"),
3125
+ "aria-label": t("timeSlots.moveUp"),
3126
+ disabled: !writable || index === 0,
3127
+ onClick: () => {
3128
+ moveTimeSlotRow(index, -1);
3129
+ },
3130
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
3131
+ }),
3132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3133
+ type: "button",
3134
+ className: FallbacksCard_module_css_default.iconButton,
3135
+ "data-tip": t("timeSlots.moveDown"),
3136
+ "aria-label": t("timeSlots.moveDown"),
3137
+ disabled: !writable || index === timeSlotRows.length - 1,
3138
+ onClick: () => {
3139
+ moveTimeSlotRow(index, 1);
3140
+ },
3141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
3142
+ }),
3143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3144
+ type: "button",
3145
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
3146
+ "data-tip": t("timeSlots.remove"),
3147
+ "aria-label": t("timeSlots.remove"),
3148
+ onClick: () => {
3149
+ removeTimeSlotRow(index);
3150
+ },
3151
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
3152
+ })
3153
+ ]
3154
+ })
3155
+ })
3156
+ ] })]
3157
+ }, index);
3158
+ })
3159
+ }),
3160
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3161
+ className: FallbacksCard_module_css_default.slotAddRow,
3162
+ children: [
3163
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3164
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3165
+ value: presetToAdd,
3166
+ "aria-label": t("timeSlots.presetPlaceholder"),
3167
+ disabled: !writable,
3168
+ onChange: (event) => {
3169
+ setPresetToAdd(event.target.value);
3170
+ },
3171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3172
+ value: "",
3173
+ children: t("timeSlots.presetPlaceholder")
3174
+ }), SLOT_PRESET_IDS.filter((id) => !timeSlotRows.some((row) => row.kind === "preset" && row.preset === id)).map((id) => {
3175
+ const glmUnconfigured = !glmConfigured && (id === "glm-peak" || id === "glm-valley");
3176
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
3177
+ value: id,
3178
+ disabled: glmUnconfigured,
3179
+ children: [t(`timeSlots.preset.${id}.label`), glmUnconfigured ? t("timeSlots.preset.glm.unconfigured") : null]
3180
+ }, id);
3181
+ })]
2424
3182
  }),
2425
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2426
- className: FallbacksCard_module_css_default.numberFields,
2427
- children: [
2428
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2429
- className: FallbacksCard_module_css_default.field,
3183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3184
+ variant: "outline",
3185
+ size: "sm",
3186
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3187
+ disabled: !writable || presetToAdd === "",
3188
+ onClick: addPresetSlotRow,
3189
+ children: t("timeSlots.addPreset")
3190
+ }),
3191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3192
+ variant: "outline",
3193
+ size: "sm",
3194
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3195
+ disabled: !writable,
3196
+ onClick: addCustomSlotRow,
3197
+ children: t("timeSlots.addCustom")
3198
+ })
3199
+ ]
3200
+ })
3201
+ ]
3202
+ }),
3203
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3204
+ className: FallbacksCard_module_css_default.field,
3205
+ role: "group",
3206
+ "aria-labelledby": "fallbacks-root-chain",
3207
+ children: [
3208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3209
+ className: FallbacksCard_module_css_default.fieldLabel,
3210
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3211
+ id: "fallbacks-root-chain",
3212
+ children: t("rootChain.label")
3213
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3214
+ label: t("rootChain.tooltip"),
3215
+ disabled: !writable
3216
+ })]
3217
+ }),
3218
+ state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3219
+ className: FallbacksCard_module_css_default.hint,
3220
+ children: t("catalog.error", { message: state.catalogError })
3221
+ }),
3222
+ state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3223
+ className: FallbacksCard_module_css_default.hint,
3224
+ children: t("catalog.partial", { message: state.catalogError })
3225
+ }),
3226
+ state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3227
+ className: FallbacksCard_module_css_default.hint,
3228
+ children: t("catalog.empty")
3229
+ }),
3230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3231
+ className: FallbacksCard_module_css_default.list,
3232
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3233
+ className: FallbacksCard_module_css_default.editorCard,
3234
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3235
+ className: FallbacksCard_module_css_default.chainSelectors,
3236
+ children: allDayChainRow.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
3237
+ selector,
3238
+ catalog: catalogOf(state),
3239
+ configuredProviders: state.configuredProviders,
3240
+ disabled: !writable,
3241
+ t,
3242
+ onChange: (patch) => {
3243
+ updateAllDayChainSelector(selectorIndex, patch);
3244
+ },
3245
+ onRemove: () => {
3246
+ removeAllDayChainSelector(selectorIndex);
3247
+ }
3248
+ }, selectorIndex))
3249
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3250
+ variant: "outline",
3251
+ size: "sm",
3252
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3253
+ className: FallbacksCard_module_css_default.addButton,
3254
+ onClick: addAllDayChainSelector,
3255
+ children: t("timeSlots.selector.add")
3256
+ })]
3257
+ })
3258
+ })
3259
+ ]
3260
+ }),
3261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3262
+ className: FallbacksCard_module_css_default.field,
3263
+ role: "group",
3264
+ "aria-labelledby": "fallbacks-default-model",
3265
+ children: [
3266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3267
+ className: FallbacksCard_module_css_default.fieldLabel,
3268
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3269
+ id: "fallbacks-default-model",
3270
+ children: t("defaultModel.label")
3271
+ })
3272
+ }),
3273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3274
+ className: FallbacksCard_module_css_default.hint,
3275
+ children: t("allDay.hint")
3276
+ }),
3277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3278
+ className: FallbacksCard_module_css_default.list,
3279
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3280
+ className: FallbacksCard_module_css_default.editorCard,
3281
+ children: [
3282
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3283
+ className: FallbacksCard_module_css_default.optionRow,
3284
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3285
+ type: "radio",
3286
+ name: "fallbacks-all-day",
3287
+ checked: allDayModel === ALL_DAY_FLASH,
3288
+ disabled: !writable,
3289
+ onChange: () => {
3290
+ setAllDayModel(ALL_DAY_FLASH);
3291
+ }
3292
+ }), t("allDay.flash")]
3293
+ }),
3294
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3295
+ className: FallbacksCard_module_css_default.optionRow,
3296
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3297
+ type: "radio",
3298
+ name: "fallbacks-all-day",
3299
+ checked: allDayModel === ALL_DAY_PRO,
3300
+ disabled: !writable,
3301
+ onChange: () => {
3302
+ setAllDayModel(ALL_DAY_PRO);
3303
+ }
3304
+ }), t("allDay.pro")]
3305
+ }),
3306
+ allDayModel === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3307
+ className: FallbacksCard_module_css_default.hint,
3308
+ children: t("allDay.nonconforming")
3309
+ })
3310
+ ]
3311
+ })
3312
+ })
3313
+ ]
3314
+ }),
3315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3316
+ className: FallbacksCard_module_css_default.sectionHeading,
3317
+ id: "fallbacks-subagents",
3318
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3319
+ className: FallbacksCard_module_css_default.sectionHeadingText,
3320
+ children: t("subagents.label")
3321
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3322
+ className: FallbacksCard_module_css_default.sectionActions,
3323
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3324
+ type: "button",
3325
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3326
+ disabled: !subDirty || saving,
3327
+ onClick: () => {
3328
+ discardSection("sub");
3329
+ },
3330
+ children: t("discard")
3331
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3332
+ type: "button",
3333
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3334
+ disabled: !writable || saving || !subDirty,
3335
+ onClick: () => {
3336
+ save("sub");
3337
+ },
3338
+ children: saving ? t("save.saving") : t("save")
3339
+ })]
3340
+ })]
3341
+ }),
3342
+ validationErrors.sub.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3343
+ className: FallbacksCard_module_css_default.error,
3344
+ role: "alert",
3345
+ children: `${t("validation.blocked")}${validationErrors.sub.join("; ")}`
3346
+ }),
3347
+ lastSaveSection === "sub" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3348
+ className: FallbacksCard_module_css_default.error,
3349
+ role: "alert",
3350
+ children: t("error.generic", { message: state.error })
3351
+ }),
3352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3353
+ className: FallbacksCard_module_css_default.field,
3354
+ role: "group",
3355
+ "aria-labelledby": "fallbacks-roles-list",
3356
+ children: [
3357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3358
+ className: FallbacksCard_module_css_default.fieldLabel,
3359
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3360
+ id: "fallbacks-roles-list",
3361
+ children: t("roles.list.label")
3362
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3363
+ label: t("roles.list.tooltip"),
3364
+ disabled: !writable
3365
+ })]
3366
+ }),
3367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3368
+ className: FallbacksCard_module_css_default.hint,
3369
+ children: t("roles.list.hint")
3370
+ }),
3371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3372
+ className: FallbacksCard_module_css_default.list,
3373
+ children: roleRows.map((row, index) => {
3374
+ const invalid = invalidRoleIds?.has(row.id.trim()) ?? false;
3375
+ const seed = seededIds.get(row.id.trim());
3376
+ const roleSummary = row.selectors.map(selectorRowToRaw).find((entry) => entry !== "") ?? row.fallback;
3377
+ const roleExpanded = !row.collapsed || !writable;
3378
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3379
+ className: FallbacksCard_module_css_default.editorCard,
3380
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3381
+ className: FallbacksCard_module_css_default.collapseRow,
3382
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3383
+ type: "button",
3384
+ className: FallbacksCard_module_css_default.collapseToggle,
3385
+ "aria-expanded": roleExpanded,
3386
+ "aria-label": t(roleExpanded ? "roles.collapse" : "roles.expand"),
3387
+ disabled: !writable,
3388
+ onClick: () => {
3389
+ updateRoleRow(index, { collapsed: !row.collapsed });
3390
+ },
2430
3391
  children: [
2431
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2432
- className: FallbacksCard_module_css_default.fieldLabel,
2433
- children: [
2434
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2435
- htmlFor: "fallbacks-cooldown-ms",
2436
- children: t("cooldownMs.label")
2437
- }),
2438
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2439
- label: t("cooldownMs.tooltip"),
2440
- disabled: !writable
2441
- }),
2442
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2443
- className: FallbacksCard_module_css_default.defaultNote,
2444
- children: [
2445
- t("defaults.prefix"),
2446
- ": ",
2447
- state.config.cooldownMs
2448
- ]
2449
- })
2450
- ]
2451
- }),
2452
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2453
- id: "fallbacks-cooldown-ms",
2454
- className: FallbacksCard_module_css_default.input,
2455
- type: "number",
2456
- min: 0,
2457
- value: String(scalars.cooldownMs),
2458
- disabled: !writable,
2459
- onChange: (event) => {
2460
- updateScalars((draft) => {
2461
- draft.cooldownMs = parseCount(event.target.value);
2462
- });
2463
- }
3392
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: roleExpanded ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron }),
3393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3394
+ className: FallbacksCard_module_css_default.collapseTitle,
3395
+ children: row.id
2464
3396
  }),
2465
3397
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2466
- className: FallbacksCard_module_css_default.hint,
2467
- children: t("cooldownMs.hint")
3398
+ className: FallbacksCard_module_css_default.collapseMeta,
3399
+ children: roleSummary
2468
3400
  })
2469
3401
  ]
3402
+ })
3403
+ }), roleExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3404
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3405
+ className: FallbacksCard_module_css_default.ruleGrid,
3406
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3407
+ className: FallbacksCard_module_css_default.ruleCell,
3408
+ children: [
3409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3410
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3411
+ children: t("roles.id")
3412
+ }),
3413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3414
+ className: `${FallbacksCard_module_css_default.input} ${invalid ? FallbacksCard_module_css_default.inputInvalid : ""}`,
3415
+ value: row.id,
3416
+ placeholder: t("roles.idPlaceholder"),
3417
+ "aria-label": t("roles.id"),
3418
+ "aria-invalid": invalid ? true : void 0,
3419
+ disabled: !writable || seed !== void 0,
3420
+ onChange: (event) => {
3421
+ updateRoleRow(index, { id: event.target.value });
3422
+ }
3423
+ }),
3424
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3425
+ className: FallbacksCard_module_css_default.hint,
3426
+ children: t("roles.id.hint")
3427
+ })
3428
+ ]
3429
+ })
3430
+ }),
3431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3432
+ className: FallbacksCard_module_css_default.ruleGrid,
3433
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3434
+ className: FallbacksCard_module_css_default.ruleCell,
3435
+ children: [
3436
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3437
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3438
+ children: t("roles.persona")
3439
+ }),
3440
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
3441
+ rows: 3,
3442
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.inputTextarea}`,
3443
+ value: row.persona,
3444
+ placeholder: t("roles.personaPlaceholder"),
3445
+ "aria-label": t("roles.persona"),
3446
+ disabled: !writable,
3447
+ onChange: (event) => {
3448
+ updateRoleRow(index, { persona: event.target.value });
3449
+ }
3450
+ }),
3451
+ seed !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3452
+ className: FallbacksCard_module_css_default.hint,
3453
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3454
+ className: FallbacksCard_module_css_default.pending,
3455
+ children: t(seed ? "roles.seedOverride" : "roles.seedDefault")
3456
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3457
+ variant: "outline",
3458
+ size: "sm",
3459
+ disabled: !writable || saving,
3460
+ onClick: () => {
3461
+ forceReseed.current = true;
3462
+ controller.revertSeed(row.id.trim()).then((persona) => {
3463
+ if (persona === void 0) return;
3464
+ updateRoleRow(index, { persona });
3465
+ });
3466
+ },
3467
+ children: t("roles.revertPersona")
3468
+ })]
3469
+ })
3470
+ ]
3471
+ })
2470
3472
  }),
2471
3473
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2472
- className: FallbacksCard_module_css_default.field,
2473
- children: [
2474
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2475
- className: FallbacksCard_module_css_default.fieldLabel,
2476
- children: [
2477
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2478
- htmlFor: "fallbacks-max-switches",
2479
- children: t("maxSwitchesPerStep.label")
2480
- }),
2481
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2482
- label: t("maxSwitchesPerStep.tooltip"),
2483
- disabled: !writable
2484
- }),
2485
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2486
- className: FallbacksCard_module_css_default.defaultNote,
2487
- children: [
2488
- t("defaults.prefix"),
2489
- ": ",
2490
- state.config.maxSwitchesPerStep
2491
- ]
2492
- })
2493
- ]
2494
- }),
2495
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2496
- id: "fallbacks-max-switches",
2497
- className: FallbacksCard_module_css_default.input,
2498
- type: "number",
2499
- min: 0,
2500
- value: String(scalars.maxSwitchesPerStep),
3474
+ className: FallbacksCard_module_css_default.chainSelectors,
3475
+ children: [row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
3476
+ selector,
3477
+ catalog: catalogOf(state),
3478
+ configuredProviders: state.configuredProviders,
3479
+ disabled: !writable,
3480
+ t,
3481
+ onChange: (patch) => {
3482
+ updateRoleSelector(index, selectorIndex, patch);
3483
+ },
3484
+ onRemove: () => {
3485
+ removeRoleSelector(index, selectorIndex);
3486
+ }
3487
+ }, selectorIndex)), row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3488
+ className: FallbacksCard_module_css_default.hint,
3489
+ children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
3490
+ })]
3491
+ }),
3492
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3493
+ className: FallbacksCard_module_css_default.ruleGrid,
3494
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3495
+ className: FallbacksCard_module_css_default.ruleCell,
3496
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3497
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3498
+ children: t("roles.fallback")
3499
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3500
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3501
+ value: row.fallback,
3502
+ "aria-label": t("roles.fallback"),
2501
3503
  disabled: !writable,
2502
3504
  onChange: (event) => {
2503
- updateScalars((draft) => {
2504
- draft.maxSwitchesPerStep = parseCount(event.target.value);
2505
- });
2506
- }
2507
- }),
2508
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2509
- className: FallbacksCard_module_css_default.hint,
2510
- children: t("maxSwitchesPerStep.hint")
2511
- })
2512
- ]
3505
+ updateRoleRow(index, { fallback: event.target.value });
3506
+ },
3507
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3508
+ value: "inherit-root",
3509
+ children: t("roles.fallback.inherit-root")
3510
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3511
+ value: "none",
3512
+ children: t("roles.fallback.none")
3513
+ })]
3514
+ })]
3515
+ })
3516
+ }),
3517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3518
+ variant: "outline",
3519
+ size: "sm",
3520
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3521
+ className: FallbacksCard_module_css_default.addButton,
3522
+ onClick: () => {
3523
+ addRoleSelector(index);
3524
+ },
3525
+ children: t("roles.selector.add")
2513
3526
  }),
3527
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3528
+ className: FallbacksCard_module_css_default.cardFoot,
3529
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3530
+ type: "button",
3531
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
3532
+ "data-tip": t("roles.remove"),
3533
+ "aria-label": t("roles.remove"),
3534
+ onClick: () => {
3535
+ removeRole(index);
3536
+ },
3537
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
3538
+ })
3539
+ })
3540
+ ] })]
3541
+ }, index);
3542
+ })
3543
+ }),
3544
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3545
+ variant: "outline",
3546
+ size: "sm",
3547
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3548
+ className: FallbacksCard_module_css_default.addButton,
3549
+ onClick: addRole,
3550
+ children: t("roles.add")
3551
+ })
3552
+ ]
3553
+ }),
3554
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3555
+ className: FallbacksCard_module_css_default.field,
3556
+ role: "group",
3557
+ "aria-labelledby": "fallbacks-roles-rules",
3558
+ children: [
3559
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3560
+ className: FallbacksCard_module_css_default.fieldLabel,
3561
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3562
+ id: "fallbacks-roles-rules",
3563
+ children: t("roles.rules")
3564
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3565
+ label: t("roles.rules.tooltip"),
3566
+ disabled: !writable
3567
+ })]
3568
+ }),
3569
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3570
+ className: FallbacksCard_module_css_default.hint,
3571
+ children: t("roles.rules.hint")
3572
+ }),
3573
+ state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3574
+ className: FallbacksCard_module_css_default.hint,
3575
+ children: t("catalog.error", { message: state.catalogError })
3576
+ }),
3577
+ state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3578
+ className: FallbacksCard_module_css_default.hint,
3579
+ children: t("catalog.partial", { message: state.catalogError })
3580
+ }),
3581
+ state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3582
+ className: FallbacksCard_module_css_default.hint,
3583
+ children: t("catalog.empty")
3584
+ }),
3585
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3586
+ className: FallbacksCard_module_css_default.list,
3587
+ children: ruleRows.map((row, index) => {
3588
+ const catalog = catalogOf(state);
3589
+ const providerRaw = selectionToRaw(row.provider);
3590
+ const group = catalog?.groups.find((entry) => entry.id === providerRaw);
3591
+ const providerOutside = row.provider?.kind === "outside";
3592
+ const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !state.configuredProviders.some((entry) => entry.provider === providerRaw);
3593
+ const modelOutside = row.model?.kind === "outside";
3594
+ const roleOutside = row.role !== "" && !roleOptions.includes(row.role);
3595
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3596
+ className: FallbacksCard_module_css_default.editorCard,
3597
+ children: [
2514
3598
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2515
- className: FallbacksCard_module_css_default.field,
3599
+ className: FallbacksCard_module_css_default.ruleGrid,
2516
3600
  children: [
2517
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2518
- className: FallbacksCard_module_css_default.fieldLabel,
2519
- children: [
2520
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2521
- htmlFor: "fallbacks-always-cap",
2522
- children: t("alwaysModeRetryCap.label")
2523
- }),
2524
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2525
- label: t("alwaysModeRetryCap.tooltip"),
2526
- disabled: !writable
2527
- }),
2528
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2529
- className: FallbacksCard_module_css_default.defaultNote,
2530
- children: [
2531
- t("defaults.prefix"),
2532
- ": ",
2533
- state.config.alwaysModeRetryCap
2534
- ]
2535
- })
2536
- ]
3601
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3602
+ className: FallbacksCard_module_css_default.ruleCell,
3603
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3604
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3605
+ children: t("roles.rule.provider")
3606
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3607
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3608
+ value: providerRaw,
3609
+ onChange: (event) => {
3610
+ if (event.target.value === providerRaw) return;
3611
+ updateRuleRow(index, {
3612
+ provider: classifyProvider(event.target.value, catalog),
3613
+ model: null
3614
+ });
3615
+ },
3616
+ children: [
3617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3618
+ value: "",
3619
+ children: t("roles.rule.provider.any")
3620
+ }),
3621
+ state.configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3622
+ value: entry.provider,
3623
+ children: entry.displayName
3624
+ }, entry.provider)),
3625
+ providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3626
+ value: providerRaw,
3627
+ children: `${providerRaw}${t("catalog.unconfigured.short")}`
3628
+ }),
3629
+ providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3630
+ value: providerRaw,
3631
+ children: `${providerRaw}${t("catalog.outside.short")}`
3632
+ })
3633
+ ]
3634
+ })]
2537
3635
  }),
2538
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2539
- id: "fallbacks-always-cap",
2540
- className: FallbacksCard_module_css_default.input,
2541
- type: "number",
2542
- min: 0,
2543
- value: String(scalars.alwaysModeRetryCap),
2544
- disabled: !writable,
2545
- onChange: (event) => {
2546
- updateScalars((draft) => {
2547
- draft.alwaysModeRetryCap = parseCount(event.target.value);
2548
- });
2549
- }
3636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3637
+ className: FallbacksCard_module_css_default.ruleCell,
3638
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3639
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3640
+ children: t("roles.rule.model")
3641
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3642
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3643
+ value: selectionToRaw(row.model),
3644
+ onChange: (event) => {
3645
+ updateRuleRow(index, { model: classifyModel(providerRaw, event.target.value, catalog) });
3646
+ },
3647
+ children: [
3648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3649
+ value: "",
3650
+ children: t("roles.rule.model.any")
3651
+ }),
3652
+ (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3653
+ value: model.id,
3654
+ children: model.name
3655
+ }, model.id)),
3656
+ modelOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3657
+ value: selectionToRaw(row.model),
3658
+ children: `${selectionToRaw(row.model)}${t("catalog.outside.short")}`
3659
+ })
3660
+ ]
3661
+ })]
2550
3662
  }),
2551
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2552
- className: FallbacksCard_module_css_default.hint,
2553
- children: t("alwaysModeRetryCap.hint")
3663
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3664
+ className: FallbacksCard_module_css_default.ruleCell,
3665
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3666
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3667
+ children: t("roles.rule.role")
3668
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3669
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3670
+ value: row.role,
3671
+ disabled: !writable,
3672
+ onChange: (event) => {
3673
+ updateRuleRow(index, { role: event.target.value });
3674
+ },
3675
+ children: [
3676
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3677
+ value: "",
3678
+ children: t("roles.rule.roleSelectPlaceholder")
3679
+ }),
3680
+ roleOptions.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3681
+ value: id,
3682
+ children: id === "inherit" ? t("roles.rule.role.inherit") : id
3683
+ }, id)),
3684
+ roleOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3685
+ value: row.role,
3686
+ children: `${row.role}${t("roles.rule.roleUndeclared.short")}`
3687
+ })
3688
+ ]
3689
+ })]
2554
3690
  })
2555
3691
  ]
3692
+ }),
3693
+ (providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3694
+ className: FallbacksCard_module_css_default.hint,
3695
+ children: [t("catalog.outside.hint"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3696
+ label: t("catalog.outside.tooltip"),
3697
+ disabled: !writable
3698
+ })]
3699
+ }),
3700
+ row.role === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3701
+ className: FallbacksCard_module_css_default.hint,
3702
+ children: t("validation.ruleRoleRequired")
3703
+ }),
3704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3705
+ className: FallbacksCard_module_css_default.cardFoot,
3706
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3707
+ type: "button",
3708
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
3709
+ "data-tip": t("roles.removeRule"),
3710
+ "aria-label": t("roles.removeRule"),
3711
+ onClick: () => {
3712
+ setRuleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
3713
+ },
3714
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
3715
+ })
2556
3716
  })
2557
3717
  ]
2558
- })
2559
- ]
2560
- })]
2561
- })
2562
- ]
2563
- })
2564
- ]
2565
- }),
2566
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2567
- className: FallbacksCard_module_css_default.statusBlock,
3718
+ }, index);
3719
+ })
3720
+ }),
3721
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3722
+ variant: "outline",
3723
+ size: "sm",
3724
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3725
+ className: FallbacksCard_module_css_default.addButton,
3726
+ onClick: () => {
3727
+ setRuleRows((rows) => [...rows, {
3728
+ provider: null,
3729
+ model: null,
3730
+ role: ""
3731
+ }]);
3732
+ },
3733
+ children: t("roles.addRule")
3734
+ })
3735
+ ]
3736
+ }),
3737
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3738
+ className: FallbacksCard_module_css_default.field,
3739
+ role: "group",
3740
+ "aria-labelledby": "fallbacks-advanced",
3741
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3742
+ type: "button",
3743
+ className: FallbacksCard_module_css_default.sectionToggle,
3744
+ disabled: !writable,
3745
+ "aria-expanded": advancedVisible,
3746
+ "aria-controls": advancedVisible ? "fallbacks-advanced-body" : void 0,
3747
+ "aria-label": t(advancedVisible ? "advanced.collapse" : "advanced.expand"),
3748
+ onClick: () => {
3749
+ if (writable) setAdvancedOpen(!advancedOpen);
3750
+ },
3751
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3752
+ id: "fallbacks-advanced",
3753
+ className: FallbacksCard_module_css_default.sectionToggleText,
3754
+ children: t("advanced.label")
3755
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: advancedVisible ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron })]
3756
+ }), advancedVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3757
+ id: "fallbacks-advanced-body",
3758
+ children: [
3759
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3760
+ className: FallbacksCard_module_css_default.checkboxRow,
3761
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3762
+ className: FallbacksCard_module_css_default.checkLabel,
3763
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3764
+ className: FallbacksCard_module_css_default.checkLabelTitle,
3765
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3766
+ htmlFor: "fallbacks-role-automatch",
3767
+ children: t("roleAutoMatch.label")
3768
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3769
+ label: t("roleAutoMatch.tooltip"),
3770
+ disabled: !writable
3771
+ })]
3772
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3773
+ className: FallbacksCard_module_css_default.checkLabelDesc,
3774
+ children: t("roleAutoMatch.hint")
3775
+ })]
3776
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3777
+ id: "fallbacks-role-automatch",
3778
+ type: "checkbox",
3779
+ className: FallbacksCard_module_css_default.checkbox,
3780
+ checked: scalars.roleAutoMatch,
3781
+ disabled: !writable,
3782
+ onChange: (event) => {
3783
+ updateScalars((draft) => {
3784
+ draft.roleAutoMatch = event.target.checked;
3785
+ });
3786
+ }
3787
+ })]
3788
+ }),
3789
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3790
+ className: FallbacksCard_module_css_default.field,
3791
+ role: "group",
3792
+ "aria-labelledby": "fallbacks-trigger-codes",
3793
+ children: [
3794
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3795
+ className: FallbacksCard_module_css_default.fieldLabel,
3796
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3797
+ id: "fallbacks-trigger-codes",
3798
+ children: t("triggerCodes.label")
3799
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3800
+ label: t("triggerCodes.tooltip"),
3801
+ disabled: !writable
3802
+ })]
3803
+ }),
3804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3805
+ className: FallbacksCard_module_css_default.hint,
3806
+ children: t("triggerCodes.hint")
3807
+ }),
3808
+ KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3809
+ className: FallbacksCard_module_css_default.optionRow,
3810
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3811
+ type: "checkbox",
3812
+ checked: scalars.triggerCodes.includes(code),
3813
+ onChange: (event) => {
3814
+ updateScalars((draft) => {
3815
+ draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
3816
+ });
3817
+ }
3818
+ }), t(TRIGGER_CODE_LABELS[code])]
3819
+ }, code)),
3820
+ unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3821
+ className: FallbacksCard_module_css_default.hint,
3822
+ children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
3823
+ })
3824
+ ]
3825
+ }),
3826
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3827
+ className: FallbacksCard_module_css_default.field,
3828
+ role: "group",
3829
+ "aria-labelledby": "fallbacks-revert-policy",
3830
+ children: [
3831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3832
+ className: FallbacksCard_module_css_default.fieldLabel,
3833
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3834
+ id: "fallbacks-revert-policy",
3835
+ children: t("revertPolicy.label")
3836
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3837
+ label: t("revertPolicy.tooltip"),
3838
+ disabled: !writable
3839
+ })]
3840
+ }),
3841
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3842
+ className: FallbacksCard_module_css_default.hint,
3843
+ children: t("revertPolicy.hint")
3844
+ }),
3845
+ ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3846
+ className: FallbacksCard_module_css_default.optionRow,
3847
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3848
+ type: "radio",
3849
+ name: "fallbacks-revert-policy",
3850
+ checked: scalars.revertPolicy === policy,
3851
+ onChange: () => {
3852
+ updateScalars((draft) => {
3853
+ draft.revertPolicy = policy;
3854
+ });
3855
+ }
3856
+ }), t(`revertPolicy.${policy}`)]
3857
+ }, policy))
3858
+ ]
3859
+ }),
3860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3861
+ className: FallbacksCard_module_css_default.numberFields,
3862
+ children: [
3863
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3864
+ className: FallbacksCard_module_css_default.field,
3865
+ children: [
3866
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3867
+ className: FallbacksCard_module_css_default.fieldLabel,
3868
+ children: [
3869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3870
+ htmlFor: "fallbacks-cooldown-ms",
3871
+ children: t("cooldownMs.label")
3872
+ }),
3873
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3874
+ label: t("cooldownMs.tooltip"),
3875
+ disabled: !writable
3876
+ }),
3877
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3878
+ className: FallbacksCard_module_css_default.defaultNote,
3879
+ children: [
3880
+ t("defaults.prefix"),
3881
+ ": ",
3882
+ state.config.cooldownMs
3883
+ ]
3884
+ })
3885
+ ]
3886
+ }),
3887
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3888
+ id: "fallbacks-cooldown-ms",
3889
+ className: FallbacksCard_module_css_default.input,
3890
+ type: "number",
3891
+ min: 0,
3892
+ value: String(scalars.cooldownMs),
3893
+ disabled: !writable,
3894
+ onChange: (event) => {
3895
+ updateScalars((draft) => {
3896
+ draft.cooldownMs = parseCount(event.target.value);
3897
+ });
3898
+ }
3899
+ }),
3900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3901
+ className: FallbacksCard_module_css_default.hint,
3902
+ children: t("cooldownMs.hint")
3903
+ })
3904
+ ]
3905
+ }),
3906
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3907
+ className: FallbacksCard_module_css_default.field,
3908
+ children: [
3909
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3910
+ className: FallbacksCard_module_css_default.fieldLabel,
3911
+ children: [
3912
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3913
+ htmlFor: "fallbacks-max-switches",
3914
+ children: t("maxSwitchesPerStep.label")
3915
+ }),
3916
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3917
+ label: t("maxSwitchesPerStep.tooltip"),
3918
+ disabled: !writable
3919
+ }),
3920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3921
+ className: FallbacksCard_module_css_default.defaultNote,
3922
+ children: [
3923
+ t("defaults.prefix"),
3924
+ ": ",
3925
+ state.config.maxSwitchesPerStep
3926
+ ]
3927
+ })
3928
+ ]
3929
+ }),
3930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3931
+ id: "fallbacks-max-switches",
3932
+ className: FallbacksCard_module_css_default.input,
3933
+ type: "number",
3934
+ min: 0,
3935
+ value: String(scalars.maxSwitchesPerStep),
3936
+ disabled: !writable,
3937
+ onChange: (event) => {
3938
+ updateScalars((draft) => {
3939
+ draft.maxSwitchesPerStep = parseCount(event.target.value);
3940
+ });
3941
+ }
3942
+ }),
3943
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3944
+ className: FallbacksCard_module_css_default.hint,
3945
+ children: t("maxSwitchesPerStep.hint")
3946
+ })
3947
+ ]
3948
+ }),
3949
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3950
+ className: FallbacksCard_module_css_default.field,
3951
+ children: [
3952
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3953
+ className: FallbacksCard_module_css_default.fieldLabel,
3954
+ children: [
3955
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3956
+ htmlFor: "fallbacks-always-cap",
3957
+ children: t("alwaysModeRetryCap.label")
3958
+ }),
3959
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3960
+ label: t("alwaysModeRetryCap.tooltip"),
3961
+ disabled: !writable
3962
+ }),
3963
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3964
+ className: FallbacksCard_module_css_default.defaultNote,
3965
+ children: [
3966
+ t("defaults.prefix"),
3967
+ ": ",
3968
+ state.config.alwaysModeRetryCap
3969
+ ]
3970
+ })
3971
+ ]
3972
+ }),
3973
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3974
+ id: "fallbacks-always-cap",
3975
+ className: FallbacksCard_module_css_default.input,
3976
+ type: "number",
3977
+ min: 0,
3978
+ value: String(scalars.alwaysModeRetryCap),
3979
+ disabled: !writable,
3980
+ onChange: (event) => {
3981
+ updateScalars((draft) => {
3982
+ draft.alwaysModeRetryCap = parseCount(event.target.value);
3983
+ });
3984
+ }
3985
+ }),
3986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3987
+ className: FallbacksCard_module_css_default.hint,
3988
+ children: t("alwaysModeRetryCap.hint")
3989
+ })
3990
+ ]
3991
+ })
3992
+ ]
3993
+ }),
3994
+ validationErrors.advanced.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3995
+ className: FallbacksCard_module_css_default.error,
3996
+ role: "alert",
3997
+ children: `${t("validation.blocked")}${validationErrors.advanced.join("; ")}`
3998
+ }),
3999
+ lastSaveSection === "advanced" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4000
+ className: FallbacksCard_module_css_default.error,
4001
+ role: "alert",
4002
+ children: t("error.generic", { message: state.error })
4003
+ }),
4004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4005
+ className: FallbacksCard_module_css_default.sectionActions,
4006
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4007
+ type: "button",
4008
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
4009
+ disabled: !advancedDirty || saving,
4010
+ onClick: () => {
4011
+ discardSection("advanced");
4012
+ },
4013
+ children: t("discard")
4014
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4015
+ type: "button",
4016
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
4017
+ disabled: !writable || saving || !advancedDirty,
4018
+ onClick: () => {
4019
+ save("advanced");
4020
+ },
4021
+ children: saving ? t("save.saving") : t("save")
4022
+ })]
4023
+ })
4024
+ ]
4025
+ })]
4026
+ })
4027
+ ]
4028
+ })
4029
+ ]
4030
+ }),
4031
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4032
+ className: FallbacksCard_module_css_default.statusBlock,
4033
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4034
+ className: FallbacksCard_module_css_default.statusTitle,
4035
+ children: t("status.title")
4036
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4037
+ className: FallbacksCard_module_css_default.statusLine,
4038
+ role: state.switchesStatus === "error" ? "alert" : void 0,
2568
4039
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2569
- className: FallbacksCard_module_css_default.statusTitle,
2570
- children: t("status.title")
2571
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2572
- className: FallbacksCard_module_css_default.statusLine,
2573
- role: state.switchesStatus === "error" ? "alert" : void 0,
2574
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2575
- className: FallbacksCard_module_css_default.statusLineLabel,
2576
- children: t("status.switches.label")
2577
- }), switchesLine]
2578
- })]
2579
- }),
2580
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2581
- className: FallbacksCard_module_css_default.footer,
2582
- children: [
2583
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2584
- type: "button",
2585
- className: FallbacksCard_module_css_default.secondaryButton,
2586
- disabled: !dirty || saving,
2587
- onClick: discard,
2588
- children: t("discard")
2589
- }),
2590
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2591
- type: "button",
2592
- className: FallbacksCard_module_css_default.secondaryButton,
2593
- disabled: !writable || saving,
2594
- onClick: () => {
2595
- setConfirmingReset(true);
2596
- },
2597
- children: t("reset")
2598
- }),
2599
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2600
- type: "button",
2601
- className: FallbacksCard_module_css_default.primaryButton,
2602
- disabled: !writable || saving || !dirty,
2603
- onClick: save,
2604
- children: saving ? t("save.saving") : t("save")
2605
- })
2606
- ]
2607
- })
2608
- ]
2609
- }),
2610
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2611
- open: confirmingReset,
2612
- onClose: () => {
2613
- if (!resetting) setConfirmingReset(false);
2614
- },
2615
- title: t("reset.confirmTitle"),
2616
- closeLabel: t("close"),
2617
- description: t("reset.confirm"),
2618
- className: FallbacksCard_module_css_default.resetDialog,
2619
- footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2620
- variant: "outline",
2621
- autoFocus: true,
2622
- disabled: resetting,
2623
- onClick: () => {
2624
- setConfirmingReset(false);
2625
- },
2626
- children: t("reset.confirm.cancel")
2627
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2628
- variant: "outline",
2629
- className: FallbacksCard_module_css_default.confirmDanger,
2630
- disabled: resetting,
2631
- onClick: confirmReset,
2632
- children: resetting ? t("reset.saving") : t("reset.confirm.action")
2633
- })] })
2634
- })
2635
- ]
4040
+ className: FallbacksCard_module_css_default.statusLineLabel,
4041
+ children: t("status.switches.label")
4042
+ }), switchesLine]
4043
+ })]
4044
+ })
4045
+ ]
4046
+ })]
2636
4047
  });
2637
4048
  }
2638
4049
  //#endregion
@@ -2935,7 +4346,7 @@ window.__ModuleLoader__.load({
2935
4346
  const connection = ctx.get("connection");
2936
4347
  const sessions = ctx.get("sessions");
2937
4348
  const controller = new FallbacksSettingsController(connection.api, connection.rpc);
2938
- const useSnapshot = (0, _deepseek_ai_dsh_client_web_react.bindSnapshotSelector)(controller.store);
4349
+ const useSnapshot = bindSnapshotSelector(controller.store);
2939
4350
  ctx.effect(() => {
2940
4351
  const syncSession = () => {
2941
4352
  controller.setCurrentSession(sessions?.list.getSnapshot().current);