dsh-llm-fallbacks 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,6 +38,238 @@ window.__ModuleLoader__.load({
38
38
  raw: trimmed
39
39
  };
40
40
  }
41
+ /**
42
+ * Frozen preset windows (UTC+8). `liang-*` presets have NO day mask (they
43
+ * apply every day, weekends included); `glm-peak` is Monday–Friday only.
44
+ * The two valleys are `complement: true` of their peak.
45
+ */
46
+ const PRESETS = {
47
+ "liang-peak": {
48
+ windows: [{
49
+ start: "09:00",
50
+ end: "12:00"
51
+ }, {
52
+ start: "14:00",
53
+ end: "18:00"
54
+ }],
55
+ complement: false,
56
+ label: "Liang Peak"
57
+ },
58
+ "liang-valley": {
59
+ windows: [{
60
+ start: "09:00",
61
+ end: "12:00"
62
+ }, {
63
+ start: "14:00",
64
+ end: "18:00"
65
+ }],
66
+ complement: true,
67
+ label: "Liang Valley"
68
+ },
69
+ "glm-peak": {
70
+ windows: [{
71
+ start: "14:00",
72
+ end: "18:00",
73
+ days: [
74
+ 1,
75
+ 2,
76
+ 3,
77
+ 4,
78
+ 5
79
+ ]
80
+ }],
81
+ complement: false,
82
+ label: "GLM Peak"
83
+ },
84
+ "glm-valley": {
85
+ windows: [{
86
+ start: "14:00",
87
+ end: "18:00",
88
+ days: [
89
+ 1,
90
+ 2,
91
+ 3,
92
+ 4,
93
+ 5
94
+ ]
95
+ }],
96
+ complement: true,
97
+ label: "GLM Valley"
98
+ }
99
+ };
100
+ const HHMM_RE$1 = /^([01]\d|2[0-3]):[0-5]\d$/;
101
+ const WEEKDAY_INDEX = {
102
+ Sun: 0,
103
+ Mon: 1,
104
+ Tue: 2,
105
+ Wed: 3,
106
+ Thu: 4,
107
+ Fri: 5,
108
+ Sat: 6
109
+ };
110
+ /** Malformed rows warn once per row INSTANCE (config snapshots are stable
111
+ * across requests — this is what keeps "warn once" from becoming spam). */
112
+ const warnedMalformedRows = /* @__PURE__ */ new WeakSet();
113
+ /** Invalid `tz` values warn once per distinct value. */
114
+ const warnedTimeZones = /* @__PURE__ */ new Set();
115
+ function warnMalformed(row, reason) {
116
+ if (typeof row !== "object" || row === null) {
117
+ console.warn(`llm-fallbacks: skipping malformed time-slot row (${reason})`);
118
+ return;
119
+ }
120
+ if (warnedMalformedRows.has(row)) return;
121
+ warnedMalformedRows.add(row);
122
+ console.warn(`llm-fallbacks: skipping malformed time-slot row (${reason}): ${JSON.stringify(row)}`);
123
+ }
124
+ /** Wall-clock weekday (0=Sunday) + minutes-since-midnight of `now` in `tz`
125
+ * (standard `Intl` timezone rules, DST-safe). */
126
+ function wallClock(now, tz) {
127
+ try {
128
+ const parts = new Intl.DateTimeFormat("en-US", {
129
+ timeZone: tz,
130
+ weekday: "short",
131
+ hour: "2-digit",
132
+ minute: "2-digit",
133
+ hourCycle: "h23"
134
+ }).formatToParts(now);
135
+ let weekday = 0;
136
+ let minutes = 0;
137
+ for (const part of parts) if (part.type === "weekday") weekday = WEEKDAY_INDEX[part.value] ?? 0;
138
+ else if (part.type === "hour") minutes += Number(part.value) * 60;
139
+ else if (part.type === "minute") minutes += Number(part.value);
140
+ return {
141
+ weekday,
142
+ minutes
143
+ };
144
+ } catch (error) {
145
+ if (!warnedTimeZones.has(tz)) {
146
+ warnedTimeZones.add(tz);
147
+ console.warn(`llm-fallbacks: invalid timezone "${tz}" (${error.message}) — slot matching falls back to UTC`);
148
+ }
149
+ return {
150
+ weekday: now.getUTCDay(),
151
+ minutes: now.getUTCHours() * 60 + now.getUTCMinutes()
152
+ };
153
+ }
154
+ }
155
+ /** `HH:mm` → minutes-since-midnight (inputs are regex-validated or frozen
156
+ * constants — this never sees garbage). */
157
+ function minutesOf(hhmm) {
158
+ const [hour, minute] = hhmm.split(":").map(Number);
159
+ return hour * 60 + minute;
160
+ }
161
+ /** P4 containment rule: day mask matches AND the window contains `t`
162
+ * (`end` exclusive; `start > end` wraps midnight — custom rows only). */
163
+ function containsWindow(window, clock) {
164
+ if (!(window.days === void 0 || window.days.length === 0 || window.days.includes(clock.weekday))) return false;
165
+ const start = minutesOf(window.start);
166
+ const end = minutesOf(window.end);
167
+ return start <= end ? start <= clock.minutes && clock.minutes < end : start <= clock.minutes || clock.minutes < end;
168
+ }
169
+ function matchesAnyWindow(windows, clock) {
170
+ return windows.some((window) => containsWindow(window, clock));
171
+ }
172
+ /** Validate one stored row → frozen windows, or `undefined` (warn once +
173
+ * skip). Preset rows reject stored windows/day masks (P4); custom rows
174
+ * require strict `HH:mm` bounds; chains must be non-empty. */
175
+ function describeRow(row) {
176
+ if (!Array.isArray(row.chain) || row.chain.length === 0) {
177
+ warnMalformed(row, "empty chain");
178
+ return;
179
+ }
180
+ if (row.kind === "preset") {
181
+ const preset = row.preset;
182
+ if (typeof preset !== "string" || !Object.hasOwn(PRESETS, preset)) {
183
+ warnMalformed(row, `unknown preset ${JSON.stringify(preset)}`);
184
+ return;
185
+ }
186
+ if (row.start !== void 0 || row.end !== void 0 || row.days !== void 0 && row.days.length > 0) {
187
+ warnMalformed(row, `preset windows are fixed — row "${preset}" cannot carry start/end/days`);
188
+ return;
189
+ }
190
+ return PRESETS[preset];
191
+ }
192
+ if (row.kind === "custom") {
193
+ const { start, end } = row;
194
+ if (typeof start !== "string" || typeof end !== "string" || !HHMM_RE$1.test(start) || !HHMM_RE$1.test(end)) {
195
+ warnMalformed(row, `invalid custom window ${JSON.stringify(start)}-${JSON.stringify(end)} (expected HH:mm)`);
196
+ return;
197
+ }
198
+ return {
199
+ windows: [{
200
+ start,
201
+ end,
202
+ days: row.days
203
+ }],
204
+ complement: false
205
+ };
206
+ }
207
+ warnMalformed(row, `unknown kind ${JSON.stringify(row.kind)}`);
208
+ }
209
+ /** Display label for a winning row (preset rows use the frozen label;
210
+ * custom rows prefer their display name, falling back to the window). */
211
+ function labelOf(row) {
212
+ if (row.kind === "preset" && typeof row.preset === "string" && Object.hasOwn(PRESETS, row.preset)) return PRESETS[row.preset].label;
213
+ return row.name !== void 0 && row.name.trim() !== "" ? row.name : `custom ${row.start}-${row.end}`;
214
+ }
215
+ /**
216
+ * All-day conformance (P6): the all-day chain is conforming when its LAST
217
+ * entry (the tail — the card's 默认模型 panel) is exactly one official V4
218
+ * model — Flash XOR Pro. Leading entries (the card's 默认降级链 block) are
219
+ * the ordered walk before that last-resort fallback. An empty chain or a
220
+ * chain whose tail is not an official V4 model keeps slot rows inert and
221
+ * refuses the virtual-row override/delegate; the v0.2.2 failure walk over
222
+ * the raw chain stays verbatim.
223
+ */
224
+ function isAllDayConforming(chain) {
225
+ if (chain.length < 1) return false;
226
+ const tail = chain[chain.length - 1];
227
+ return tail === "deepseek-official/deepseek-v4-flash" || tail === "deepseek-official/deepseek-v4-pro";
228
+ }
229
+ /**
230
+ * Slot winner + display label (P5): drives 分时切换 detection (per-root-agent
231
+ * last-winner marker, in-process only) and the card / `/fallbacks` status
232
+ * strip. `winner` is the matching row or `'all-day'`; `label` names the
233
+ * slot (frozen preset label or `custom HH:mm-HH:mm`).
234
+ *
235
+ * P6 gate (qc1 F-001): without a conforming all-day
236
+ * (`isAllDayConforming(config.rootChain)`) the winner is ALWAYS `'all-day'`
237
+ * — a legacy multi-model (or empty) chain earns no slot rows, so every
238
+ * surface fed by this resolver reports the inert state and routing stays on
239
+ * the raw `rootChain` (the v0.2.2 walk verbatim).
240
+ */
241
+ function resolveSlotState(config, now, tz) {
242
+ if (!isAllDayConforming(config.rootChain)) return {
243
+ winner: "all-day",
244
+ label: "all-day"
245
+ };
246
+ const clock = wallClock(now, tz);
247
+ const seenPresets = /* @__PURE__ */ new Set();
248
+ const rows = Array.isArray(config.timeSlots) ? config.timeSlots : [];
249
+ for (const row of rows) {
250
+ if (typeof row !== "object" || row === null) {
251
+ warnMalformed(row, "row is not an object");
252
+ continue;
253
+ }
254
+ if (row.kind === "preset" && typeof row.preset === "string") {
255
+ if (seenPresets.has(row.preset)) {
256
+ warnMalformed(row, `duplicate preset "${row.preset}" — only the first row takes effect`);
257
+ continue;
258
+ }
259
+ seenPresets.add(row.preset);
260
+ }
261
+ const descriptor = describeRow(row);
262
+ if (descriptor === void 0) continue;
263
+ if (descriptor.complement ? !matchesAnyWindow(descriptor.windows, clock) : matchesAnyWindow(descriptor.windows, clock)) return {
264
+ winner: row,
265
+ label: labelOf(row)
266
+ };
267
+ }
268
+ return {
269
+ winner: "all-day",
270
+ label: "all-day"
271
+ };
272
+ }
41
273
  //#endregion
42
274
  //#region src/config.ts
43
275
  /**
@@ -64,7 +296,9 @@ window.__ModuleLoader__.load({
64
296
  maxSwitchesPerStep: 8,
65
297
  alwaysModeRetryCap: 5,
66
298
  presets: "bundled",
67
- roleAutoMatch: true
299
+ roleAutoMatch: true,
300
+ timeSlots: [],
301
+ tz: "Asia/Shanghai"
68
302
  };
69
303
  /**
70
304
  * Reserved role id: legal as a rule target (`roles.rules[].role`) and as
@@ -115,6 +349,15 @@ window.__ModuleLoader__.load({
115
349
  return typeof entry.id === "string" && typeof entry.overridden === "boolean";
116
350
  });
117
351
  }
352
+ /** Seed-default persona from a revert-seed wire body (issue #59). */
353
+ function revertOutcomePersona(value) {
354
+ if (value === null || typeof value !== "object" || !("outcome" in value)) return void 0;
355
+ const outcome = value.outcome;
356
+ if (outcome === null || typeof outcome !== "object") return void 0;
357
+ if (!("reverted" in outcome) || outcome.reverted !== true) return void 0;
358
+ if (!("persona" in outcome) || typeof outcome.persona !== "string") return void 0;
359
+ return outcome.persona;
360
+ }
118
361
  /**
119
362
  * The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
120
363
  * settings profile resolves in the describe namespaces — the Models page's
@@ -195,6 +438,23 @@ window.__ModuleLoader__.load({
195
438
  if (enabled !== void 0 && typeof enabled !== "boolean") throw new TypeError("fallbacks descriptor enabled must be a boolean");
196
439
  const roleAutoMatch = value.roleAutoMatch;
197
440
  if (roleAutoMatch !== void 0 && typeof roleAutoMatch !== "boolean") throw new TypeError("fallbacks descriptor roleAutoMatch must be a boolean");
441
+ const timeSlots = value.timeSlots;
442
+ if (timeSlots !== void 0 && (!Array.isArray(timeSlots) || timeSlots.some((row) => {
443
+ if (!isRecord(row)) return true;
444
+ for (const field of [
445
+ "kind",
446
+ "preset",
447
+ "start",
448
+ "end"
449
+ ]) if (row[field] !== void 0 && typeof row[field] !== "string") return true;
450
+ const chain = row.chain;
451
+ if (chain !== void 0 && (!Array.isArray(chain) || chain.some((entry) => typeof entry !== "string"))) return true;
452
+ const days = row.days;
453
+ if (days !== void 0 && (!Array.isArray(days) || days.some((day) => typeof day !== "number"))) return true;
454
+ return false;
455
+ }))) throw new TypeError("fallbacks descriptor timeSlots must be an array of slot rows (kind/preset/start/end strings, chain string array, days number array)");
456
+ const tz = value.tz;
457
+ if (tz !== void 0 && typeof tz !== "string") throw new TypeError("fallbacks descriptor tz must be a string");
198
458
  return {
199
459
  enabled: enabled ?? defaultFallbacksConfig.enabled,
200
460
  triggerCodes: triggerCodes ?? [...defaultFallbacksConfig.triggerCodes],
@@ -208,7 +468,9 @@ window.__ModuleLoader__.load({
208
468
  maxSwitchesPerStep: maxSwitchesPerStep ?? defaultFallbacksConfig.maxSwitchesPerStep,
209
469
  alwaysModeRetryCap: alwaysModeRetryCap ?? defaultFallbacksConfig.alwaysModeRetryCap,
210
470
  presets: presets ?? defaultFallbacksConfig.presets,
211
- roleAutoMatch: roleAutoMatch ?? defaultFallbacksConfig.roleAutoMatch
471
+ roleAutoMatch: roleAutoMatch ?? defaultFallbacksConfig.roleAutoMatch,
472
+ timeSlots: timeSlots ?? [...defaultFallbacksConfig.timeSlots ?? []],
473
+ tz: tz ?? defaultFallbacksConfig.tz
212
474
  };
213
475
  }
214
476
  /** The raw selector string a selection serializes to ('' when empty). */
@@ -310,13 +572,54 @@ window.__ModuleLoader__.load({
310
572
  }
311
573
  return entries;
312
574
  }
575
+ /** Project the time-slot rows into editable rows (chain selectors classified). */
576
+ function timeSlotsToRows(timeSlots, catalog) {
577
+ return timeSlots.map((row) => ({
578
+ kind: row.kind,
579
+ ...row.preset === void 0 ? {} : { preset: row.preset },
580
+ start: row.start ?? "",
581
+ end: row.end ?? "",
582
+ days: [...row.days ?? []],
583
+ name: row.name ?? "",
584
+ collapsed: true,
585
+ selectors: (row.chain ?? []).map((entry) => entryToSelectorRow(entry, catalog))
586
+ }));
587
+ }
588
+ /** Rebuild the time-slot rows from edited rows; blank selectors drop out.
589
+ * `kind` rides verbatim (a hand-written unknown kind reads back unchanged;
590
+ * save validation rejects it) — the cast asserts the trusted editor shape.
591
+ * `days` is ALWAYS serialized ([] included): schemastery composes absent
592
+ * array fields as `[]`, so the composed config every card load accepts
593
+ * carries `days` on every row — the draft must too, or a clean card would
594
+ * read back dirty. */
595
+ function rowsToTimeSlots(rows) {
596
+ return rows.map((row) => {
597
+ const chain = row.selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
598
+ if (row.kind === "preset") return {
599
+ kind: "preset",
600
+ preset: row.preset,
601
+ days: row.days,
602
+ chain
603
+ };
604
+ return {
605
+ kind: row.kind,
606
+ ...row.preset === void 0 ? {} : { preset: row.preset },
607
+ start: row.start,
608
+ end: row.end,
609
+ days: row.days,
610
+ ...row.name === "" ? {} : { name: row.name },
611
+ chain
612
+ };
613
+ });
614
+ }
313
615
  /** Project the declared roles into editable rows (chain selectors classified). */
314
616
  function rolesToRows(roles, catalog) {
315
617
  return roles.map((role) => ({
316
618
  id: role.id,
317
619
  persona: role.persona,
318
620
  selectors: (role.chain ?? []).map((entry) => entryToSelectorRow(entry, catalog)),
319
- fallback: role.fallback ?? "inherit-root"
621
+ fallback: role.fallback ?? "inherit-root",
622
+ collapsed: true
320
623
  }));
321
624
  }
322
625
  /** Rebuild the declared roles from edited rows; empty selectors drop out. */
@@ -365,16 +668,14 @@ window.__ModuleLoader__.load({
365
668
  /** Project the role rules into editable rows (provider/model classified). */
366
669
  function rulesToRows(rules, catalog) {
367
670
  return rules.map((rule) => ({
368
- origin: rule.origin ?? "",
369
671
  provider: classifyProvider(rule.provider ?? "", catalog),
370
672
  model: classifyModel(rule.provider ?? "", rule.model ?? "", catalog),
371
673
  role: rule.role
372
674
  }));
373
675
  }
374
- /** Rebuild the role rules from edited rows; empty origin/provider/model drop out. */
676
+ /** Rebuild the role rules from edited rows; empty provider/model drop out. */
375
677
  function rowsToRules(rows) {
376
678
  return rows.map((row) => ({
377
- ...row.origin === "" ? {} : { origin: row.origin },
378
679
  ...row.provider === null ? {} : { provider: selectionToRaw(row.provider) },
379
680
  ...row.model === null ? {} : { model: selectionToRaw(row.model) },
380
681
  role: row.role.trim()
@@ -658,12 +959,17 @@ window.__ModuleLoader__.load({
658
959
  * reason }` outcome is still a successful RPC — the post-write read
659
960
  * result (config / legacyKeys / seeds) lands either way, and the revert
660
961
  * button stays disabled while the write is in flight.
962
+ *
963
+ * Returns the seed-default persona when the outcome is `{ reverted:
964
+ * true, persona }` — including the persist no-op (persisted already
965
+ * equals the seed). The card applies that string to the row's **draft**
966
+ * so an unsaved persona edit still snaps back (issue #59).
661
967
  * @param id - the seeded role id; the host matches it by trimmed id
662
968
  * against the seed registry (spec §9.3).
663
969
  */
664
970
  async revertSeed(id) {
665
971
  const state = this.store.getSnapshot();
666
- if (!state.writable || state.status === "saving") return;
972
+ if (!state.writable || state.status === "saving") return void 0;
667
973
  const generation = ++this.writeGeneration;
668
974
  this.store.update((draft) => {
669
975
  draft.status = "saving";
@@ -671,7 +977,7 @@ window.__ModuleLoader__.load({
671
977
  });
672
978
  try {
673
979
  const result = await this.rpc.call("/api", "fallbacks/revert-seed", { args: { id } });
674
- if (generation !== this.writeGeneration) return;
980
+ if (generation !== this.writeGeneration) return void 0;
675
981
  if (!result.ok) throw result.error;
676
982
  const value = result.value;
677
983
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -686,9 +992,11 @@ window.__ModuleLoader__.load({
686
992
  if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
687
993
  }
688
994
  this.accept(config, true, legacyKeys, seeds);
995
+ return revertOutcomePersona(value);
689
996
  } catch (error) {
690
- if (generation !== this.writeGeneration) return;
997
+ if (generation !== this.writeGeneration) return void 0;
691
998
  this.fail(error);
999
+ return;
692
1000
  }
693
1001
  }
694
1002
  /** Stop in-flight responses from publishing after plugin disposal. */
@@ -817,16 +1125,62 @@ window.__ModuleLoader__.load({
817
1125
  "roleAutoMatch.label": "启用角色自动匹配",
818
1126
  "roleAutoMatch.hint": "规则未命中时,由模型自选最贴近的已声明角色",
819
1127
  "roleAutoMatch.tooltip": "规则未命中时,模型会自动从已声明角色(id + persona)中选择最匹配者并注入该角色的链;关闭后未命中规则时按现状回落(inherit / rootChain)。",
820
- "rootChain.label": "root 主代理降级链",
821
- "rootChain.firstLine": "仅当当前会话所选模型失败后才启用降级——不抢占会话模型",
822
- "rootChain.hint": "优先使用当前会话的模型;失败后才按此链降级",
823
- "rootChain.tooltip": "root 主代理失败时按此有序选择器列表依次降级;未配置时行为与未安装插件一致。",
824
- "rootChain.selector.add": "添加选择器",
1128
+ "rootChain.label": "默认降级链",
1129
+ "rootChain.tooltip": "未命中任何分时槽时先走这条降级链;全部失败后落到下面的默认模型。",
1130
+ "defaultModel.label": "默认模型",
825
1131
  "chains.selector.remove": "删除该选择器",
826
1132
  "chains.selector.providerPlaceholder": "选择 provider",
827
1133
  "chains.selector.modelPlaceholder": "选择 model",
828
1134
  "chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
829
1135
  "chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
1136
+ "mainAgent.label": "主代理",
1137
+ "subagents.label": "子代理",
1138
+ "timeSlots.label": "分时槽设置",
1139
+ "timeSlots.hint": "自上而下第一条命中生效;全时段行固定最后",
1140
+ "timeSlots.tz.label": "时区(UTC±)",
1141
+ "timeSlots.drag": "拖拽排序(或使用上下按钮)",
1142
+ "timeSlots.name": "名称",
1143
+ "timeSlots.expand": "展开该行",
1144
+ "timeSlots.collapse": "收起该行",
1145
+ "timeSlots.tooltip": "命中行的模型链成为 root 生效链(取代全时段链);未命中任何行时使用全时段链。分时切换是路由种子而非失败决策:不消耗冷却、不计入单步切换上限。",
1146
+ "timeSlots.addPreset": "添加预设",
1147
+ "timeSlots.addCustom": "添加自定义时段",
1148
+ "timeSlots.presetPlaceholder": "选择预设",
1149
+ "timeSlots.remove": "删除该时段行",
1150
+ "timeSlots.moveUp": "上移该时段行",
1151
+ "timeSlots.moveDown": "下移该时段行",
1152
+ "timeSlots.start": "开始(HH:mm)",
1153
+ "timeSlots.end": "结束(HH:mm)",
1154
+ "timeSlots.days": "星期",
1155
+ "timeSlots.days.hint": "不勾选 = 每天;可跨午夜",
1156
+ "timeSlots.preset.name": "预设",
1157
+ "timeSlots.preset.windowLabel": "时段(只读)",
1158
+ "timeSlots.preset.chainsOnly": "预设窗口已锁定:仅可编辑模型链",
1159
+ "timeSlots.preset.liang-peak.label": "梁文峰",
1160
+ "timeSlots.preset.liang-peak.window": "09:00–12:00 与 14:00–18:00(每天,UTC+8)",
1161
+ "timeSlots.preset.liang-valley.label": "梁文谷",
1162
+ "timeSlots.preset.liang-valley.window": "Liang Peak 之外的所有时间(每天,UTC+8)",
1163
+ "timeSlots.preset.glm-peak.label": "GLM峰",
1164
+ "timeSlots.preset.glm-peak.window": "周一至周五 14:00–18:00(UTC+8)",
1165
+ "timeSlots.preset.glm-valley.label": "GLM谷",
1166
+ "timeSlots.preset.glm-valley.window": "GLM Peak 之外的所有时间(UTC+8)",
1167
+ "timeSlots.preset.glm.note": "仅配置了 zai-coding-cn 时有效",
1168
+ "timeSlots.preset.highCost": "高消耗",
1169
+ "timeSlots.preset.multiplier": "x{n}",
1170
+ "timeSlots.active": "激活",
1171
+ "timeSlots.preset.glm.unconfigured": "(需配置 zai-coding-cn)",
1172
+ "timeSlots.day.sun": "日",
1173
+ "timeSlots.day.mon": "一",
1174
+ "timeSlots.day.tue": "二",
1175
+ "timeSlots.day.wed": "三",
1176
+ "timeSlots.day.thu": "四",
1177
+ "timeSlots.day.fri": "五",
1178
+ "timeSlots.day.sat": "六",
1179
+ "timeSlots.selector.add": "添加选择器",
1180
+ "allDay.hint": "全天链的最后一档兜底:官方 V4 Flash 或 V4 Pro 二选一",
1181
+ "allDay.flash": "官方 V4 Flash(deepseek-official/deepseek-v4-flash)",
1182
+ "allDay.pro": "官方 V4 Pro(deepseek-official/deepseek-v4-pro)",
1183
+ "allDay.nonconforming": "当前默认模型不合法:请选择官方 V4 Flash 或 V4 Pro 后保存",
830
1184
  "roles.list.label": "角色实体",
831
1185
  "roles.list.hint": "先声明角色,规则才能引用",
832
1186
  "roles.list.tooltip": "角色 id 须匹配 /^[a-z0-9-]{1,32}$/ 且唯一;\"inherit\" 为保留字,不能用作角色 id。",
@@ -844,14 +1198,12 @@ window.__ModuleLoader__.load({
844
1198
  "roles.fallback.none": "仅角色链(不追加 rootChain)",
845
1199
  "roles.add": "添加角色",
846
1200
  "roles.remove": "删除该角色",
1201
+ "roles.expand": "展开该角色",
1202
+ "roles.collapse": "收起该角色",
847
1203
  "roles.selector.add": "添加选择器",
848
1204
  "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",
1205
+ "roles.rules.hint": "仅对子代理生效:顺序匹配 provider/model,未命中 → inherit(root 链)",
1206
+ "roles.rules.tooltip": "规则仅对子代理生效(root 请求不匹配规则):命中后走对应角色的链;未命中走内置 inherit(rootChain)。",
855
1207
  "roles.rule.provider": "provider",
856
1208
  "roles.rule.provider.any": "任意",
857
1209
  "roles.rule.model": "model",
@@ -869,6 +1221,14 @@ window.__ModuleLoader__.load({
869
1221
  "validation.ruleRoleUndeclared": "规则引用了未声明的角色 \"{role}\"",
870
1222
  "validation.ruleRoleRequired": "规则未选择角色:请选择目标角色,或删除该行",
871
1223
  "validation.roleChainRequired": "角色 \"{id}\" 未配置模型:请至少添加一条链选择器(模型配置)",
1224
+ "validation.allDayRequired": "默认模型必须二选一:官方 V4 Flash 或 V4 Pro",
1225
+ "validation.slotChainRequired": "分时槽未配置模型:请至少添加一条链选择器",
1226
+ "validation.slotWindow": "分时槽开始/结束时间须为 HH:mm 格式",
1227
+ "validation.slotDays": "星期取值须为 0–6 的整数",
1228
+ "validation.slotKind": "分时槽 kind 须为 \"preset\" 或 \"custom\"",
1229
+ "validation.slotPresetUnknown": "未知的分时槽预设 \"{preset}\"",
1230
+ "validation.slotPresetDuplicate": "预设 \"{preset}\" 已存在:每个预设只能添加一行",
1231
+ "validation.slotPresetFrozen": "预设窗口是冻结代码常量:预设行不能携带 start/end/days",
872
1232
  "validation.selector": "选择器 \"{entry}\" 非法:{message}",
873
1233
  "legacy.banner": "检测到旧格式配置字段({keys}):已按新模型展示,请按 docs/configuration.md 迁移表手工改写;插件不会自动改写配置。",
874
1234
  "catalog.empty": "暂无可用模型:请先在模型页添加模型,添加后此处将自动可选。",
@@ -905,12 +1265,6 @@ window.__ModuleLoader__.load({
905
1265
  "save.saving": "保存中…",
906
1266
  "save.error": "保存失败:{message}",
907
1267
  "close": "关闭",
908
- "reset": "恢复默认",
909
- "reset.confirmTitle": "恢复默认配置",
910
- "reset.confirm": "恢复后 fallbacks 配置将回到插件默认值,当前编辑内容会丢失。",
911
- "reset.confirm.cancel": "取消",
912
- "reset.confirm.action": "恢复默认",
913
- "reset.saving": "恢复中…",
914
1268
  "loading": "加载中…",
915
1269
  "unavailable": "fallbacks 配置通道暂不可达:以下显示默认配置(或上次读取值),可尝试保存;保存失败会在此处如实提示。",
916
1270
  "error.generic": "出错:{message}"
@@ -956,16 +1310,62 @@ window.__ModuleLoader__.load({
956
1310
  "roleAutoMatch.label": "Enable role auto-match",
957
1311
  "roleAutoMatch.hint": "On rules-miss, the model picks the closest declared role",
958
1312
  "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",
1313
+ "rootChain.label": "Default fallback chain",
1314
+ "rootChain.tooltip": "Walked first whenever no time slot matches; if every entry fails, the default model below is the last fallback.",
1315
+ "defaultModel.label": "Default model",
964
1316
  "chains.selector.remove": "Remove this selector",
965
1317
  "chains.selector.providerPlaceholder": "Select provider",
966
1318
  "chains.selector.modelPlaceholder": "Select model",
967
1319
  "chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
968
1320
  "chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
1321
+ "mainAgent.label": "Main agent",
1322
+ "subagents.label": "Subagents",
1323
+ "timeSlots.label": "Time slots",
1324
+ "timeSlots.hint": "First match from top to bottom wins; the all-day row is always last",
1325
+ "timeSlots.tz.label": "Timezone (UTC±)",
1326
+ "timeSlots.drag": "Drag to reorder (or use the up/down buttons)",
1327
+ "timeSlots.name": "Name",
1328
+ "timeSlots.expand": "Expand this row",
1329
+ "timeSlots.collapse": "Collapse this row",
1330
+ "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.",
1331
+ "timeSlots.addPreset": "Add preset",
1332
+ "timeSlots.addCustom": "Add custom time slot",
1333
+ "timeSlots.presetPlaceholder": "Select a preset",
1334
+ "timeSlots.remove": "Remove this time-slot row",
1335
+ "timeSlots.moveUp": "Move this time-slot row up",
1336
+ "timeSlots.moveDown": "Move this time-slot row down",
1337
+ "timeSlots.start": "Start (HH:mm)",
1338
+ "timeSlots.end": "End (HH:mm)",
1339
+ "timeSlots.days": "Days",
1340
+ "timeSlots.days.hint": "None selected = every day; may wrap midnight",
1341
+ "timeSlots.preset.name": "Preset",
1342
+ "timeSlots.preset.windowLabel": "Window (read-only)",
1343
+ "timeSlots.preset.chainsOnly": "Preset windows are frozen: only the model chain is editable",
1344
+ "timeSlots.preset.liang-peak.label": "Liang Peak",
1345
+ "timeSlots.preset.liang-peak.window": "09:00–12:00 & 14:00–18:00 (every day, UTC+8)",
1346
+ "timeSlots.preset.liang-valley.label": "Liang Valley",
1347
+ "timeSlots.preset.liang-valley.window": "All times outside Liang Peak (every day, UTC+8)",
1348
+ "timeSlots.preset.glm-peak.label": "GLM Peak",
1349
+ "timeSlots.preset.glm-peak.window": "Monday–Friday 14:00–18:00 (UTC+8)",
1350
+ "timeSlots.preset.glm-valley.label": "GLM Valley",
1351
+ "timeSlots.preset.glm-valley.window": "All times outside GLM Peak (UTC+8)",
1352
+ "timeSlots.preset.glm.note": "Only effective when zai-coding-cn is configured",
1353
+ "timeSlots.preset.highCost": "High Cost",
1354
+ "timeSlots.preset.multiplier": "x{n}",
1355
+ "timeSlots.active": "Active",
1356
+ "timeSlots.preset.glm.unconfigured": " (requires zai-coding-cn)",
1357
+ "timeSlots.day.sun": "Sun",
1358
+ "timeSlots.day.mon": "Mon",
1359
+ "timeSlots.day.tue": "Tue",
1360
+ "timeSlots.day.wed": "Wed",
1361
+ "timeSlots.day.thu": "Thu",
1362
+ "timeSlots.day.fri": "Fri",
1363
+ "timeSlots.day.sat": "Sat",
1364
+ "timeSlots.selector.add": "Add selector",
1365
+ "allDay.hint": "Last-resort fallback of the all-day chain: official V4 Flash or Pro (pick exactly one)",
1366
+ "allDay.flash": "Official V4 Flash (deepseek-official/deepseek-v4-flash)",
1367
+ "allDay.pro": "Official V4 Pro (deepseek-official/deepseek-v4-pro)",
1368
+ "allDay.nonconforming": "The current default model is not valid: pick official V4 Flash or Pro before saving",
969
1369
  "roles.list.label": "Declared roles",
970
1370
  "roles.list.hint": "Declare roles before rules can reference them",
971
1371
  "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 +1383,12 @@ window.__ModuleLoader__.load({
983
1383
  "roles.fallback.none": "Role chain only (no rootChain)",
984
1384
  "roles.add": "Add role",
985
1385
  "roles.remove": "Remove this role",
1386
+ "roles.expand": "Expand this role",
1387
+ "roles.collapse": "Collapse this role",
986
1388
  "roles.selector.add": "Add selector",
987
1389
  "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",
1390
+ "roles.rules.hint": "Subagents only: matches provider/model in order; no match → inherit (root chain)",
1391
+ "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
1392
  "roles.rule.provider": "provider",
995
1393
  "roles.rule.provider.any": "Any",
996
1394
  "roles.rule.model": "model",
@@ -1008,6 +1406,14 @@ window.__ModuleLoader__.load({
1008
1406
  "validation.ruleRoleUndeclared": "Rule references undeclared role \"{role}\"",
1009
1407
  "validation.ruleRoleRequired": "Rule has no role selected: pick a target role, or remove the row",
1010
1408
  "validation.roleChainRequired": "Role \"{id}\" has no model config: add at least one chain entry",
1409
+ "validation.allDayRequired": "The default model must be exactly one official V4 model (V4 Flash or V4 Pro)",
1410
+ "validation.slotChainRequired": "Time-slot row has no models: add at least one chain entry",
1411
+ "validation.slotWindow": "Time-slot start/end must use HH:mm format",
1412
+ "validation.slotDays": "Days must be integers 0–6",
1413
+ "validation.slotKind": "Time-slot kind must be \"preset\" or \"custom\"",
1414
+ "validation.slotPresetUnknown": "Unknown time-slot preset \"{preset}\"",
1415
+ "validation.slotPresetDuplicate": "Preset \"{preset}\" already exists: at most one row per preset",
1416
+ "validation.slotPresetFrozen": "Preset windows are frozen code constants: a preset row cannot carry start/end/days",
1011
1417
  "validation.selector": "Invalid selector \"{entry}\": {message}",
1012
1418
  "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
1419
  "catalog.empty": "No models yet: add a model on the Models page first; options will appear here automatically.",
@@ -1044,12 +1450,6 @@ window.__ModuleLoader__.load({
1044
1450
  "save.saving": "Saving…",
1045
1451
  "save.error": "Save failed: {message}",
1046
1452
  "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
1453
  "loading": "Loading…",
1054
1454
  "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
1455
  "error.generic": "Error: {message}"
@@ -1092,7 +1492,7 @@ window.__ModuleLoader__.load({
1092
1492
  }
1093
1493
  //#endregion
1094
1494
  //#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";
1495
+ 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
1496
  const tagId$2 = "dsh-llm-fallbacks/FallbacksCard.module.css";
1097
1497
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1098
1498
  const tag = document.createElement("style");
@@ -1113,7 +1513,6 @@ window.__ModuleLoader__.load({
1113
1513
  "body": "_dbaa7975_body",
1114
1514
  "readOnly": "_4e39cf17_readOnly",
1115
1515
  "pending": "_99bf7f3c_pending",
1116
- "footer": "_114985b2_footer",
1117
1516
  "notice": "_9860a5b1_notice",
1118
1517
  "legacyNotice": "_5006d43e_legacyNotice",
1119
1518
  "error": "_21918751_error",
@@ -1127,13 +1526,19 @@ window.__ModuleLoader__.load({
1127
1526
  "fieldset": "_94c91bfd_fieldset",
1128
1527
  "field": "_67826267_field",
1129
1528
  "fieldLabel": "_13e68c3f_fieldLabel",
1529
+ "sectionHeading": "_ba240d82_sectionHeading",
1530
+ "sectionHeadingText": "_ddbc1b49_sectionHeadingText",
1531
+ "sectionActions": "_c41f9db7_sectionActions",
1130
1532
  "sectionToggle": "_b116c758_sectionToggle",
1131
1533
  "sectionToggleText": "_a5313fdf_sectionToggleText",
1132
1534
  "hint": "_4bc809b8_hint",
1133
1535
  "defaultNote": "_579f813a_defaultNote",
1536
+ "slotPresetName": "_b1962759_slotPresetName",
1134
1537
  "infoHint": "_a6244318_infoHint",
1135
1538
  "infoHintDisabled": "_e655c840_infoHintDisabled",
1136
1539
  "optionRow": "_a2521bb0_optionRow",
1540
+ "dayRow": "_c4ecbdf3_dayRow",
1541
+ "dayCell": "_0330c1ed_dayCell",
1137
1542
  "input": "_f9d86f7b_input",
1138
1543
  "inputTextarea": "_3a9b42fd_inputTextarea",
1139
1544
  "inputInvalid": "_7b60337a_inputInvalid",
@@ -1144,21 +1549,34 @@ window.__ModuleLoader__.load({
1144
1549
  "cardFoot": "_224b1387_cardFoot",
1145
1550
  "ruleGrid": "_8e220e07_ruleGrid",
1146
1551
  "ruleCell": "_24a12a2f_ruleCell",
1552
+ "collapseRow": "_86bc52d2_collapseRow",
1553
+ "collapseToggle": "_a73efcc6_collapseToggle",
1554
+ "collapseTitle": "_af017718_collapseTitle",
1555
+ "collapseMeta": "_2cabe603_collapseMeta",
1556
+ "slotTag": "_7b04f727_slotTag",
1557
+ "slotTagHighCost": "_c381e792_slotTagHighCost",
1558
+ "slotTagMultiplier": "_23bdb6fc_slotTagMultiplier",
1559
+ "slotTagActive": "_f000eed9_slotTagActive",
1560
+ "dragHandle": "_cd905ce5_dragHandle",
1561
+ "dragHandleIcon": "_49f0c4f0_dragHandleIcon",
1562
+ "slotCardDragging": "_fee63b7e_slotCardDragging",
1563
+ "slotCardOver": "_6f108491_slotCardOver",
1147
1564
  "ruleCellLabel": "_1014c097_ruleCellLabel",
1148
1565
  "chainSelectors": "_15a12620_chainSelectors",
1149
1566
  "selectorRow": "_240cbbf8_selectorRow",
1150
1567
  "iconButton": "_70effa64_iconButton",
1151
1568
  "iconButtonDanger": "_a0968257_iconButtonDanger",
1152
1569
  "addButton": "_28202c90_addButton",
1570
+ "slotAddRow": "_6a2bd54c_slotAddRow",
1571
+ "rowActions": "_4e77523e_rowActions",
1153
1572
  "primaryButton": "_747fd56d_primaryButton",
1154
1573
  "secondaryButton": "_7e855445_secondaryButton",
1574
+ "sectionAction": "_238d19be_sectionAction",
1155
1575
  "statusBlock": "_fbd045e0_statusBlock",
1156
1576
  "statusTitle": "_d4f70367_statusTitle",
1157
1577
  "statusLine": "_8ab300ed_statusLine",
1158
1578
  "statusLineLabel": "_e8cedbf5_statusLineLabel",
1159
- "offNotice": "_b48dfcba_offNotice",
1160
- "resetDialog": "_ec8bd970_resetDialog",
1161
- "confirmDanger": "_8677165c_confirmDanger"
1579
+ "offNotice": "_b48dfcba_offNotice"
1162
1580
  };
1163
1581
  //#endregion
1164
1582
  //#region src/client/FallbacksCard.tsx
@@ -1176,12 +1594,22 @@ window.__ModuleLoader__.load({
1176
1594
  * description, with a dirty "unsaved" pill and a rotating chevron
1177
1595
  * (`IconChevronDownOutline14` from ui-primitives — a CLIENT_EXTERNALS value
1178
1596
  * 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).
1597
+ * under the header; then the form content. PR #62 UX round 2: the card
1598
+ * footer is gone — each big section (主代理 / 子代理 / 高级选项) carries its
1599
+ * own Save/Discard actions beside its heading (高级选项: inside the expanded
1600
+ * body) and its own validation / save-error surface. PR #62 UX round 3:
1601
+ * each section's Save writes ONLY that section's fields — 主代理 owns
1602
+ * rootChain / timeSlots / tz (+ the card-level `enabled`), 子代理 owns
1603
+ * roles, 高级选项 owns the advanced scalars; the patch spreads the last
1604
+ * ACCEPTED config for every other section, so a 主代理 Save can never
1605
+ * ride along an unsaved 子代理 edit (and vice versa) — and validation /
1606
+ * the dirty gate apply per section too (a bad role id never blocks 主代理,
1607
+ * and only the saved section's Discard reverts that section's edits).
1608
+ * Save/discard disabled terms: save = `!sectionDirty || saving ||
1609
+ * !writable`, discard = `!sectionDirty || saving` (KD-U1). Disclosure is
1610
+ * card-local state:
1611
+ * which card a user has open is a reading gesture, and staged edits outlive
1612
+ * collapsing — the pill rides the header (upstream rationale).
1185
1613
  *
1186
1614
  * The form body is the two-block editing surface (spec §8): the `enabled`
1187
1615
  * checkbox row, the 6 top-level scalar fields (trigger codes / revert
@@ -1197,13 +1625,14 @@ window.__ModuleLoader__.load({
1197
1625
  * non-empty `state.legacyKeys` renders the migration banner at the top of
1198
1626
  * the card body. The row editors keep their filled editorCard surface
1199
1627
  * 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`.
1628
+ * to-defaults affordance is GONE from the card (PR #62 UX round 3) — the
1629
+ * gateway RPC `fallbacks/reset` and the store `resetToDefaults()` stay as
1630
+ * host APIs (store/gateway tests unchanged), only the card UI was removed.
1202
1631
  *
1203
1632
  * The page-only chrome is gone (720px column wrapper, title/intro banners,
1204
1633
  * 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.
1634
+ * model + recent-switch summary) is folded into the card body, and the
1635
+ * plugin-config section owns the column width.
1207
1636
  *
1208
1637
  * Degraded/error/loading states keep the same card chrome (KD-U3): the
1209
1638
  * header always renders title+description+chevron, and the body carries the
@@ -1217,8 +1646,11 @@ window.__ModuleLoader__.load({
1217
1646
  * 'error'`) also forces the body open with an error notice and — when the
1218
1647
  * form is inert (`!writable`, i.e. the load never landed) — a Retry button;
1219
1648
  * 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).
1649
+ * retry. PR #62 UX round 2: the single `state.error` surface is split by
1650
+ * origin — a LOAD failure keeps the card-top notice (with Retry when
1651
+ * inert), while a WRITE failure renders under the section whose Save was
1652
+ * last clicked (`lastSaveSection`), unlike the advisor's separate
1653
+ * apply-failure hints.
1222
1654
  *
1223
1655
  * The degraded derivation is latched in the card (the store stays untouched):
1224
1656
  * `present` only ever changes inside the store's `accept()`, so the settled
@@ -1228,6 +1660,77 @@ window.__ModuleLoader__.load({
1228
1660
  * implemented without a store change); on a first mount the latch is false,
1229
1661
  * so the healthy card starts (and stays) collapsed through its first load.
1230
1662
  */
1663
+ const ALL_DAY_FLASH = "deepseek-official/deepseek-v4-flash";
1664
+ const ALL_DAY_PRO = "deepseek-official/deepseek-v4-pro";
1665
+ const SLOT_PRESET_IDS = [
1666
+ "liang-peak",
1667
+ "liang-valley",
1668
+ "glm-peak",
1669
+ "glm-valley"
1670
+ ];
1671
+ /** Custom-row day toggle order (index = weekday, 0=Sunday); display copy lives in the dictionaries. */
1672
+ const SLOT_WEEKDAYS = [
1673
+ "sun",
1674
+ "mon",
1675
+ "tue",
1676
+ "wed",
1677
+ "thu",
1678
+ "fri",
1679
+ "sat"
1680
+ ];
1681
+ /** IANA timezone of this renderer (browser / host). */
1682
+ function hostTimeZone() {
1683
+ try {
1684
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
1685
+ return typeof tz === "string" && tz !== "" ? tz : "UTC";
1686
+ } catch {
1687
+ return "UTC";
1688
+ }
1689
+ }
1690
+ /** `UTC+8` / `UTC-4` for an IANA id (current offset, DST-honest). */
1691
+ function tzUtcOffset(tz) {
1692
+ try {
1693
+ const name = new Intl.DateTimeFormat("en-US", {
1694
+ timeZone: tz,
1695
+ timeZoneName: "shortOffset"
1696
+ }).formatToParts(/* @__PURE__ */ new Date()).find((part) => part.type === "timeZoneName")?.value;
1697
+ if (name === void 0 || name === "") return "";
1698
+ return name.replace(/^GMT/, "UTC");
1699
+ } catch {
1700
+ return "";
1701
+ }
1702
+ }
1703
+ /** Read-only custom-row copy: `Asia/Shanghai (UTC+8)`. */
1704
+ function tzDisplayLabel(tz) {
1705
+ const offset = tzUtcOffset(tz);
1706
+ return offset === "" ? tz : `${tz} (${offset})`;
1707
+ }
1708
+ /** Persist tz: presets lock UTC+8; custom-only uses the host zone; else keep the accepted value. */
1709
+ function resolvedSlotTz(rows, fallback) {
1710
+ if (rows.some((row) => row.kind === "preset")) return "Asia/Shanghai";
1711
+ if (rows.some((row) => row.kind === "custom")) return hostTimeZone();
1712
+ return fallback === "" ? "Asia/Shanghai" : fallback;
1713
+ }
1714
+ /** Strict 24h `HH:mm` — the resolver's HHMM_RE twin (drift-guarded by the gateway reject-on-save). */
1715
+ const HHMM_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
1716
+ /**
1717
+ * The 默认模型 value for a chain: the official V4 id when the chain TAIL
1718
+ * is that model (Flash XOR Pro — leading 默认降级链 entries allowed);
1719
+ * `''` for an empty chain or a chain whose last entry is not official
1720
+ * (the panel reads back unselected and save validation blocks the value).
1721
+ */
1722
+ function allDayModelOf(chain) {
1723
+ const tail = chain.length >= 1 ? chain[chain.length - 1] : void 0;
1724
+ return tail === ALL_DAY_FLASH || tail === ALL_DAY_PRO ? tail : "";
1725
+ }
1726
+ /**
1727
+ * The 默认降级链 editor row: the leading entries BEFORE the official-V4
1728
+ * tail, or the whole chain while the tail is not official (the draft
1729
+ * rides the accepted value until a 默认模型 pick).
1730
+ */
1731
+ function allDayChainRowOf(chain, catalog) {
1732
+ return rootChainToRows(allDayModelOf(chain) === "" ? chain : chain.slice(0, -1), catalog)[0];
1733
+ }
1231
1734
  /** Split scalars from the row editors (rootChain / role entities / role rules). */
1232
1735
  function scalarsOf(config) {
1233
1736
  return {
@@ -1237,7 +1740,8 @@ window.__ModuleLoader__.load({
1237
1740
  revertPolicy: config.revertPolicy,
1238
1741
  maxSwitchesPerStep: config.maxSwitchesPerStep,
1239
1742
  alwaysModeRetryCap: config.alwaysModeRetryCap,
1240
- roleAutoMatch: config.roleAutoMatch
1743
+ roleAutoMatch: config.roleAutoMatch,
1744
+ tz: config.tz ?? "Asia/Shanghai"
1241
1745
  };
1242
1746
  }
1243
1747
  /**
@@ -1255,13 +1759,22 @@ window.__ModuleLoader__.load({
1255
1759
  * `true` even for a legacy config that never declared the key — so the
1256
1760
  * toggle always renders (default on) and a save persists the resolved value
1257
1761
  * (AC-7 re-scope, PM decision 2026-08-17 Option A).
1762
+ *
1763
+ * All-day: rootChain is composed from the 默认降级链 editor's leading
1764
+ * selectors plus the 默认模型 tail (exactly one official V4 — Flash XOR
1765
+ * Pro). While no tail is selected the ACCEPTED chain rides through
1766
+ * untouched. `timeSlots` is rebuilt from the slot rows every render. `tz`
1767
+ * is a card scalar: preset rows lock it to Asia/Shanghai; custom rows
1768
+ * follow the selected timezone.
1258
1769
  */
1259
- function assembleConfig(scalars, rootChainRows, roleRows, ruleRows, originalRoles, presets, roleAutoMatch) {
1770
+ function assembleConfig(scalars, allDayModel, acceptedRootChain, allDayChainRow, roleRows, ruleRows, originalRoles, presets, roleAutoMatch, timeSlotRows) {
1260
1771
  const list = mergeRoleExtras(roleRows, originalRoles);
1772
+ const trailingChain = rowsToRootChain([allDayChainRow]);
1773
+ const tz = resolvedSlotTz(timeSlotRows, scalars.tz);
1261
1774
  return {
1262
1775
  enabled: scalars.enabled,
1263
1776
  triggerCodes: [...scalars.triggerCodes],
1264
- rootChain: rowsToRootChain(rootChainRows),
1777
+ rootChain: allDayModel === "" ? [...acceptedRootChain] : [...trailingChain, allDayModel],
1265
1778
  roles: {
1266
1779
  list,
1267
1780
  rules: rowsToRules(ruleRows)
@@ -1271,7 +1784,17 @@ window.__ModuleLoader__.load({
1271
1784
  maxSwitchesPerStep: scalars.maxSwitchesPerStep,
1272
1785
  alwaysModeRetryCap: scalars.alwaysModeRetryCap,
1273
1786
  ...presets === void 0 ? {} : { presets },
1274
- roleAutoMatch
1787
+ roleAutoMatch,
1788
+ timeSlots: rowsToTimeSlots(timeSlotRows),
1789
+ tz
1790
+ };
1791
+ }
1792
+ /** An empty per-section validation-error record (the clean-draft shape). */
1793
+ function emptyValidationErrors() {
1794
+ return {
1795
+ main: [],
1796
+ sub: [],
1797
+ advanced: []
1275
1798
  };
1276
1799
  }
1277
1800
  /**
@@ -1279,9 +1802,14 @@ window.__ModuleLoader__.load({
1279
1802
  * role id format/reserved word/duplicates, undeclared rule role references
1280
1803
  * (only reachable through the synthetic outside option — the dropdown
1281
1804
  * 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.
1805
+ * rootChain and role chains. Returns one localized message per violation,
1806
+ * bucketed by the section that owns the offending field (PR #62 UX round
1807
+ * 2 — 主代理: allDay / timeSlots / slot* / tz / default model / default
1808
+ * chain; 子代理: role* / rule*; 高级选项: trigger / cooldown / revert /
1809
+ * always / roleAutoMatch — the scalars are never validated, so the
1810
+ * advanced bucket stays empty today). A non-empty result blocks
1811
+ * {@link save} — the draft is never written. `persona` is free text and
1812
+ * never validated.
1285
1813
  *
1286
1814
  * `seededIds` is the live trimmed-id → overridden map derived from
1287
1815
  * `state.seeds` (spec §9.4): the empty-chain block relaxes for seeded ids
@@ -1290,33 +1818,57 @@ window.__ModuleLoader__.load({
1290
1818
  * behavior is byte-identical.
1291
1819
  */
1292
1820
  function validateDraft(draft, t, seededIds) {
1293
- const errors = [];
1821
+ const errors = emptyValidationErrors();
1294
1822
  const declaredIds = /* @__PURE__ */ new Set();
1295
1823
  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 }));
1824
+ if (!ROLE_ID_PATTERN.test(role.id)) errors.sub.push(t("validation.roleIdFormat", { id: role.id }));
1825
+ if (role.id === "inherit") errors.sub.push(t("validation.roleIdReserved"));
1826
+ if (declaredIds.has(role.id)) errors.sub.push(t("validation.roleIdDuplicate", { id: role.id }));
1299
1827
  declaredIds.add(role.id);
1300
1828
  for (const entry of role.chain ?? []) try {
1301
1829
  parseSelector(entry);
1302
1830
  } catch (error) {
1303
- errors.push(t("validation.selector", {
1831
+ errors.sub.push(t("validation.selector", {
1304
1832
  entry,
1305
1833
  message: error.message
1306
1834
  }));
1307
1835
  }
1308
- if ((role.chain ?? []).length === 0 && !seededIds.has(role.id.trim())) errors.push(t("validation.roleChainRequired", { id: role.id }));
1836
+ if ((role.chain ?? []).length === 0 && !seededIds.has(role.id.trim())) errors.sub.push(t("validation.roleChainRequired", { id: role.id }));
1309
1837
  }
1838
+ const allDayTail = draft.rootChain.length >= 1 ? draft.rootChain[draft.rootChain.length - 1] : void 0;
1839
+ if (allDayTail !== ALL_DAY_FLASH && allDayTail !== ALL_DAY_PRO) errors.main.push(t("validation.allDayRequired"));
1310
1840
  for (const entry of draft.rootChain) try {
1311
1841
  parseSelector(entry);
1312
1842
  } catch (error) {
1313
- errors.push(t("validation.selector", {
1843
+ errors.main.push(t("validation.selector", {
1314
1844
  entry,
1315
1845
  message: error.message
1316
1846
  }));
1317
1847
  }
1848
+ const seenSlotPresets = /* @__PURE__ */ new Set();
1849
+ for (const row of draft.timeSlots ?? []) {
1850
+ if (row.kind !== "preset" && row.kind !== "custom") errors.main.push(t("validation.slotKind"));
1851
+ if (row.kind === "preset") {
1852
+ if (typeof row.preset !== "string" || !SLOT_PRESET_IDS.includes(row.preset)) errors.main.push(t("validation.slotPresetUnknown", { preset: row.preset }));
1853
+ else if (seenSlotPresets.has(row.preset)) errors.main.push(t("validation.slotPresetDuplicate", { preset: row.preset }));
1854
+ else seenSlotPresets.add(row.preset);
1855
+ if (row.start !== void 0 || row.end !== void 0 || row.days !== void 0 && row.days.length > 0) errors.main.push(t("validation.slotPresetFrozen"));
1856
+ } else if (row.kind === "custom") {
1857
+ 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"));
1858
+ if (row.days !== void 0 && row.days.some((day) => !Number.isInteger(day) || day < 0 || day > 6)) errors.main.push(t("validation.slotDays"));
1859
+ }
1860
+ for (const entry of row.chain) try {
1861
+ parseSelector(entry);
1862
+ } catch (error) {
1863
+ errors.main.push(t("validation.selector", {
1864
+ entry,
1865
+ message: error.message
1866
+ }));
1867
+ }
1868
+ if (row.chain.length === 0) errors.main.push(t("validation.slotChainRequired"));
1869
+ }
1318
1870
  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 }));
1871
+ for (const rule of draft.roles.rules) if (!validTargets.has(rule.role)) errors.sub.push(t("validation.ruleRoleUndeclared", { role: rule.role }));
1320
1872
  return errors;
1321
1873
  }
1322
1874
  /**
@@ -1345,6 +1897,20 @@ window.__ModuleLoader__.load({
1345
1897
  const parsed = Number.parseInt(raw, 10);
1346
1898
  return Number.isNaN(parsed) ? 0 : Math.max(0, parsed);
1347
1899
  }
1900
+ /**
1901
+ * Custom time-slot rows whose window is not valid `HH:mm` — drives the
1902
+ * inline red border after a blocked save attempt (same derivation pattern
1903
+ * as {@link collectInvalidRoleIds}: one pass per render, index lookup per
1904
+ * row).
1905
+ */
1906
+ function collectInvalidSlotRows(rows) {
1907
+ const invalid = /* @__PURE__ */ new Set();
1908
+ rows.forEach((row, index) => {
1909
+ if (row.kind === "preset") return;
1910
+ if (!HHMM_RE.test(row.start) || !HHMM_RE.test(row.end)) invalid.add(index);
1911
+ });
1912
+ return invalid;
1913
+ }
1348
1914
  /** The catalog faces the dropdowns classify against; undefined while unready. */
1349
1915
  function catalogOf(state) {
1350
1916
  return state.catalogStatus === "ready" ? {
@@ -1533,24 +2099,79 @@ window.__ModuleLoader__.load({
1533
2099
  if (snapshot.switchesStatus === "idle") controller.loadSwitches();
1534
2100
  }, [controller]);
1535
2101
  const [scalars, setScalars] = (0, react.useState)(() => scalarsOf(defaultFallbacksConfig));
1536
- const [rootChainRows, setRootChainRows] = (0, react.useState)(() => rootChainToRows(defaultFallbacksConfig.rootChain));
2102
+ const [allDayModel, setAllDayModel] = (0, react.useState)(() => allDayModelOf(defaultFallbacksConfig.rootChain));
2103
+ const [allDayChainRow, setAllDayChainRow] = (0, react.useState)(() => allDayChainRowOf(defaultFallbacksConfig.rootChain, void 0));
2104
+ const [timeSlotRows, setTimeSlotRows] = (0, react.useState)(() => timeSlotsToRows(defaultFallbacksConfig.timeSlots ?? []));
1537
2105
  const [roleRows, setRoleRows] = (0, react.useState)(() => rolesToRows(defaultFallbacksConfig.roles.list));
1538
2106
  const [ruleRows, setRuleRows] = (0, react.useState)(() => rulesToRows(defaultFallbacksConfig.roles.rules));
1539
- const [validationErrors, setValidationErrors] = (0, react.useState)([]);
2107
+ const [presetToAdd, setPresetToAdd] = (0, react.useState)("");
2108
+ const [validationErrors, setValidationErrors] = (0, react.useState)(emptyValidationErrors);
1540
2109
  const [validationAttempted, setValidationAttempted] = (0, react.useState)(false);
2110
+ const [lastSaveSection, setLastSaveSection] = (0, react.useState)(null);
1541
2111
  const seededConfigKey = (0, react.useRef)(null);
2112
+ const draft = assembleConfig(scalars, allDayModel, state.config.rootChain, allDayChainRow, roleRows, ruleRows, state.config.roles.list, state.config.presets, scalars.roleAutoMatch, timeSlotRows);
2113
+ const hasEmptyRuleRows = ruleRows.some((row) => row.role === "");
2114
+ const enabledDirty = scalars.enabled !== state.config.enabled;
2115
+ const mainDirty = enabledDirty || JSON.stringify([...draft.rootChain, draft.timeSlots]) !== JSON.stringify([...state.config.rootChain, state.config.timeSlots ?? []]);
2116
+ const subDirty = hasEmptyRuleRows || JSON.stringify(draft.roles) !== JSON.stringify(state.config.roles);
2117
+ const advancedDirty = JSON.stringify([
2118
+ draft.triggerCodes,
2119
+ draft.cooldownMs,
2120
+ draft.revertPolicy,
2121
+ draft.maxSwitchesPerStep,
2122
+ draft.alwaysModeRetryCap,
2123
+ draft.roleAutoMatch
2124
+ ]) !== JSON.stringify([
2125
+ state.config.triggerCodes,
2126
+ state.config.cooldownMs,
2127
+ state.config.revertPolicy,
2128
+ state.config.maxSwitchesPerStep,
2129
+ state.config.alwaysModeRetryCap,
2130
+ state.config.roleAutoMatch
2131
+ ]);
2132
+ const dirty = mainDirty || subDirty || advancedDirty;
2133
+ const firstSeedDone = (0, react.useRef)(false);
2134
+ const forceReseed = (0, react.useRef)(false);
1542
2135
  (0, react.useEffect)(() => {
1543
2136
  if (state.status !== "ready") return;
1544
2137
  const key = JSON.stringify(state.config);
1545
2138
  if (seededConfigKey.current === key) return;
1546
2139
  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);
2140
+ const firstSeed = !firstSeedDone.current;
2141
+ firstSeedDone.current = true;
2142
+ const force = forceReseed.current;
2143
+ forceReseed.current = false;
2144
+ const catalog = catalogOf(state);
2145
+ if (firstSeed || force || !mainDirty) {
2146
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2147
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
2148
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
2149
+ setScalars((prev) => ({
2150
+ ...prev,
2151
+ enabled: state.config.enabled,
2152
+ tz: state.config.tz ?? "Asia/Shanghai"
2153
+ }));
2154
+ }
2155
+ if (firstSeed || force || !subDirty) {
2156
+ setRoleRows(rolesToRows(state.config.roles.list, catalog));
2157
+ setRuleRows(rulesToRows(state.config.roles.rules, catalog));
2158
+ }
2159
+ if (firstSeed || force || !advancedDirty) setScalars((prev) => ({
2160
+ ...prev,
2161
+ triggerCodes: [...state.config.triggerCodes],
2162
+ cooldownMs: state.config.cooldownMs,
2163
+ revertPolicy: state.config.revertPolicy,
2164
+ maxSwitchesPerStep: state.config.maxSwitchesPerStep,
2165
+ alwaysModeRetryCap: state.config.alwaysModeRetryCap,
2166
+ roleAutoMatch: state.config.roleAutoMatch
2167
+ }));
2168
+ }, [
2169
+ state.status,
2170
+ state.config,
2171
+ mainDirty,
2172
+ subDirty,
2173
+ advancedDirty
2174
+ ]);
1554
2175
  const updateScalars = (mutator) => {
1555
2176
  setScalars((prev) => {
1556
2177
  const next = {
@@ -1561,17 +2182,32 @@ window.__ModuleLoader__.load({
1561
2182
  return next;
1562
2183
  });
1563
2184
  };
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,
2185
+ const updateTimeSlotRow = (index, patch) => {
2186
+ setTimeSlotRows((rows) => {
2187
+ const next = rows.map((row) => ({ ...row }));
2188
+ next[index] = {
2189
+ ...next[index],
1569
2190
  ...patch
1570
- } : selector)
1571
- } : row));
2191
+ };
2192
+ return next;
2193
+ });
2194
+ };
2195
+ const updateTimeSlotSelector = (rowIndex, selectorIndex, patch) => {
2196
+ setTimeSlotRows((rows) => {
2197
+ const next = rows.map((row) => ({
2198
+ ...row,
2199
+ selectors: row.selectors.map((selector) => ({ ...selector }))
2200
+ }));
2201
+ const selectors = next[rowIndex].selectors;
2202
+ selectors[selectorIndex] = {
2203
+ ...selectors[selectorIndex],
2204
+ ...patch
2205
+ };
2206
+ return next;
2207
+ });
1572
2208
  };
1573
- const addRootChainSelector = () => {
1574
- setRootChainRows((rows) => rows.map((row, index) => index === 0 ? {
2209
+ const addTimeSlotSelector = (rowIndex) => {
2210
+ setTimeSlotRows((rows) => rows.map((row, index) => index === rowIndex ? {
1575
2211
  ...row,
1576
2212
  selectors: [...row.selectors, {
1577
2213
  wildcard: false,
@@ -1580,12 +2216,88 @@ window.__ModuleLoader__.load({
1580
2216
  }]
1581
2217
  } : row));
1582
2218
  };
1583
- const removeRootChainSelector = (selectorIndex) => {
1584
- setRootChainRows((rows) => rows.map((row, index) => index === 0 ? {
2219
+ const removeTimeSlotSelector = (rowIndex, selectorIndex) => {
2220
+ setTimeSlotRows((rows) => rows.map((row, index) => index === rowIndex ? {
1585
2221
  ...row,
1586
2222
  selectors: row.selectors.filter((_, sIndex) => sIndex !== selectorIndex)
1587
2223
  } : row));
1588
2224
  };
2225
+ const addPresetSlotRow = () => {
2226
+ if (presetToAdd === "") return;
2227
+ if ((presetToAdd === "glm-peak" || presetToAdd === "glm-valley") && !glmConfigured) return;
2228
+ setTimeSlotRows((rows) => [...rows, {
2229
+ kind: "preset",
2230
+ preset: presetToAdd,
2231
+ start: "",
2232
+ end: "",
2233
+ days: [],
2234
+ name: "",
2235
+ collapsed: false,
2236
+ selectors: []
2237
+ }]);
2238
+ setPresetToAdd("");
2239
+ };
2240
+ const addCustomSlotRow = () => {
2241
+ setTimeSlotRows((rows) => [...rows, {
2242
+ kind: "custom",
2243
+ start: "",
2244
+ end: "",
2245
+ days: [],
2246
+ name: "",
2247
+ collapsed: false,
2248
+ selectors: []
2249
+ }]);
2250
+ };
2251
+ const removeTimeSlotRow = (index) => {
2252
+ setTimeSlotRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
2253
+ };
2254
+ const moveTimeSlotRow = (index, delta) => {
2255
+ setTimeSlotRows((rows) => {
2256
+ const target = index + delta;
2257
+ if (target < 0 || target >= rows.length) return rows;
2258
+ const next = rows.map((row) => ({ ...row }));
2259
+ const moved = next[index];
2260
+ next[index] = next[target];
2261
+ next[target] = moved;
2262
+ return next;
2263
+ });
2264
+ };
2265
+ const [draggedSlotIndex, setDraggedSlotIndex] = (0, react.useState)(null);
2266
+ const [overSlotIndex, setOverSlotIndex] = (0, react.useState)(null);
2267
+ const reorderTimeSlotRow = (from, to) => {
2268
+ setTimeSlotRows((rows) => {
2269
+ if (from === to || from < 0 || to < 0 || from >= rows.length || to >= rows.length) return rows;
2270
+ const next = rows.map((row) => ({ ...row }));
2271
+ const [moved] = next.splice(from, 1);
2272
+ next.splice(to, 0, moved);
2273
+ return next;
2274
+ });
2275
+ };
2276
+ const updateAllDayChainSelector = (selectorIndex, patch) => {
2277
+ setAllDayChainRow((row) => ({
2278
+ ...row,
2279
+ selectors: row.selectors.map((selector, index) => index === selectorIndex ? {
2280
+ ...selector,
2281
+ ...patch
2282
+ } : selector)
2283
+ }));
2284
+ };
2285
+ const addAllDayChainSelector = () => {
2286
+ setAllDayChainRow((row) => ({
2287
+ ...row,
2288
+ selectors: [...row.selectors, {
2289
+ wildcard: false,
2290
+ provider: null,
2291
+ model: null
2292
+ }]
2293
+ }));
2294
+ };
2295
+ const removeAllDayChainSelector = (selectorIndex) => {
2296
+ setAllDayChainRow((row) => ({
2297
+ ...row,
2298
+ selectors: row.selectors.filter((_, index) => index !== selectorIndex)
2299
+ }));
2300
+ };
1589
2301
  const updateRoleRow = (index, patch) => {
1590
2302
  setRoleRows((rows) => {
1591
2303
  const next = rows.map((row) => ({ ...row }));
@@ -1631,7 +2343,8 @@ window.__ModuleLoader__.load({
1631
2343
  id: "",
1632
2344
  persona: "",
1633
2345
  selectors: [],
1634
- fallback: "inherit-root"
2346
+ fallback: "inherit-root",
2347
+ collapsed: true
1635
2348
  }]);
1636
2349
  };
1637
2350
  const removeRole = (index) => {
@@ -1647,15 +2360,21 @@ window.__ModuleLoader__.load({
1647
2360
  return next;
1648
2361
  });
1649
2362
  };
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;
2363
+ const allValidationErrors = [
2364
+ ...validationErrors.main,
2365
+ ...validationErrors.sub,
2366
+ ...validationErrors.advanced
2367
+ ];
1653
2368
  const saving = state.status === "saving";
1654
2369
  const writable = state.writable;
1655
2370
  const unknownCodes = scalars.triggerCodes.filter((code) => !KNOWN_TRIGGER_CODES.includes(code));
1656
- const rootChainConfigured = rowsToRootChain(rootChainRows).length > 0;
2371
+ const presetsPresent = timeSlotRows.some((row) => row.kind === "preset");
2372
+ const glmConfigured = state.configuredProviders.some((entry) => entry.provider === "zai-coding-cn");
2373
+ const slotState = resolveSlotState(state.config, /* @__PURE__ */ new Date(), scalars.tz);
2374
+ const activeSlotIndex = slotState.winner === "all-day" ? -1 : state.config.timeSlots?.indexOf(slotState.winner) ?? -1;
1657
2375
  const roleOptions = ruleRoleOptions({ list: roleRows });
1658
2376
  const invalidRoleIds = validationAttempted ? collectInvalidRoleIds(roleRows) : null;
2377
+ const invalidSlotRows = validationAttempted ? collectInvalidSlotRows(timeSlotRows) : null;
1659
2378
  const seededIds = /* @__PURE__ */ new Map();
1660
2379
  for (const seed of state.seeds) seededIds.set(seed.id.trim(), seed.overridden);
1661
2380
  const latestSwitch = state.switches[0];
@@ -1674,45 +2393,129 @@ window.__ModuleLoader__.load({
1674
2393
  };
1675
2394
  switchesLine = latestSwitch.reason === "role-inject" ? t("status.switches.compact.roleInject", params) : t("status.switches.compact", params);
1676
2395
  }
1677
- const catalogSeededEpoch = (0, react.useRef)(null);
2396
+ const catalogSeededSections = (0, react.useRef)({
2397
+ epoch: null,
2398
+ main: false,
2399
+ sub: false
2400
+ });
1678
2401
  (0, react.useEffect)(() => {
1679
2402
  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)));
2403
+ const seed = catalogSeededSections.current;
2404
+ if (seed.epoch !== state.catalogEpoch) {
2405
+ seed.epoch = state.catalogEpoch;
2406
+ seed.main = false;
2407
+ seed.sub = false;
2408
+ }
2409
+ const catalog = catalogOf(state);
2410
+ if (!mainDirty && !seed.main) {
2411
+ seed.main = true;
2412
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2413
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
2414
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
2415
+ }
2416
+ if (!subDirty && !seed.sub) {
2417
+ seed.sub = true;
2418
+ setRoleRows(rolesToRows(state.config.roles.list, catalog));
2419
+ setRuleRows(rulesToRows(state.config.roles.rules, catalog));
2420
+ }
1686
2421
  }, [
1687
2422
  state.catalogStatus,
1688
2423
  state.catalogEpoch,
1689
2424
  state.config,
1690
- dirty
2425
+ mainDirty,
2426
+ subDirty
1691
2427
  ]);
1692
- const save = () => {
2428
+ const sectionPatch = (section) => {
2429
+ const base = {
2430
+ ...state.config,
2431
+ enabled: scalars.enabled
2432
+ };
2433
+ switch (section) {
2434
+ case "main": return {
2435
+ ...base,
2436
+ rootChain: draft.rootChain,
2437
+ timeSlots: draft.timeSlots,
2438
+ tz: draft.tz
2439
+ };
2440
+ case "sub": return {
2441
+ ...base,
2442
+ roles: draft.roles
2443
+ };
2444
+ case "advanced": return {
2445
+ ...base,
2446
+ triggerCodes: draft.triggerCodes,
2447
+ cooldownMs: draft.cooldownMs,
2448
+ revertPolicy: draft.revertPolicy,
2449
+ maxSwitchesPerStep: draft.maxSwitchesPerStep,
2450
+ alwaysModeRetryCap: draft.alwaysModeRetryCap,
2451
+ roleAutoMatch: draft.roleAutoMatch
2452
+ };
2453
+ }
2454
+ };
2455
+ const save = (section) => {
2456
+ setLastSaveSection(section);
1693
2457
  const errors = validateDraft(draft, t, seededIds);
1694
- if (hasEmptyRuleRows) errors.push(t("validation.ruleRoleRequired"));
1695
- if (errors.length > 0) {
2458
+ if (section === "sub" && hasEmptyRuleRows) errors.sub.push(t("validation.ruleRoleRequired"));
2459
+ if (errors[section].length > 0) {
1696
2460
  setValidationErrors(errors);
1697
2461
  setValidationAttempted(true);
1698
2462
  return;
1699
2463
  }
1700
- setValidationErrors([]);
2464
+ setValidationErrors(emptyValidationErrors());
2465
+ setValidationAttempted(false);
2466
+ controller.save(sectionPatch(section));
2467
+ };
2468
+ const saveEnabled = () => {
2469
+ setLastSaveSection("main");
2470
+ setValidationErrors(emptyValidationErrors());
2471
+ setValidationAttempted(false);
2472
+ controller.save({
2473
+ ...state.config,
2474
+ enabled: scalars.enabled
2475
+ });
2476
+ };
2477
+ const discardSection = (section) => {
2478
+ switch (section) {
2479
+ case "main":
2480
+ setAllDayModel(allDayModelOf(state.config.rootChain));
2481
+ setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalogOf(state)));
2482
+ setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalogOf(state)));
2483
+ setScalars((prev) => ({
2484
+ ...prev,
2485
+ enabled: state.config.enabled,
2486
+ tz: state.config.tz ?? "Asia/Shanghai"
2487
+ }));
2488
+ break;
2489
+ case "sub":
2490
+ setRoleRows(rolesToRows(state.config.roles.list, catalogOf(state)));
2491
+ setRuleRows(rulesToRows(state.config.roles.rules, catalogOf(state)));
2492
+ break;
2493
+ case "advanced": setScalars((prev) => ({
2494
+ ...prev,
2495
+ triggerCodes: [...state.config.triggerCodes],
2496
+ cooldownMs: state.config.cooldownMs,
2497
+ revertPolicy: state.config.revertPolicy,
2498
+ maxSwitchesPerStep: state.config.maxSwitchesPerStep,
2499
+ alwaysModeRetryCap: state.config.alwaysModeRetryCap,
2500
+ roleAutoMatch: state.config.roleAutoMatch
2501
+ }));
2502
+ }
2503
+ setValidationErrors(emptyValidationErrors());
1701
2504
  setValidationAttempted(false);
1702
- controller.save(draft);
1703
2505
  };
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([]);
2506
+ const discardEnabled = () => {
2507
+ setScalars((prev) => ({
2508
+ ...prev,
2509
+ enabled: state.config.enabled
2510
+ }));
2511
+ setValidationErrors(emptyValidationErrors());
1710
2512
  setValidationAttempted(false);
1711
2513
  };
1712
2514
  (0, react.useEffect)(() => {
1713
2515
  if (!validationAttempted) return;
1714
- if (validateDraft(draft, t, seededIds).length === 0 && !ruleRows.some((row) => row.role === "")) {
1715
- setValidationErrors([]);
2516
+ const errors = validateDraft(draft, t, seededIds);
2517
+ if (errors.main.length === 0 && errors.sub.length === 0 && errors.advanced.length === 0 && !ruleRows.some((row) => row.role === "")) {
2518
+ setValidationErrors(emptyValidationErrors());
1716
2519
  setValidationAttempted(false);
1717
2520
  }
1718
2521
  }, [
@@ -1721,13 +2524,10 @@ window.__ModuleLoader__.load({
1721
2524
  ruleRows,
1722
2525
  t
1723
2526
  ]);
1724
- const confirmReset = () => {
1725
- setResetting(true);
1726
- controller.resetToDefaults().finally(() => {
1727
- setResetting(false);
1728
- setConfirmingReset(false);
1729
- });
1730
- };
2527
+ (0, react.useEffect)(() => {
2528
+ if (state.status === "ready") setLastSaveSection(null);
2529
+ if (state.status === "error") forceReseed.current = false;
2530
+ }, [state.status]);
1731
2531
  const [userOpen, setUserOpen] = (0, react.useState)(false);
1732
2532
  const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
1733
2533
  const degradedLatch = (0, react.useRef)(false);
@@ -1768,871 +2568,1323 @@ window.__ModuleLoader__.load({
1768
2568
  });
1769
2569
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1770
2570
  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", {
2571
+ children: [header, open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2572
+ className: FallbacksCard_module_css_default.body,
2573
+ children: [
2574
+ state.legacyKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2575
+ className: FallbacksCard_module_css_default.legacyNotice,
2576
+ role: "status",
2577
+ children: t("legacy.banner", { keys: state.legacyKeys.join(", ") })
2578
+ }),
2579
+ state.status === "error" && state.error !== null && lastSaveSection === null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2580
+ className: FallbacksCard_module_css_default.noticeRow,
2581
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1797
2582
  className: FallbacksCard_module_css_default.error,
1798
2583
  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")
2584
+ children: t("error.generic", { message: state.error })
2585
+ }), !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2586
+ variant: "outline",
2587
+ size: "sm",
2588
+ onClick: () => {
2589
+ controller.load();
2590
+ },
2591
+ children: t("retry")
2592
+ })]
2593
+ }),
2594
+ degraded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2595
+ className: FallbacksCard_module_css_default.notice,
2596
+ role: "status",
2597
+ children: t("unavailable")
2598
+ }),
2599
+ state.status === "ready" && !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2600
+ className: FallbacksCard_module_css_default.readOnly,
2601
+ role: "status",
2602
+ children: t("readOnly")
2603
+ }),
2604
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2605
+ className: FallbacksCard_module_css_default.form,
2606
+ children: [
2607
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2608
+ className: FallbacksCard_module_css_default.checkboxRow,
2609
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2610
+ className: FallbacksCard_module_css_default.checkLabel,
2611
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2612
+ className: FallbacksCard_module_css_default.checkLabelTitle,
2613
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2614
+ htmlFor: "fallbacks-enabled",
2615
+ children: t("enabled.label")
2616
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2617
+ label: t("enabled.tooltip"),
2618
+ disabled: !writable
1830
2619
  })]
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
- }
2620
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2621
+ className: FallbacksCard_module_css_default.checkLabelDesc,
2622
+ children: t("enabled.hint")
1842
2623
  })]
1843
- }),
1844
- !scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2624
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2625
+ id: "fallbacks-enabled",
2626
+ type: "checkbox",
2627
+ className: FallbacksCard_module_css_default.checkbox,
2628
+ checked: scalars.enabled,
2629
+ disabled: !writable,
2630
+ onChange: (event) => {
2631
+ updateScalars((draft) => {
2632
+ draft.enabled = event.target.checked;
2633
+ });
2634
+ }
2635
+ })]
2636
+ }),
2637
+ !scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2638
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1845
2639
  className: FallbacksCard_module_css_default.offNotice,
1846
2640
  children: t("enabled.off")
1847
2641
  }),
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,
2642
+ allValidationErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2643
+ className: FallbacksCard_module_css_default.error,
2644
+ role: "alert",
2645
+ children: `${t("validation.blocked")}${allValidationErrors.join("; ")}`
2646
+ }),
2647
+ lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2648
+ className: FallbacksCard_module_css_default.error,
2649
+ role: "alert",
2650
+ children: t("error.generic", { message: state.error })
2651
+ }),
2652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2653
+ className: FallbacksCard_module_css_default.sectionActions,
2654
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2655
+ type: "button",
2656
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2657
+ disabled: !enabledDirty || saving,
2658
+ onClick: discardEnabled,
2659
+ children: t("discard")
2660
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2661
+ type: "button",
2662
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2663
+ disabled: !writable || saving || !enabledDirty,
2664
+ onClick: saveEnabled,
2665
+ children: saving ? t("save.saving") : t("save")
2666
+ })]
2667
+ })
2668
+ ] }),
2669
+ scalars.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2670
+ className: FallbacksCard_module_css_default.fieldset,
2671
+ disabled: !writable,
2672
+ children: [
2673
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2674
+ className: FallbacksCard_module_css_default.sectionHeading,
2675
+ id: "fallbacks-main-agent",
2676
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2677
+ className: FallbacksCard_module_css_default.sectionHeadingText,
2678
+ children: t("mainAgent.label")
2679
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2680
+ className: FallbacksCard_module_css_default.sectionActions,
2681
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2682
+ type: "button",
2683
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2684
+ disabled: !mainDirty || saving,
2685
+ onClick: () => {
2686
+ discardSection("main");
2687
+ },
2688
+ children: t("discard")
2689
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2690
+ type: "button",
2691
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
2692
+ disabled: !writable || saving || !mainDirty,
2693
+ onClick: () => {
2694
+ save("main");
2695
+ },
2696
+ children: saving ? t("save.saving") : t("save")
2697
+ })]
2698
+ })]
2699
+ }),
2700
+ validationErrors.main.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2701
+ className: FallbacksCard_module_css_default.error,
2702
+ role: "alert",
2703
+ children: `${t("validation.blocked")}${validationErrors.main.join("; ")}`
2704
+ }),
2705
+ lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2706
+ className: FallbacksCard_module_css_default.error,
2707
+ role: "alert",
2708
+ children: t("error.generic", { message: state.error })
2709
+ }),
2710
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2711
+ className: FallbacksCard_module_css_default.field,
2712
+ role: "group",
2713
+ "aria-labelledby": "fallbacks-time-slots",
2714
+ children: [
2715
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2716
+ className: FallbacksCard_module_css_default.fieldLabel,
2717
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2718
+ id: "fallbacks-time-slots",
2719
+ children: t("timeSlots.label")
2720
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2721
+ label: t("timeSlots.tooltip"),
2722
+ disabled: !writable
2723
+ })]
2724
+ }),
2725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2726
+ className: FallbacksCard_module_css_default.hint,
2727
+ children: t("timeSlots.hint")
2728
+ }),
2729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2730
+ className: FallbacksCard_module_css_default.list,
2731
+ children: timeSlotRows.map((row, index) => {
2732
+ const invalidWindow = invalidSlotRows?.has(index) ?? false;
2733
+ const chainEmpty = row.selectors.every((selector) => selectorRowToRaw(selector) === "");
2734
+ const firstModel = row.selectors.map(selectorRowToRaw).find((entry) => entry !== "");
2735
+ const slotExpanded = !row.collapsed || !writable;
2736
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2737
+ className: `${FallbacksCard_module_css_default.editorCard} ${draggedSlotIndex === index ? FallbacksCard_module_css_default.slotCardDragging : ""} ${overSlotIndex === index && draggedSlotIndex !== null && draggedSlotIndex !== index ? FallbacksCard_module_css_default.slotCardOver : ""}`,
2738
+ onDragOver: (event) => {
2739
+ if (draggedSlotIndex === null) return;
2740
+ event.preventDefault();
2741
+ if (overSlotIndex !== index) setOverSlotIndex(index);
2742
+ },
2743
+ onDrop: (event) => {
2744
+ event.preventDefault();
2745
+ const from = draggedSlotIndex;
2746
+ setDraggedSlotIndex(null);
2747
+ setOverSlotIndex(null);
2748
+ if (from !== null && from !== index) reorderTimeSlotRow(from, index);
2749
+ },
2750
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2751
+ className: FallbacksCard_module_css_default.collapseRow,
2752
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2753
+ type: "button",
2754
+ className: FallbacksCard_module_css_default.collapseToggle,
2755
+ "aria-expanded": slotExpanded,
2756
+ "aria-label": t(slotExpanded ? "timeSlots.collapse" : "timeSlots.expand"),
1897
2757
  disabled: !writable,
1898
- t,
1899
- onChange: (patch) => {
1900
- updateRootChainSelector(selectorIndex, patch);
2758
+ onClick: () => {
2759
+ updateTimeSlotRow(index, { collapsed: !row.collapsed });
1901
2760
  },
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
- ]
2761
+ children: [
2762
+ /* @__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 }),
2763
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2764
+ className: FallbacksCard_module_css_default.collapseTitle,
2765
+ children: row.kind === "preset" ? t(`timeSlots.preset.${row.preset}.label`) : row.name !== "" ? row.name : `custom ${row.start}-${row.end}`
2766
+ }),
2767
+ 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", {
2768
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagHighCost}`,
2769
+ children: t("timeSlots.preset.highCost")
2770
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2771
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagMultiplier}`,
2772
+ children: t("timeSlots.preset.multiplier", { n: row.preset === "liang-peak" ? "2" : "3" })
2773
+ })] }),
2774
+ activeSlotIndex === index && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2775
+ className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagActive}`,
2776
+ children: t("timeSlots.active")
2777
+ }),
2778
+ firstModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2779
+ className: FallbacksCard_module_css_default.collapseMeta,
2780
+ children: firstModel
1970
2781
  })
1971
- }),
1972
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2782
+ ]
2783
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2784
+ type: "button",
2785
+ className: FallbacksCard_module_css_default.dragHandle,
2786
+ draggable: writable,
2787
+ "data-tip": t("timeSlots.drag"),
2788
+ "aria-label": t("timeSlots.drag"),
2789
+ disabled: !writable,
2790
+ onDragStart: () => {
2791
+ if (!writable) return;
2792
+ setDraggedSlotIndex(index);
2793
+ setOverSlotIndex(index);
2794
+ },
2795
+ onDragEnd: () => {
2796
+ setDraggedSlotIndex(null);
2797
+ setOverSlotIndex(null);
2798
+ },
2799
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, { className: FallbacksCard_module_css_default.dragHandleIcon })
2800
+ })]
2801
+ }), slotExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2802
+ row.kind === "preset" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2803
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1973
2804
  className: FallbacksCard_module_css_default.ruleGrid,
1974
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1975
2806
  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
- })
2009
- }),
2010
- /* @__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,
2016
- disabled: !writable,
2017
- t,
2018
- onChange: (patch) => {
2019
- updateRoleSelector(index, selectorIndex, patch);
2020
- },
2021
- onRemove: () => {
2022
- removeRoleSelector(index, selectorIndex);
2807
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2808
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2809
+ children: t("timeSlots.preset.name")
2810
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2811
+ className: FallbacksCard_module_css_default.slotPresetName,
2812
+ children: t(`timeSlots.preset.${row.preset}.label`)
2813
+ })]
2814
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2815
+ className: FallbacksCard_module_css_default.ruleCell,
2816
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2817
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2818
+ children: t("timeSlots.preset.windowLabel")
2819
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2820
+ className: FallbacksCard_module_css_default.hint,
2821
+ children: t(`timeSlots.preset.${row.preset}.window`)
2822
+ })]
2823
+ })]
2824
+ }),
2825
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2826
+ className: FallbacksCard_module_css_default.hint,
2827
+ children: t("timeSlots.preset.chainsOnly")
2828
+ }),
2829
+ (row.preset === "glm-peak" || row.preset === "glm-valley") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2830
+ className: FallbacksCard_module_css_default.hint,
2831
+ children: t("timeSlots.preset.glm.note")
2832
+ })
2833
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2835
+ className: FallbacksCard_module_css_default.field,
2836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2837
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2838
+ children: t("timeSlots.name")
2839
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2840
+ className: FallbacksCard_module_css_default.input,
2841
+ value: row.name,
2842
+ placeholder: t("timeSlots.name"),
2843
+ "aria-label": t("timeSlots.name"),
2844
+ disabled: !writable,
2845
+ onChange: (event) => {
2846
+ updateTimeSlotRow(index, { name: event.target.value });
2023
2847
  }
2024
- }, selectorIndex)), row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2848
+ })]
2849
+ }),
2850
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2851
+ className: FallbacksCard_module_css_default.field,
2852
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2853
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2854
+ children: t("timeSlots.tz.label")
2855
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2025
2856
  className: FallbacksCard_module_css_default.hint,
2026
- children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
2857
+ "aria-label": t("timeSlots.tz.label"),
2858
+ children: tzDisplayLabel(presetsPresent ? "Asia/Shanghai" : hostTimeZone())
2027
2859
  })]
2028
2860
  }),
2029
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2030
2862
  className: FallbacksCard_module_css_default.ruleGrid,
2031
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2863
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2032
2864
  className: FallbacksCard_module_css_default.ruleCell,
2033
2865
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2034
2866
  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"),
2867
+ children: t("timeSlots.start")
2868
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2869
+ className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
2870
+ value: row.start,
2871
+ placeholder: "09:00",
2872
+ "aria-label": t("timeSlots.start"),
2040
2873
  disabled: !writable,
2041
2874
  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
- })]
2875
+ updateTimeSlotRow(index, { start: event.target.value });
2876
+ }
2051
2877
  })]
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")
2878
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2879
+ className: FallbacksCard_module_css_default.ruleCell,
2880
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2881
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2882
+ children: t("timeSlots.end")
2883
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2884
+ className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
2885
+ value: row.end,
2886
+ placeholder: "18:00",
2887
+ "aria-label": t("timeSlots.end"),
2888
+ disabled: !writable,
2889
+ onChange: (event) => {
2890
+ updateTimeSlotRow(index, { end: event.target.value });
2891
+ }
2892
+ })]
2893
+ })]
2063
2894
  }),
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
2895
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2136
- className: FallbacksCard_module_css_default.ruleGrid,
2896
+ className: FallbacksCard_module_css_default.field,
2137
2897
  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
- })]
2898
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2899
+ className: FallbacksCard_module_css_default.ruleCellLabel,
2900
+ children: t("timeSlots.days")
2199
2901
  }),
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
- })]
2902
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2903
+ className: FallbacksCard_module_css_default.dayRow,
2904
+ children: SLOT_WEEKDAYS.map((day, dayIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2905
+ className: FallbacksCard_module_css_default.dayCell,
2906
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2907
+ type: "checkbox",
2908
+ checked: row.days.includes(dayIndex),
2909
+ disabled: !writable,
2910
+ onChange: () => {
2911
+ updateTimeSlotRow(index, { days: row.days.includes(dayIndex) ? row.days.filter((existing) => existing !== dayIndex) : [...row.days, dayIndex] });
2912
+ }
2913
+ }), t(`timeSlots.day.${day}`)]
2914
+ }, day))
2226
2915
  }),
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
- })]
2916
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2917
+ className: FallbacksCard_module_css_default.hint,
2918
+ children: t("timeSlots.days.hint")
2254
2919
  })
2255
2920
  ]
2256
2921
  }),
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", {
2922
+ (row.start !== "" || row.end !== "") && !(HHMM_RE.test(row.start) && HHMM_RE.test(row.end)) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2265
2923
  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
- })
2924
+ children: t("validation.slotWindow")
2280
2925
  })
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", {
2926
+ ] }),
2927
+ chainEmpty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2370
2928
  className: FallbacksCard_module_css_default.hint,
2371
- children: t("triggerCodes.hint")
2929
+ children: t("validation.slotChainRequired")
2372
2930
  }),
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
- });
2931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2932
+ className: FallbacksCard_module_css_default.chainSelectors,
2933
+ children: row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
2934
+ selector,
2935
+ catalog: catalogOf(state),
2936
+ configuredProviders: state.configuredProviders,
2937
+ disabled: !writable,
2938
+ t,
2939
+ onChange: (patch) => {
2940
+ updateTimeSlotSelector(index, selectorIndex, patch);
2941
+ },
2942
+ onRemove: () => {
2943
+ removeTimeSlotSelector(index, selectorIndex);
2382
2944
  }
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
- })]
2945
+ }, selectorIndex))
2405
2946
  }),
2406
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2407
- className: FallbacksCard_module_css_default.hint,
2408
- children: t("revertPolicy.hint")
2947
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2948
+ variant: "outline",
2949
+ size: "sm",
2950
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
2951
+ className: FallbacksCard_module_css_default.addButton,
2952
+ onClick: () => {
2953
+ addTimeSlotSelector(index);
2954
+ },
2955
+ children: t("timeSlots.selector.add")
2409
2956
  }),
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
- ]
2957
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2958
+ className: FallbacksCard_module_css_default.cardFoot,
2959
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2960
+ className: FallbacksCard_module_css_default.rowActions,
2961
+ children: [
2962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2963
+ type: "button",
2964
+ className: FallbacksCard_module_css_default.iconButton,
2965
+ "data-tip": t("timeSlots.moveUp"),
2966
+ "aria-label": t("timeSlots.moveUp"),
2967
+ disabled: !writable || index === 0,
2968
+ onClick: () => {
2969
+ moveTimeSlotRow(index, -1);
2970
+ },
2971
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
2972
+ }),
2973
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2974
+ type: "button",
2975
+ className: FallbacksCard_module_css_default.iconButton,
2976
+ "data-tip": t("timeSlots.moveDown"),
2977
+ "aria-label": t("timeSlots.moveDown"),
2978
+ disabled: !writable || index === timeSlotRows.length - 1,
2979
+ onClick: () => {
2980
+ moveTimeSlotRow(index, 1);
2981
+ },
2982
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
2983
+ }),
2984
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2985
+ type: "button",
2986
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
2987
+ "data-tip": t("timeSlots.remove"),
2988
+ "aria-label": t("timeSlots.remove"),
2989
+ onClick: () => {
2990
+ removeTimeSlotRow(index);
2991
+ },
2992
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
2993
+ })
2994
+ ]
2995
+ })
2996
+ })
2997
+ ] })]
2998
+ }, index);
2999
+ })
3000
+ }),
3001
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3002
+ className: FallbacksCard_module_css_default.slotAddRow,
3003
+ children: [
3004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3005
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3006
+ value: presetToAdd,
3007
+ "aria-label": t("timeSlots.presetPlaceholder"),
3008
+ disabled: !writable,
3009
+ onChange: (event) => {
3010
+ setPresetToAdd(event.target.value);
3011
+ },
3012
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3013
+ value: "",
3014
+ children: t("timeSlots.presetPlaceholder")
3015
+ }), SLOT_PRESET_IDS.filter((id) => !timeSlotRows.some((row) => row.kind === "preset" && row.preset === id)).map((id) => {
3016
+ const glmUnconfigured = !glmConfigured && (id === "glm-peak" || id === "glm-valley");
3017
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
3018
+ value: id,
3019
+ disabled: glmUnconfigured,
3020
+ children: [t(`timeSlots.preset.${id}.label`), glmUnconfigured ? t("timeSlots.preset.glm.unconfigured") : null]
3021
+ }, id);
3022
+ })]
2424
3023
  }),
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,
3024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3025
+ variant: "outline",
3026
+ size: "sm",
3027
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3028
+ disabled: !writable || presetToAdd === "",
3029
+ onClick: addPresetSlotRow,
3030
+ children: t("timeSlots.addPreset")
3031
+ }),
3032
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3033
+ variant: "outline",
3034
+ size: "sm",
3035
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3036
+ disabled: !writable,
3037
+ onClick: addCustomSlotRow,
3038
+ children: t("timeSlots.addCustom")
3039
+ })
3040
+ ]
3041
+ })
3042
+ ]
3043
+ }),
3044
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3045
+ className: FallbacksCard_module_css_default.field,
3046
+ role: "group",
3047
+ "aria-labelledby": "fallbacks-root-chain",
3048
+ children: [
3049
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3050
+ className: FallbacksCard_module_css_default.fieldLabel,
3051
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3052
+ id: "fallbacks-root-chain",
3053
+ children: t("rootChain.label")
3054
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3055
+ label: t("rootChain.tooltip"),
3056
+ disabled: !writable
3057
+ })]
3058
+ }),
3059
+ state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3060
+ className: FallbacksCard_module_css_default.hint,
3061
+ children: t("catalog.error", { message: state.catalogError })
3062
+ }),
3063
+ state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3064
+ className: FallbacksCard_module_css_default.hint,
3065
+ children: t("catalog.partial", { message: state.catalogError })
3066
+ }),
3067
+ state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3068
+ className: FallbacksCard_module_css_default.hint,
3069
+ children: t("catalog.empty")
3070
+ }),
3071
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3072
+ className: FallbacksCard_module_css_default.list,
3073
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3074
+ className: FallbacksCard_module_css_default.editorCard,
3075
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3076
+ className: FallbacksCard_module_css_default.chainSelectors,
3077
+ children: allDayChainRow.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
3078
+ selector,
3079
+ catalog: catalogOf(state),
3080
+ configuredProviders: state.configuredProviders,
3081
+ disabled: !writable,
3082
+ t,
3083
+ onChange: (patch) => {
3084
+ updateAllDayChainSelector(selectorIndex, patch);
3085
+ },
3086
+ onRemove: () => {
3087
+ removeAllDayChainSelector(selectorIndex);
3088
+ }
3089
+ }, selectorIndex))
3090
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3091
+ variant: "outline",
3092
+ size: "sm",
3093
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3094
+ className: FallbacksCard_module_css_default.addButton,
3095
+ onClick: addAllDayChainSelector,
3096
+ children: t("timeSlots.selector.add")
3097
+ })]
3098
+ })
3099
+ })
3100
+ ]
3101
+ }),
3102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3103
+ className: FallbacksCard_module_css_default.field,
3104
+ role: "group",
3105
+ "aria-labelledby": "fallbacks-default-model",
3106
+ children: [
3107
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3108
+ className: FallbacksCard_module_css_default.fieldLabel,
3109
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3110
+ id: "fallbacks-default-model",
3111
+ children: t("defaultModel.label")
3112
+ })
3113
+ }),
3114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3115
+ className: FallbacksCard_module_css_default.hint,
3116
+ children: t("allDay.hint")
3117
+ }),
3118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3119
+ className: FallbacksCard_module_css_default.list,
3120
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3121
+ className: FallbacksCard_module_css_default.editorCard,
3122
+ children: [
3123
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3124
+ className: FallbacksCard_module_css_default.optionRow,
3125
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3126
+ type: "radio",
3127
+ name: "fallbacks-all-day",
3128
+ checked: allDayModel === ALL_DAY_FLASH,
3129
+ disabled: !writable,
3130
+ onChange: () => {
3131
+ setAllDayModel(ALL_DAY_FLASH);
3132
+ }
3133
+ }), t("allDay.flash")]
3134
+ }),
3135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3136
+ className: FallbacksCard_module_css_default.optionRow,
3137
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3138
+ type: "radio",
3139
+ name: "fallbacks-all-day",
3140
+ checked: allDayModel === ALL_DAY_PRO,
3141
+ disabled: !writable,
3142
+ onChange: () => {
3143
+ setAllDayModel(ALL_DAY_PRO);
3144
+ }
3145
+ }), t("allDay.pro")]
3146
+ }),
3147
+ allDayModel === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3148
+ className: FallbacksCard_module_css_default.hint,
3149
+ children: t("allDay.nonconforming")
3150
+ })
3151
+ ]
3152
+ })
3153
+ })
3154
+ ]
3155
+ }),
3156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3157
+ className: FallbacksCard_module_css_default.sectionHeading,
3158
+ id: "fallbacks-subagents",
3159
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3160
+ className: FallbacksCard_module_css_default.sectionHeadingText,
3161
+ children: t("subagents.label")
3162
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3163
+ className: FallbacksCard_module_css_default.sectionActions,
3164
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3165
+ type: "button",
3166
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3167
+ disabled: !subDirty || saving,
3168
+ onClick: () => {
3169
+ discardSection("sub");
3170
+ },
3171
+ children: t("discard")
3172
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3173
+ type: "button",
3174
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3175
+ disabled: !writable || saving || !subDirty,
3176
+ onClick: () => {
3177
+ save("sub");
3178
+ },
3179
+ children: saving ? t("save.saving") : t("save")
3180
+ })]
3181
+ })]
3182
+ }),
3183
+ validationErrors.sub.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3184
+ className: FallbacksCard_module_css_default.error,
3185
+ role: "alert",
3186
+ children: `${t("validation.blocked")}${validationErrors.sub.join("; ")}`
3187
+ }),
3188
+ lastSaveSection === "sub" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3189
+ className: FallbacksCard_module_css_default.error,
3190
+ role: "alert",
3191
+ children: t("error.generic", { message: state.error })
3192
+ }),
3193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3194
+ className: FallbacksCard_module_css_default.field,
3195
+ role: "group",
3196
+ "aria-labelledby": "fallbacks-roles-list",
3197
+ children: [
3198
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3199
+ className: FallbacksCard_module_css_default.fieldLabel,
3200
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3201
+ id: "fallbacks-roles-list",
3202
+ children: t("roles.list.label")
3203
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3204
+ label: t("roles.list.tooltip"),
3205
+ disabled: !writable
3206
+ })]
3207
+ }),
3208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3209
+ className: FallbacksCard_module_css_default.hint,
3210
+ children: t("roles.list.hint")
3211
+ }),
3212
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3213
+ className: FallbacksCard_module_css_default.list,
3214
+ children: roleRows.map((row, index) => {
3215
+ const invalid = invalidRoleIds?.has(row.id.trim()) ?? false;
3216
+ const seed = seededIds.get(row.id.trim());
3217
+ const roleSummary = row.selectors.map(selectorRowToRaw).find((entry) => entry !== "") ?? row.fallback;
3218
+ const roleExpanded = !row.collapsed || !writable;
3219
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3220
+ className: FallbacksCard_module_css_default.editorCard,
3221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3222
+ className: FallbacksCard_module_css_default.collapseRow,
3223
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3224
+ type: "button",
3225
+ className: FallbacksCard_module_css_default.collapseToggle,
3226
+ "aria-expanded": roleExpanded,
3227
+ "aria-label": t(roleExpanded ? "roles.collapse" : "roles.expand"),
3228
+ disabled: !writable,
3229
+ onClick: () => {
3230
+ updateRoleRow(index, { collapsed: !row.collapsed });
3231
+ },
2430
3232
  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
- }
3233
+ /* @__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 }),
3234
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3235
+ className: FallbacksCard_module_css_default.collapseTitle,
3236
+ children: row.id
2464
3237
  }),
2465
3238
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2466
- className: FallbacksCard_module_css_default.hint,
2467
- children: t("cooldownMs.hint")
3239
+ className: FallbacksCard_module_css_default.collapseMeta,
3240
+ children: roleSummary
2468
3241
  })
2469
3242
  ]
3243
+ })
3244
+ }), roleExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3245
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3246
+ className: FallbacksCard_module_css_default.ruleGrid,
3247
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3248
+ className: FallbacksCard_module_css_default.ruleCell,
3249
+ children: [
3250
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3251
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3252
+ children: t("roles.id")
3253
+ }),
3254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3255
+ className: `${FallbacksCard_module_css_default.input} ${invalid ? FallbacksCard_module_css_default.inputInvalid : ""}`,
3256
+ value: row.id,
3257
+ placeholder: t("roles.idPlaceholder"),
3258
+ "aria-label": t("roles.id"),
3259
+ "aria-invalid": invalid ? true : void 0,
3260
+ disabled: !writable || seed !== void 0,
3261
+ onChange: (event) => {
3262
+ updateRoleRow(index, { id: event.target.value });
3263
+ }
3264
+ }),
3265
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3266
+ className: FallbacksCard_module_css_default.hint,
3267
+ children: t("roles.id.hint")
3268
+ })
3269
+ ]
3270
+ })
3271
+ }),
3272
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3273
+ className: FallbacksCard_module_css_default.ruleGrid,
3274
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3275
+ className: FallbacksCard_module_css_default.ruleCell,
3276
+ children: [
3277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3278
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3279
+ children: t("roles.persona")
3280
+ }),
3281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
3282
+ rows: 3,
3283
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.inputTextarea}`,
3284
+ value: row.persona,
3285
+ placeholder: t("roles.personaPlaceholder"),
3286
+ "aria-label": t("roles.persona"),
3287
+ disabled: !writable,
3288
+ onChange: (event) => {
3289
+ updateRoleRow(index, { persona: event.target.value });
3290
+ }
3291
+ }),
3292
+ seed !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3293
+ className: FallbacksCard_module_css_default.hint,
3294
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3295
+ className: FallbacksCard_module_css_default.pending,
3296
+ children: t(seed ? "roles.seedOverride" : "roles.seedDefault")
3297
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3298
+ variant: "outline",
3299
+ size: "sm",
3300
+ disabled: !writable || saving,
3301
+ onClick: () => {
3302
+ forceReseed.current = true;
3303
+ controller.revertSeed(row.id.trim()).then((persona) => {
3304
+ if (persona === void 0) return;
3305
+ updateRoleRow(index, { persona });
3306
+ });
3307
+ },
3308
+ children: t("roles.revertPersona")
3309
+ })]
3310
+ })
3311
+ ]
3312
+ })
2470
3313
  }),
2471
3314
  /* @__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),
3315
+ className: FallbacksCard_module_css_default.chainSelectors,
3316
+ children: [row.selectors.map((selector, selectorIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorEditor, {
3317
+ selector,
3318
+ catalog: catalogOf(state),
3319
+ configuredProviders: state.configuredProviders,
3320
+ disabled: !writable,
3321
+ t,
3322
+ onChange: (patch) => {
3323
+ updateRoleSelector(index, selectorIndex, patch);
3324
+ },
3325
+ onRemove: () => {
3326
+ removeRoleSelector(index, selectorIndex);
3327
+ }
3328
+ }, selectorIndex)), row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3329
+ className: FallbacksCard_module_css_default.hint,
3330
+ children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
3331
+ })]
3332
+ }),
3333
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3334
+ className: FallbacksCard_module_css_default.ruleGrid,
3335
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3336
+ className: FallbacksCard_module_css_default.ruleCell,
3337
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3338
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3339
+ children: t("roles.fallback")
3340
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3341
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3342
+ value: row.fallback,
3343
+ "aria-label": t("roles.fallback"),
2501
3344
  disabled: !writable,
2502
3345
  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
- ]
3346
+ updateRoleRow(index, { fallback: event.target.value });
3347
+ },
3348
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3349
+ value: "inherit-root",
3350
+ children: t("roles.fallback.inherit-root")
3351
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3352
+ value: "none",
3353
+ children: t("roles.fallback.none")
3354
+ })]
3355
+ })]
3356
+ })
3357
+ }),
3358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3359
+ variant: "outline",
3360
+ size: "sm",
3361
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3362
+ className: FallbacksCard_module_css_default.addButton,
3363
+ onClick: () => {
3364
+ addRoleSelector(index);
3365
+ },
3366
+ children: t("roles.selector.add")
2513
3367
  }),
3368
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3369
+ className: FallbacksCard_module_css_default.cardFoot,
3370
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3371
+ type: "button",
3372
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
3373
+ "data-tip": t("roles.remove"),
3374
+ "aria-label": t("roles.remove"),
3375
+ onClick: () => {
3376
+ removeRole(index);
3377
+ },
3378
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
3379
+ })
3380
+ })
3381
+ ] })]
3382
+ }, index);
3383
+ })
3384
+ }),
3385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3386
+ variant: "outline",
3387
+ size: "sm",
3388
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3389
+ className: FallbacksCard_module_css_default.addButton,
3390
+ onClick: addRole,
3391
+ children: t("roles.add")
3392
+ })
3393
+ ]
3394
+ }),
3395
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3396
+ className: FallbacksCard_module_css_default.field,
3397
+ role: "group",
3398
+ "aria-labelledby": "fallbacks-roles-rules",
3399
+ children: [
3400
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3401
+ className: FallbacksCard_module_css_default.fieldLabel,
3402
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3403
+ id: "fallbacks-roles-rules",
3404
+ children: t("roles.rules")
3405
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3406
+ label: t("roles.rules.tooltip"),
3407
+ disabled: !writable
3408
+ })]
3409
+ }),
3410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3411
+ className: FallbacksCard_module_css_default.hint,
3412
+ children: t("roles.rules.hint")
3413
+ }),
3414
+ state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3415
+ className: FallbacksCard_module_css_default.hint,
3416
+ children: t("catalog.error", { message: state.catalogError })
3417
+ }),
3418
+ state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3419
+ className: FallbacksCard_module_css_default.hint,
3420
+ children: t("catalog.partial", { message: state.catalogError })
3421
+ }),
3422
+ state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3423
+ className: FallbacksCard_module_css_default.hint,
3424
+ children: t("catalog.empty")
3425
+ }),
3426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3427
+ className: FallbacksCard_module_css_default.list,
3428
+ children: ruleRows.map((row, index) => {
3429
+ const catalog = catalogOf(state);
3430
+ const providerRaw = selectionToRaw(row.provider);
3431
+ const group = catalog?.groups.find((entry) => entry.id === providerRaw);
3432
+ const providerOutside = row.provider?.kind === "outside";
3433
+ const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !state.configuredProviders.some((entry) => entry.provider === providerRaw);
3434
+ const modelOutside = row.model?.kind === "outside";
3435
+ const roleOutside = row.role !== "" && !roleOptions.includes(row.role);
3436
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3437
+ className: FallbacksCard_module_css_default.editorCard,
3438
+ children: [
2514
3439
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2515
- className: FallbacksCard_module_css_default.field,
3440
+ className: FallbacksCard_module_css_default.ruleGrid,
2516
3441
  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
- ]
3442
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3443
+ className: FallbacksCard_module_css_default.ruleCell,
3444
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3445
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3446
+ children: t("roles.rule.provider")
3447
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3448
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3449
+ value: providerRaw,
3450
+ onChange: (event) => {
3451
+ if (event.target.value === providerRaw) return;
3452
+ updateRuleRow(index, {
3453
+ provider: classifyProvider(event.target.value, catalog),
3454
+ model: null
3455
+ });
3456
+ },
3457
+ children: [
3458
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3459
+ value: "",
3460
+ children: t("roles.rule.provider.any")
3461
+ }),
3462
+ state.configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3463
+ value: entry.provider,
3464
+ children: entry.displayName
3465
+ }, entry.provider)),
3466
+ providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3467
+ value: providerRaw,
3468
+ children: `${providerRaw}${t("catalog.unconfigured.short")}`
3469
+ }),
3470
+ providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3471
+ value: providerRaw,
3472
+ children: `${providerRaw}${t("catalog.outside.short")}`
3473
+ })
3474
+ ]
3475
+ })]
2537
3476
  }),
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
- }
3477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3478
+ className: FallbacksCard_module_css_default.ruleCell,
3479
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3480
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3481
+ children: t("roles.rule.model")
3482
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3483
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3484
+ value: selectionToRaw(row.model),
3485
+ onChange: (event) => {
3486
+ updateRuleRow(index, { model: classifyModel(providerRaw, event.target.value, catalog) });
3487
+ },
3488
+ children: [
3489
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3490
+ value: "",
3491
+ children: t("roles.rule.model.any")
3492
+ }),
3493
+ (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3494
+ value: model.id,
3495
+ children: model.name
3496
+ }, model.id)),
3497
+ modelOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3498
+ value: selectionToRaw(row.model),
3499
+ children: `${selectionToRaw(row.model)}${t("catalog.outside.short")}`
3500
+ })
3501
+ ]
3502
+ })]
2550
3503
  }),
2551
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2552
- className: FallbacksCard_module_css_default.hint,
2553
- children: t("alwaysModeRetryCap.hint")
3504
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3505
+ className: FallbacksCard_module_css_default.ruleCell,
3506
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3507
+ className: FallbacksCard_module_css_default.ruleCellLabel,
3508
+ children: t("roles.rule.role")
3509
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3510
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
3511
+ value: row.role,
3512
+ disabled: !writable,
3513
+ onChange: (event) => {
3514
+ updateRuleRow(index, { role: event.target.value });
3515
+ },
3516
+ children: [
3517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3518
+ value: "",
3519
+ children: t("roles.rule.roleSelectPlaceholder")
3520
+ }),
3521
+ roleOptions.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3522
+ value: id,
3523
+ children: id === "inherit" ? t("roles.rule.role.inherit") : id
3524
+ }, id)),
3525
+ roleOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3526
+ value: row.role,
3527
+ children: `${row.role}${t("roles.rule.roleUndeclared.short")}`
3528
+ })
3529
+ ]
3530
+ })]
2554
3531
  })
2555
3532
  ]
3533
+ }),
3534
+ (providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3535
+ className: FallbacksCard_module_css_default.hint,
3536
+ children: [t("catalog.outside.hint"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3537
+ label: t("catalog.outside.tooltip"),
3538
+ disabled: !writable
3539
+ })]
3540
+ }),
3541
+ row.role === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3542
+ className: FallbacksCard_module_css_default.hint,
3543
+ children: t("validation.ruleRoleRequired")
3544
+ }),
3545
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3546
+ className: FallbacksCard_module_css_default.cardFoot,
3547
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3548
+ type: "button",
3549
+ className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
3550
+ "data-tip": t("roles.removeRule"),
3551
+ "aria-label": t("roles.removeRule"),
3552
+ onClick: () => {
3553
+ setRuleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
3554
+ },
3555
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
3556
+ })
2556
3557
  })
2557
3558
  ]
2558
- })
2559
- ]
2560
- })]
2561
- })
2562
- ]
2563
- })
2564
- ]
2565
- }),
2566
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2567
- className: FallbacksCard_module_css_default.statusBlock,
3559
+ }, index);
3560
+ })
3561
+ }),
3562
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3563
+ variant: "outline",
3564
+ size: "sm",
3565
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
3566
+ className: FallbacksCard_module_css_default.addButton,
3567
+ onClick: () => {
3568
+ setRuleRows((rows) => [...rows, {
3569
+ provider: null,
3570
+ model: null,
3571
+ role: ""
3572
+ }]);
3573
+ },
3574
+ children: t("roles.addRule")
3575
+ })
3576
+ ]
3577
+ }),
3578
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3579
+ className: FallbacksCard_module_css_default.field,
3580
+ role: "group",
3581
+ "aria-labelledby": "fallbacks-advanced",
3582
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3583
+ type: "button",
3584
+ className: FallbacksCard_module_css_default.sectionToggle,
3585
+ disabled: !writable,
3586
+ "aria-expanded": advancedVisible,
3587
+ "aria-controls": advancedVisible ? "fallbacks-advanced-body" : void 0,
3588
+ "aria-label": t(advancedVisible ? "advanced.collapse" : "advanced.expand"),
3589
+ onClick: () => {
3590
+ if (writable) setAdvancedOpen(!advancedOpen);
3591
+ },
3592
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3593
+ id: "fallbacks-advanced",
3594
+ className: FallbacksCard_module_css_default.sectionToggleText,
3595
+ children: t("advanced.label")
3596
+ }), /* @__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 })]
3597
+ }), advancedVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3598
+ id: "fallbacks-advanced-body",
3599
+ children: [
3600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3601
+ className: FallbacksCard_module_css_default.checkboxRow,
3602
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3603
+ className: FallbacksCard_module_css_default.checkLabel,
3604
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3605
+ className: FallbacksCard_module_css_default.checkLabelTitle,
3606
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3607
+ htmlFor: "fallbacks-role-automatch",
3608
+ children: t("roleAutoMatch.label")
3609
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3610
+ label: t("roleAutoMatch.tooltip"),
3611
+ disabled: !writable
3612
+ })]
3613
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3614
+ className: FallbacksCard_module_css_default.checkLabelDesc,
3615
+ children: t("roleAutoMatch.hint")
3616
+ })]
3617
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3618
+ id: "fallbacks-role-automatch",
3619
+ type: "checkbox",
3620
+ className: FallbacksCard_module_css_default.checkbox,
3621
+ checked: scalars.roleAutoMatch,
3622
+ disabled: !writable,
3623
+ onChange: (event) => {
3624
+ updateScalars((draft) => {
3625
+ draft.roleAutoMatch = event.target.checked;
3626
+ });
3627
+ }
3628
+ })]
3629
+ }),
3630
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3631
+ className: FallbacksCard_module_css_default.field,
3632
+ role: "group",
3633
+ "aria-labelledby": "fallbacks-trigger-codes",
3634
+ children: [
3635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3636
+ className: FallbacksCard_module_css_default.fieldLabel,
3637
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3638
+ id: "fallbacks-trigger-codes",
3639
+ children: t("triggerCodes.label")
3640
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3641
+ label: t("triggerCodes.tooltip"),
3642
+ disabled: !writable
3643
+ })]
3644
+ }),
3645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3646
+ className: FallbacksCard_module_css_default.hint,
3647
+ children: t("triggerCodes.hint")
3648
+ }),
3649
+ KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3650
+ className: FallbacksCard_module_css_default.optionRow,
3651
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3652
+ type: "checkbox",
3653
+ checked: scalars.triggerCodes.includes(code),
3654
+ onChange: (event) => {
3655
+ updateScalars((draft) => {
3656
+ draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
3657
+ });
3658
+ }
3659
+ }), t(TRIGGER_CODE_LABELS[code])]
3660
+ }, code)),
3661
+ unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3662
+ className: FallbacksCard_module_css_default.hint,
3663
+ children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
3664
+ })
3665
+ ]
3666
+ }),
3667
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3668
+ className: FallbacksCard_module_css_default.field,
3669
+ role: "group",
3670
+ "aria-labelledby": "fallbacks-revert-policy",
3671
+ children: [
3672
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3673
+ className: FallbacksCard_module_css_default.fieldLabel,
3674
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3675
+ id: "fallbacks-revert-policy",
3676
+ children: t("revertPolicy.label")
3677
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3678
+ label: t("revertPolicy.tooltip"),
3679
+ disabled: !writable
3680
+ })]
3681
+ }),
3682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3683
+ className: FallbacksCard_module_css_default.hint,
3684
+ children: t("revertPolicy.hint")
3685
+ }),
3686
+ ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3687
+ className: FallbacksCard_module_css_default.optionRow,
3688
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3689
+ type: "radio",
3690
+ name: "fallbacks-revert-policy",
3691
+ checked: scalars.revertPolicy === policy,
3692
+ onChange: () => {
3693
+ updateScalars((draft) => {
3694
+ draft.revertPolicy = policy;
3695
+ });
3696
+ }
3697
+ }), t(`revertPolicy.${policy}`)]
3698
+ }, policy))
3699
+ ]
3700
+ }),
3701
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3702
+ className: FallbacksCard_module_css_default.numberFields,
3703
+ children: [
3704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3705
+ className: FallbacksCard_module_css_default.field,
3706
+ children: [
3707
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3708
+ className: FallbacksCard_module_css_default.fieldLabel,
3709
+ children: [
3710
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3711
+ htmlFor: "fallbacks-cooldown-ms",
3712
+ children: t("cooldownMs.label")
3713
+ }),
3714
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3715
+ label: t("cooldownMs.tooltip"),
3716
+ disabled: !writable
3717
+ }),
3718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3719
+ className: FallbacksCard_module_css_default.defaultNote,
3720
+ children: [
3721
+ t("defaults.prefix"),
3722
+ ": ",
3723
+ state.config.cooldownMs
3724
+ ]
3725
+ })
3726
+ ]
3727
+ }),
3728
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3729
+ id: "fallbacks-cooldown-ms",
3730
+ className: FallbacksCard_module_css_default.input,
3731
+ type: "number",
3732
+ min: 0,
3733
+ value: String(scalars.cooldownMs),
3734
+ disabled: !writable,
3735
+ onChange: (event) => {
3736
+ updateScalars((draft) => {
3737
+ draft.cooldownMs = parseCount(event.target.value);
3738
+ });
3739
+ }
3740
+ }),
3741
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3742
+ className: FallbacksCard_module_css_default.hint,
3743
+ children: t("cooldownMs.hint")
3744
+ })
3745
+ ]
3746
+ }),
3747
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3748
+ className: FallbacksCard_module_css_default.field,
3749
+ children: [
3750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3751
+ className: FallbacksCard_module_css_default.fieldLabel,
3752
+ children: [
3753
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3754
+ htmlFor: "fallbacks-max-switches",
3755
+ children: t("maxSwitchesPerStep.label")
3756
+ }),
3757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3758
+ label: t("maxSwitchesPerStep.tooltip"),
3759
+ disabled: !writable
3760
+ }),
3761
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3762
+ className: FallbacksCard_module_css_default.defaultNote,
3763
+ children: [
3764
+ t("defaults.prefix"),
3765
+ ": ",
3766
+ state.config.maxSwitchesPerStep
3767
+ ]
3768
+ })
3769
+ ]
3770
+ }),
3771
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3772
+ id: "fallbacks-max-switches",
3773
+ className: FallbacksCard_module_css_default.input,
3774
+ type: "number",
3775
+ min: 0,
3776
+ value: String(scalars.maxSwitchesPerStep),
3777
+ disabled: !writable,
3778
+ onChange: (event) => {
3779
+ updateScalars((draft) => {
3780
+ draft.maxSwitchesPerStep = parseCount(event.target.value);
3781
+ });
3782
+ }
3783
+ }),
3784
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3785
+ className: FallbacksCard_module_css_default.hint,
3786
+ children: t("maxSwitchesPerStep.hint")
3787
+ })
3788
+ ]
3789
+ }),
3790
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3791
+ className: FallbacksCard_module_css_default.field,
3792
+ children: [
3793
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3794
+ className: FallbacksCard_module_css_default.fieldLabel,
3795
+ children: [
3796
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
3797
+ htmlFor: "fallbacks-always-cap",
3798
+ children: t("alwaysModeRetryCap.label")
3799
+ }),
3800
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
3801
+ label: t("alwaysModeRetryCap.tooltip"),
3802
+ disabled: !writable
3803
+ }),
3804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3805
+ className: FallbacksCard_module_css_default.defaultNote,
3806
+ children: [
3807
+ t("defaults.prefix"),
3808
+ ": ",
3809
+ state.config.alwaysModeRetryCap
3810
+ ]
3811
+ })
3812
+ ]
3813
+ }),
3814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3815
+ id: "fallbacks-always-cap",
3816
+ className: FallbacksCard_module_css_default.input,
3817
+ type: "number",
3818
+ min: 0,
3819
+ value: String(scalars.alwaysModeRetryCap),
3820
+ disabled: !writable,
3821
+ onChange: (event) => {
3822
+ updateScalars((draft) => {
3823
+ draft.alwaysModeRetryCap = parseCount(event.target.value);
3824
+ });
3825
+ }
3826
+ }),
3827
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3828
+ className: FallbacksCard_module_css_default.hint,
3829
+ children: t("alwaysModeRetryCap.hint")
3830
+ })
3831
+ ]
3832
+ })
3833
+ ]
3834
+ }),
3835
+ validationErrors.advanced.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3836
+ className: FallbacksCard_module_css_default.error,
3837
+ role: "alert",
3838
+ children: `${t("validation.blocked")}${validationErrors.advanced.join("; ")}`
3839
+ }),
3840
+ lastSaveSection === "advanced" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3841
+ className: FallbacksCard_module_css_default.error,
3842
+ role: "alert",
3843
+ children: t("error.generic", { message: state.error })
3844
+ }),
3845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3846
+ className: FallbacksCard_module_css_default.sectionActions,
3847
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3848
+ type: "button",
3849
+ className: `${FallbacksCard_module_css_default.secondaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3850
+ disabled: !advancedDirty || saving,
3851
+ onClick: () => {
3852
+ discardSection("advanced");
3853
+ },
3854
+ children: t("discard")
3855
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3856
+ type: "button",
3857
+ className: `${FallbacksCard_module_css_default.primaryButton} ${FallbacksCard_module_css_default.sectionAction}`,
3858
+ disabled: !writable || saving || !advancedDirty,
3859
+ onClick: () => {
3860
+ save("advanced");
3861
+ },
3862
+ children: saving ? t("save.saving") : t("save")
3863
+ })]
3864
+ })
3865
+ ]
3866
+ })]
3867
+ })
3868
+ ]
3869
+ })
3870
+ ]
3871
+ }),
3872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3873
+ className: FallbacksCard_module_css_default.statusBlock,
3874
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3875
+ className: FallbacksCard_module_css_default.statusTitle,
3876
+ children: t("status.title")
3877
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
3878
+ className: FallbacksCard_module_css_default.statusLine,
3879
+ role: state.switchesStatus === "error" ? "alert" : void 0,
2568
3880
  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
- ]
3881
+ className: FallbacksCard_module_css_default.statusLineLabel,
3882
+ children: t("status.switches.label")
3883
+ }), switchesLine]
3884
+ })]
3885
+ })
3886
+ ]
3887
+ })]
2636
3888
  });
2637
3889
  }
2638
3890
  //#endregion