dsh-llm-fallbacks 0.6.13 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -30
- package/README.zh-CN.md +32 -28
- package/dist/chain-walk.d.ts +197 -0
- package/dist/chains.d.ts +35 -82
- package/dist/client/FallbacksCard.d.ts +27 -80
- package/dist/client/fallbacks-store.d.ts +71 -225
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.js +1332 -2110
- package/dist/client/locales.d.ts +61 -117
- package/dist/commands.d.ts +70 -112
- package/dist/config.d.ts +152 -117
- package/dist/cooldown.d.ts +1 -15
- package/dist/events.d.ts +8 -8
- package/dist/gateway.d.ts +66 -110
- package/dist/index.d.ts +21 -64
- package/dist/index.js +900 -1313
- package/dist/role-resolution.d.ts +15 -83
- package/dist/schema.d.ts +6 -5
- package/dist/seeds.d.ts +5 -7
- package/dist/state.d.ts +15 -141
- package/dist/subagents-seam.d.ts +51 -14
- package/dist/time-slots.d.ts +21 -24
- package/dist/tui-settings.d.ts +5 -6
- package/dist/virtual-adapter.d.ts +70 -119
- package/docs/DETAIL.md +1 -0
- package/docs/DETAIL.zh.md +1 -0
- package/docs/preview.svg +47 -0
- package/docs/preview.zh.svg +47 -0
- package/locale/en.json +2 -1
- package/locale/zh.json +2 -1
- package/package.json +29 -25
- package/dist/authorized-route.d.ts +0 -68
- package/dist/automatch.d.ts +0 -98
- package/dist/client/GeneralFallbacksRow.d.ts +0 -19
- package/dist/override.d.ts +0 -39
- package/dist/roles.d.ts +0 -90
- package/dist/route-allowlist.d.ts +0 -43
- package/dist/subagent-policy.d.ts +0 -123
package/dist/client/index.js
CHANGED
|
@@ -146,26 +146,9 @@ window.__ModuleLoader__.load({
|
|
|
146
146
|
return value;
|
|
147
147
|
};
|
|
148
148
|
}));
|
|
149
|
-
|
|
150
|
-
//#region src/client/use-snapshot.ts
|
|
151
|
-
var import_with_selector = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
149
|
+
(/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
152
150
|
module.exports = require_with_selector_production();
|
|
153
151
|
})))();
|
|
154
|
-
/**
|
|
155
|
-
* Bind a bare observable source to a typed uSES selector hook.
|
|
156
|
-
* subscribe/getSnapshot are captured once per source into stable closures
|
|
157
|
-
* (also re-binds `this` for method-based sources), so components never
|
|
158
|
-
* resubscribe across renders. Equality defaults to Object.is.
|
|
159
|
-
* @param w - snapshot source (engine store, Session object, store instance).
|
|
160
|
-
* @returns the selector hook.
|
|
161
|
-
*/
|
|
162
|
-
function bindSnapshotSelector(w) {
|
|
163
|
-
const subscribe = (fn) => w.subscribe(fn);
|
|
164
|
-
const getSnapshot = () => w.getSnapshot();
|
|
165
|
-
return function useSelector(sel, eq) {
|
|
166
|
-
return (0, import_with_selector.useSyncExternalStoreWithSelector)(subscribe, getSnapshot, void 0, sel, eq);
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
152
|
//#endregion
|
|
170
153
|
//#region src/selectors.ts
|
|
171
154
|
/** Catchable error for illegal/unknown selectors (config-warning path). */
|
|
@@ -423,25 +406,24 @@ window.__ModuleLoader__.load({
|
|
|
423
406
|
return isOfficialAllDayId(tail);
|
|
424
407
|
}
|
|
425
408
|
/**
|
|
426
|
-
* Slot winner + display label
|
|
427
|
-
* last-winner marker, in-process only) and the card /
|
|
428
|
-
* strip. `winner` is the matching row or `'all-day'`;
|
|
429
|
-
* slot (frozen preset label or `custom HH:mm-HH:mm`).
|
|
409
|
+
* Slot winner + display label for one named chain: drives 分时切换 detection
|
|
410
|
+
* (per-(agent, chain) last-winner marker, in-process only) and the card /
|
|
411
|
+
* `/fallbacks` status strip. `winner` is the matching row or `'all-day'`;
|
|
412
|
+
* `label` names the slot (frozen preset label or `custom HH:mm-HH:mm`).
|
|
430
413
|
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* the raw `rootChain` (the legacy walk verbatim).
|
|
414
|
+
* Gate: without a conforming entries tail (`isAllDayConforming(chain.entries)`)
|
|
415
|
+
* the winner is ALWAYS `'all-day'` — a multi-model (or empty) chain earns no
|
|
416
|
+
* slot rows, so every surface fed by this resolver reports the inert state and
|
|
417
|
+
* routing stays on the raw `entries`.
|
|
436
418
|
*/
|
|
437
|
-
function resolveSlotState(
|
|
438
|
-
if (!isAllDayConforming(
|
|
419
|
+
function resolveSlotState(chain, now, tz) {
|
|
420
|
+
if (!isAllDayConforming(chain.entries)) return {
|
|
439
421
|
winner: "all-day",
|
|
440
422
|
label: "all-day"
|
|
441
423
|
};
|
|
442
424
|
const clock = wallClock(now, tz);
|
|
443
425
|
const seenPresets = /* @__PURE__ */ new Set();
|
|
444
|
-
const rows = Array.isArray(
|
|
426
|
+
const rows = Array.isArray(chain.timeSlots) ? chain.timeSlots : [];
|
|
445
427
|
for (const row of rows) {
|
|
446
428
|
if (typeof row !== "object" || row === null) {
|
|
447
429
|
warnMalformed(row, "row is not an object");
|
|
@@ -468,12 +450,17 @@ window.__ModuleLoader__.load({
|
|
|
468
450
|
}
|
|
469
451
|
//#endregion
|
|
470
452
|
//#region src/config.ts
|
|
453
|
+
/** Chain id format (v2): case-sensitive, uppercase allowed so the migrated
|
|
454
|
+
* default id is exactly `Auto`. Deliberately NOT {@link ROLE_ID_PATTERN},
|
|
455
|
+
* which keeps governing role ids. */
|
|
456
|
+
const CHAIN_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{0,31}$/;
|
|
471
457
|
/**
|
|
472
|
-
* Spec §
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
* (
|
|
458
|
+
* Spec §1 defaults — `Config({})` must equal this (no-op install).
|
|
459
|
+
* Empty `chains` is the ONLY first-run state: nothing pre-creates an
|
|
460
|
+
* `Auto` chain and nothing seeds `entries` — the picker row appears only
|
|
461
|
+
* after the user creates a chain in the card. The migrated/seeded default
|
|
462
|
+
* chain (`{ id: 'Auto', label: '默认链', entries, timeSlots }`) exists only
|
|
463
|
+
* in the migration table (docs) applied by hand — warn-only migration.
|
|
477
464
|
*/
|
|
478
465
|
const defaultFallbacksConfig = {
|
|
479
466
|
triggerCodes: [
|
|
@@ -481,52 +468,18 @@ window.__ModuleLoader__.load({
|
|
|
481
468
|
"QUOTA",
|
|
482
469
|
"RATE_LIMIT"
|
|
483
470
|
],
|
|
484
|
-
|
|
485
|
-
roles: {
|
|
486
|
-
list: [],
|
|
487
|
-
rules: []
|
|
488
|
-
},
|
|
471
|
+
chains: [],
|
|
472
|
+
roles: { list: [] },
|
|
489
473
|
cooldownMs: 3e5,
|
|
490
474
|
revertPolicy: "cooldown-expiry",
|
|
491
475
|
maxSwitchesPerStep: 8,
|
|
492
|
-
alwaysModeRetryCap: 5,
|
|
493
476
|
presets: "bundled",
|
|
494
|
-
roleAutoMatch: true,
|
|
495
|
-
timeSlots: [],
|
|
496
477
|
tz: "Asia/Shanghai",
|
|
497
478
|
recovery: "timer"
|
|
498
479
|
};
|
|
499
|
-
/**
|
|
500
|
-
* Reserved role id: legal as a rule target (`roles.rules[].role`) and as
|
|
501
|
-
* the no-rule-match fallback, but FORBIDDEN in `roles.list[].id`.
|
|
502
|
-
*/
|
|
503
|
-
const INHERIT_ROLE_ID = "inherit";
|
|
504
|
-
/**
|
|
505
|
-
* The no-op gate (AC-8), re-keyed from the removed `enabled` switch to
|
|
506
|
-
* CONTENT PRESENCE (plan fallbacks-web-ux-alignment T2): the plugin
|
|
507
|
-
* intervenes exactly when something is configured — a non-empty `rootChain`
|
|
508
|
-
* (the all-day chain), at least one time-slot row, or at least one declared
|
|
509
|
-
* role. An inactive config is a pass-through exactly like an uninstalled
|
|
510
|
-
* plugin; the Plugins-page row toggle remains the master switch (row
|
|
511
|
-
* disable = fiber destroy). ONE helper for every runtime gate — the former
|
|
512
|
-
* `config.enabled` sites (request waterfalls, picker-row registration, TUI
|
|
513
|
-
* summary, General-row badge) all read through this.
|
|
514
|
-
*/
|
|
515
|
-
function isFallbackActive(config) {
|
|
516
|
-
return config.rootChain.length > 0 || (config.timeSlots?.length ?? 0) > 0 || config.roles.list.length > 0;
|
|
517
|
-
}
|
|
518
480
|
/** Role id format (aligned with yet-another-subagent `isValidProfileId`). */
|
|
519
481
|
const ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
520
482
|
//#endregion
|
|
521
|
-
//#region src/route-allowlist.ts
|
|
522
|
-
/**
|
|
523
|
-
* Whether the route is on the allowlist under exact route identity: both the
|
|
524
|
-
* provider and the model strings must equal an allowed route exactly.
|
|
525
|
-
*/
|
|
526
|
-
function routeOnAllowlist(route, allowedModels) {
|
|
527
|
-
return allowedModels.some((allowed) => allowed.provider === route.provider && allowed.model === route.model);
|
|
528
|
-
}
|
|
529
|
-
//#endregion
|
|
530
483
|
//#region src/client/fallbacks-store.ts
|
|
531
484
|
/** The plugin's settings namespace on the host wire (settings/document-updated ns filter). */
|
|
532
485
|
const FALLBACKS_SETTINGS_NS = "fallbacks";
|
|
@@ -582,56 +535,6 @@ window.__ModuleLoader__.load({
|
|
|
582
535
|
}
|
|
583
536
|
return entries;
|
|
584
537
|
}
|
|
585
|
-
function parsePolicyRoute(value) {
|
|
586
|
-
if (!isRecord(value)) return void 0;
|
|
587
|
-
if (typeof value.provider !== "string" || value.provider.length === 0) return void 0;
|
|
588
|
-
if (typeof value.model !== "string" || value.model.length === 0) return void 0;
|
|
589
|
-
return {
|
|
590
|
-
provider: value.provider,
|
|
591
|
-
model: value.model
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* Shape-guard the additive `subagentPolicy` wire field (T5). Malformed
|
|
596
|
-
* payloads resolve to `undefined` (hide the status area) — the store never
|
|
597
|
-
* throws on an old or misshapen field (old-payload tolerance).
|
|
598
|
-
*/
|
|
599
|
-
function parseSubagentPolicyWire(value) {
|
|
600
|
-
if (!isRecord(value) || typeof value.state !== "string") return void 0;
|
|
601
|
-
if (value.state === "disabled" || value.state === "unprovable") return { state: value.state };
|
|
602
|
-
if (value.state !== "enabled") return void 0;
|
|
603
|
-
if (!Array.isArray(value.allowedModels)) return void 0;
|
|
604
|
-
const allowedModels = [];
|
|
605
|
-
for (const entry of value.allowedModels) {
|
|
606
|
-
const route = parsePolicyRoute(entry);
|
|
607
|
-
if (route === void 0) return void 0;
|
|
608
|
-
allowedModels.push(route);
|
|
609
|
-
}
|
|
610
|
-
const view = {
|
|
611
|
-
state: "enabled",
|
|
612
|
-
allowedModels
|
|
613
|
-
};
|
|
614
|
-
if (isRecord(value.head) && (value.head.source === "authorized" || value.head.source === "injected")) {
|
|
615
|
-
const route = parsePolicyRoute(value.head.route);
|
|
616
|
-
if (route !== void 0) view.head = {
|
|
617
|
-
route,
|
|
618
|
-
source: value.head.source
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
if (isRecord(value.blockedAttempt) && typeof value.blockedAttempt.at === "number") {
|
|
622
|
-
const route = parsePolicyRoute(value.blockedAttempt.route);
|
|
623
|
-
if (route !== void 0) view.blockedAttempt = {
|
|
624
|
-
at: value.blockedAttempt.at,
|
|
625
|
-
route,
|
|
626
|
-
reason: typeof value.blockedAttempt.reason === "string" ? value.blockedAttempt.reason : ""
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
return view;
|
|
630
|
-
}
|
|
631
|
-
function subagentPolicyFromWire(value, fallback) {
|
|
632
|
-
if (!isRecord(value) || !("subagentPolicy" in value)) return fallback;
|
|
633
|
-
return parseSubagentPolicyWire(value.subagentPolicy);
|
|
634
|
-
}
|
|
635
538
|
/**
|
|
636
539
|
* The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
|
|
637
540
|
* settings profile resolves in the describe namespaces — the Models page's
|
|
@@ -657,8 +560,29 @@ window.__ModuleLoader__.load({
|
|
|
657
560
|
if (!isRecord(value)) throw new TypeError(`fallbacks descriptor value is not an object: ${String(value)}`);
|
|
658
561
|
const triggerCodes = value.triggerCodes;
|
|
659
562
|
if (triggerCodes !== void 0 && (!Array.isArray(triggerCodes) || triggerCodes.some((code) => typeof code !== "string"))) throw new TypeError("fallbacks descriptor triggerCodes must be a string array");
|
|
660
|
-
const
|
|
661
|
-
if (
|
|
563
|
+
const chains = value.chains;
|
|
564
|
+
if (chains !== void 0 && (!Array.isArray(chains) || chains.some((chain) => {
|
|
565
|
+
if (!isRecord(chain)) return true;
|
|
566
|
+
if (typeof chain.id !== "string" || typeof chain.label !== "string") return true;
|
|
567
|
+
const entries = chain.entries;
|
|
568
|
+
if (entries !== void 0 && (!Array.isArray(entries) || entries.some((entry) => typeof entry !== "string"))) return true;
|
|
569
|
+
const slots = chain.timeSlots;
|
|
570
|
+
if (slots !== void 0 && (!Array.isArray(slots) || slots.some((row) => {
|
|
571
|
+
if (!isRecord(row)) return true;
|
|
572
|
+
for (const field of [
|
|
573
|
+
"kind",
|
|
574
|
+
"preset",
|
|
575
|
+
"start",
|
|
576
|
+
"end"
|
|
577
|
+
]) if (row[field] !== void 0 && typeof row[field] !== "string") return true;
|
|
578
|
+
const rowChain = row.chain;
|
|
579
|
+
if (rowChain !== void 0 && (!Array.isArray(rowChain) || rowChain.some((entry) => typeof entry !== "string"))) return true;
|
|
580
|
+
const days = row.days;
|
|
581
|
+
if (days !== void 0 && (!Array.isArray(days) || days.some((day) => typeof day !== "number"))) return true;
|
|
582
|
+
return false;
|
|
583
|
+
}))) return true;
|
|
584
|
+
return false;
|
|
585
|
+
}))) throw new TypeError("fallbacks descriptor chains must be an array of named chains ({ id, label, entries, timeSlots })");
|
|
662
586
|
const roles = isRecord(value.roles) ? value.roles : {};
|
|
663
587
|
const parsedList = (Array.isArray(roles.list) ? roles.list : []).map((role, index) => {
|
|
664
588
|
if (!isRecord(role) || typeof role.id !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}] must have a string id`);
|
|
@@ -668,81 +592,37 @@ window.__ModuleLoader__.load({
|
|
|
668
592
|
if (prompt !== void 0 && typeof prompt !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].prompt must be a string`);
|
|
669
593
|
const permissions = role.permissions;
|
|
670
594
|
if (permissions !== void 0 && (!isRecord(permissions) || permissions.allow !== void 0 && (!Array.isArray(permissions.allow) || permissions.allow.some((item) => typeof item !== "string")) || permissions.deny !== void 0 && (!Array.isArray(permissions.deny) || permissions.deny.some((item) => typeof item !== "string")))) throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].permissions must be an allow/deny string-array object`);
|
|
671
|
-
const chain = role.chain;
|
|
672
|
-
if (chain !== void 0 && (!Array.isArray(chain) || chain.some((entry) => typeof entry !== "string"))) throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].chain must be a string array`);
|
|
673
|
-
const fallback = role.fallback;
|
|
674
|
-
if (fallback !== void 0 && fallback !== "inherit-root" && fallback !== "none") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].fallback must be inherit-root|none`);
|
|
595
|
+
const chain = typeof role.chain === "string" ? role.chain : void 0;
|
|
675
596
|
return {
|
|
676
597
|
id: role.id,
|
|
677
598
|
persona: persona ?? "",
|
|
678
599
|
...prompt === void 0 ? {} : { prompt },
|
|
679
600
|
...permissions === void 0 ? {} : { permissions },
|
|
680
|
-
chain: chain
|
|
681
|
-
fallback: fallback ?? "inherit-root"
|
|
682
|
-
};
|
|
683
|
-
});
|
|
684
|
-
const parsedRules = (Array.isArray(roles.rules) ? roles.rules : []).map((rule, index) => {
|
|
685
|
-
if (!isRecord(rule) || typeof rule.role !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}] must have a string role`);
|
|
686
|
-
const origin = rule.origin;
|
|
687
|
-
if (origin !== void 0 && origin !== "root" && origin !== "subagent") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].origin must be root|subagent`);
|
|
688
|
-
const provider = rule.provider;
|
|
689
|
-
const model = rule.model;
|
|
690
|
-
if (provider !== void 0 && typeof provider !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].provider must be a string`);
|
|
691
|
-
if (model !== void 0 && typeof model !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].model must be a string`);
|
|
692
|
-
return {
|
|
693
|
-
...origin === void 0 ? {} : { origin },
|
|
694
|
-
...provider === void 0 ? {} : { provider },
|
|
695
|
-
...model === void 0 ? {} : { model },
|
|
696
|
-
role: rule.role
|
|
601
|
+
...chain === void 0 || chain === "" ? {} : { chain }
|
|
697
602
|
};
|
|
698
603
|
});
|
|
699
604
|
const cooldownMs = value.cooldownMs;
|
|
700
605
|
const maxSwitchesPerStep = value.maxSwitchesPerStep;
|
|
701
|
-
const
|
|
702
|
-
for (const [field, raw] of [
|
|
703
|
-
["cooldownMs", cooldownMs],
|
|
704
|
-
["maxSwitchesPerStep", maxSwitchesPerStep],
|
|
705
|
-
["alwaysModeRetryCap", alwaysModeRetryCap]
|
|
706
|
-
]) if (raw !== void 0 && typeof raw !== "number") throw new TypeError(`fallbacks descriptor ${field} must be a number`);
|
|
606
|
+
for (const [field, raw] of [["cooldownMs", cooldownMs], ["maxSwitchesPerStep", maxSwitchesPerStep]]) if (raw !== void 0 && typeof raw !== "number") throw new TypeError(`fallbacks descriptor ${field} must be a number`);
|
|
707
607
|
const revertPolicy = value.revertPolicy;
|
|
708
608
|
if (revertPolicy !== void 0 && revertPolicy !== "cooldown-expiry" && revertPolicy !== "never") throw new TypeError("fallbacks descriptor revertPolicy must be cooldown-expiry|never");
|
|
709
609
|
const presets = value.presets;
|
|
710
610
|
if (presets !== void 0 && presets !== "bundled" && presets !== "none") throw new TypeError("fallbacks descriptor presets must be bundled|none");
|
|
711
|
-
const roleAutoMatch = value.roleAutoMatch;
|
|
712
|
-
if (roleAutoMatch !== void 0 && typeof roleAutoMatch !== "boolean") throw new TypeError("fallbacks descriptor roleAutoMatch must be a boolean");
|
|
713
|
-
const timeSlots = value.timeSlots;
|
|
714
|
-
if (timeSlots !== void 0 && (!Array.isArray(timeSlots) || timeSlots.some((row) => {
|
|
715
|
-
if (!isRecord(row)) return true;
|
|
716
|
-
for (const field of [
|
|
717
|
-
"kind",
|
|
718
|
-
"preset",
|
|
719
|
-
"start",
|
|
720
|
-
"end"
|
|
721
|
-
]) if (row[field] !== void 0 && typeof row[field] !== "string") return true;
|
|
722
|
-
const chain = row.chain;
|
|
723
|
-
if (chain !== void 0 && (!Array.isArray(chain) || chain.some((entry) => typeof entry !== "string"))) return true;
|
|
724
|
-
const days = row.days;
|
|
725
|
-
if (days !== void 0 && (!Array.isArray(days) || days.some((day) => typeof day !== "number"))) return true;
|
|
726
|
-
return false;
|
|
727
|
-
}))) throw new TypeError("fallbacks descriptor timeSlots must be an array of slot rows (kind/preset/start/end strings, chain string array, days number array)");
|
|
728
611
|
const tz = value.tz;
|
|
729
612
|
if (tz !== void 0 && typeof tz !== "string") throw new TypeError("fallbacks descriptor tz must be a string");
|
|
730
613
|
const recovery = value.recovery;
|
|
731
614
|
if (recovery !== void 0 && recovery !== "timer" && recovery !== "half-open") throw new TypeError("fallbacks descriptor recovery must be timer|half-open");
|
|
732
615
|
return {
|
|
733
616
|
triggerCodes: triggerCodes ?? [...defaultFallbacksConfig.triggerCodes],
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
},
|
|
617
|
+
chains: chains ?? defaultFallbacksConfig.chains.map((chain) => ({
|
|
618
|
+
...chain,
|
|
619
|
+
timeSlots: [...chain.timeSlots ?? []]
|
|
620
|
+
})),
|
|
621
|
+
roles: { list: parsedList },
|
|
739
622
|
cooldownMs: cooldownMs ?? defaultFallbacksConfig.cooldownMs,
|
|
740
623
|
revertPolicy: revertPolicy ?? defaultFallbacksConfig.revertPolicy,
|
|
741
624
|
maxSwitchesPerStep: maxSwitchesPerStep ?? defaultFallbacksConfig.maxSwitchesPerStep,
|
|
742
|
-
alwaysModeRetryCap: alwaysModeRetryCap ?? defaultFallbacksConfig.alwaysModeRetryCap,
|
|
743
625
|
presets: presets ?? defaultFallbacksConfig.presets,
|
|
744
|
-
roleAutoMatch: roleAutoMatch ?? defaultFallbacksConfig.roleAutoMatch,
|
|
745
|
-
timeSlots: timeSlots ?? [...defaultFallbacksConfig.timeSlots ?? []],
|
|
746
626
|
tz: tz ?? defaultFallbacksConfig.tz,
|
|
747
627
|
recovery: recovery ?? defaultFallbacksConfig.recovery
|
|
748
628
|
};
|
|
@@ -782,27 +662,6 @@ window.__ModuleLoader__.load({
|
|
|
782
662
|
raw
|
|
783
663
|
};
|
|
784
664
|
}
|
|
785
|
-
/**
|
|
786
|
-
* Extract the most recent `fallbacks/switch` events from one history page
|
|
787
|
-
* (spec §2.5 D-5): drop packed chunk runs, filter by event type, order by
|
|
788
|
-
* `seq` descending, take at most `limit`. Single-page read — fewer than
|
|
789
|
-
* `limit` events show as-is; no multi-page backfill (Non-Goal).
|
|
790
|
-
*/
|
|
791
|
-
function extractRecentSwitches(entries, limit = 5) {
|
|
792
|
-
const switches = [];
|
|
793
|
-
for (const entry of entries) {
|
|
794
|
-
if (entry.type !== "event") continue;
|
|
795
|
-
const event = entry.event;
|
|
796
|
-
if (event.type !== "fallbacks/switch") continue;
|
|
797
|
-
switches.push({
|
|
798
|
-
...event.data,
|
|
799
|
-
seq: event.seq,
|
|
800
|
-
time: event.time
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
switches.sort((a, b) => b.seq - a.seq);
|
|
804
|
-
return switches.slice(0, limit);
|
|
805
|
-
}
|
|
806
665
|
/** Serialize one selector row to its wire string (`provider/model` | `provider/*`). */
|
|
807
666
|
function selectorRowToRaw(row) {
|
|
808
667
|
const provider = selectionToRaw(row.provider);
|
|
@@ -831,22 +690,6 @@ window.__ModuleLoader__.load({
|
|
|
831
690
|
};
|
|
832
691
|
}
|
|
833
692
|
}
|
|
834
|
-
/** Project the rootChain entries into editable rows (one flat chain row). */
|
|
835
|
-
function rootChainToRows(rootChain, catalog) {
|
|
836
|
-
return [{ selectors: rootChain.map((entry) => entryToSelectorRow(entry, catalog)) }];
|
|
837
|
-
}
|
|
838
|
-
/** Rebuild the rootChain from edited rows; rows with no usable selector drop out. */
|
|
839
|
-
function rowsToRootChain(rows) {
|
|
840
|
-
const entries = [];
|
|
841
|
-
for (const row of rows) {
|
|
842
|
-
if (row.selectors.length === 0) continue;
|
|
843
|
-
for (const selector of row.selectors) {
|
|
844
|
-
const raw = selectorRowToRaw(selector);
|
|
845
|
-
if (raw !== "") entries.push(raw);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
return entries;
|
|
849
|
-
}
|
|
850
693
|
/** Project the time-slot rows into editable rows (chain selectors classified). */
|
|
851
694
|
function timeSlotsToRows(timeSlots, catalog) {
|
|
852
695
|
return timeSlots.map((row) => ({
|
|
@@ -862,11 +705,10 @@ window.__ModuleLoader__.load({
|
|
|
862
705
|
}
|
|
863
706
|
/** Rebuild the time-slot rows from edited rows; blank selectors drop out.
|
|
864
707
|
* `kind` rides verbatim (a hand-written unknown kind reads back unchanged;
|
|
865
|
-
* save validation rejects it)
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
* read back dirty. */
|
|
708
|
+
* save validation rejects it). `days` is ALWAYS serialized ([] included):
|
|
709
|
+
* schemastery composes absent array fields as `[]`, so the composed config
|
|
710
|
+
* every card load accepts carries `days` on every row — the draft must too,
|
|
711
|
+
* or a clean card would read back dirty. */
|
|
870
712
|
function rowsToTimeSlots(rows) {
|
|
871
713
|
return rows.map((row) => {
|
|
872
714
|
const chain = row.selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
|
|
@@ -887,35 +729,63 @@ window.__ModuleLoader__.load({
|
|
|
887
729
|
};
|
|
888
730
|
});
|
|
889
731
|
}
|
|
890
|
-
/** Project the
|
|
891
|
-
function
|
|
732
|
+
/** Project the named chains into editable drafts (selectors classified). */
|
|
733
|
+
function chainsToDrafts(chains, catalog) {
|
|
734
|
+
return chains.map((chain) => ({
|
|
735
|
+
id: chain.id,
|
|
736
|
+
label: chain.label,
|
|
737
|
+
selectors: chain.entries.map((entry) => entryToSelectorRow(entry, catalog)),
|
|
738
|
+
slots: timeSlotsToRows(chain.timeSlots ?? [], catalog)
|
|
739
|
+
}));
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Generate a fresh unique chain id for the card's `+` affordance. Pass the
|
|
743
|
+
* DRAFT list, not the accepted config: two unsaved additions must not both
|
|
744
|
+
* claim `chain-N` (duplicate React keys, and a section the gateway rejects).
|
|
745
|
+
*/
|
|
746
|
+
function newChainId(existing) {
|
|
747
|
+
let index = existing.length + 1;
|
|
748
|
+
let id = `chain-${index}`;
|
|
749
|
+
while (existing.some((chain) => chain.id === id)) {
|
|
750
|
+
index += 1;
|
|
751
|
+
id = `chain-${index}`;
|
|
752
|
+
}
|
|
753
|
+
return id;
|
|
754
|
+
}
|
|
755
|
+
/** Rebuild one named chain from its draft; blank selectors drop out. */
|
|
756
|
+
function draftToChain(draft) {
|
|
757
|
+
return {
|
|
758
|
+
id: draft.id,
|
|
759
|
+
label: draft.label,
|
|
760
|
+
entries: draft.selectors.map(selectorRowToRaw).filter((entry) => entry !== ""),
|
|
761
|
+
timeSlots: rowsToTimeSlots(draft.slots)
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
/** Project the declared roles into editable rows (chain pointer kept verbatim). */
|
|
765
|
+
function rolesToRows(roles) {
|
|
892
766
|
return roles.map((role) => ({
|
|
893
767
|
id: role.id,
|
|
894
768
|
persona: role.persona,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
collapsed: true,
|
|
769
|
+
chain: role.chain ?? "",
|
|
770
|
+
collapsed: false,
|
|
898
771
|
personaOpen: false
|
|
899
772
|
}));
|
|
900
773
|
}
|
|
901
|
-
/** Rebuild the declared roles from edited rows; empty
|
|
774
|
+
/** Rebuild the declared roles from edited rows; an empty pointer drops out. */
|
|
902
775
|
function rowsToRoles(rows) {
|
|
903
776
|
return rows.map((row) => ({
|
|
904
777
|
id: row.id.trim(),
|
|
905
778
|
persona: row.persona,
|
|
906
|
-
chain: row.
|
|
907
|
-
fallback: row.fallback
|
|
779
|
+
...row.chain === "" ? {} : { chain: row.chain }
|
|
908
780
|
}));
|
|
909
781
|
}
|
|
910
782
|
/**
|
|
911
783
|
* Rebuild the declared roles from edited rows, re-attaching the
|
|
912
784
|
* schema-reserved `prompt`/`permissions` fields from the last accepted
|
|
913
785
|
* config by role id — they never round-trip through rows this round, so
|
|
914
|
-
* without the merge a save would silently drop them
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
* `parseFallbacksConfig` so a clean draft's JSON dirty comparison never
|
|
918
|
-
* flags it.
|
|
786
|
+
* without the merge a save would silently drop them. The id trim matches
|
|
787
|
+
* {@link rowsToRoles}; a row whose id matches no original role (a freshly
|
|
788
|
+
* added one) keeps no extras.
|
|
919
789
|
*/
|
|
920
790
|
function mergeRoleExtras(rows, originalRoles) {
|
|
921
791
|
const originalById = new Map(originalRoles.map((role) => [role.id, role]));
|
|
@@ -927,36 +797,10 @@ window.__ModuleLoader__.load({
|
|
|
927
797
|
persona: role.persona,
|
|
928
798
|
...original.prompt === void 0 ? {} : { prompt: original.prompt },
|
|
929
799
|
...original.permissions === void 0 ? {} : { permissions: original.permissions },
|
|
930
|
-
chain: role.chain
|
|
931
|
-
fallback: role.fallback
|
|
800
|
+
...role.chain === void 0 ? {} : { chain: role.chain }
|
|
932
801
|
};
|
|
933
802
|
});
|
|
934
803
|
}
|
|
935
|
-
/**
|
|
936
|
-
* The `roles.rules` role dropdown's offer set — the ONLY data source for the
|
|
937
|
-
* rule rows' role selector: the built-in `'inherit'` target plus every
|
|
938
|
-
* declared `roles.list` id, in declaration order (a role added/removed on
|
|
939
|
-
* the same page is reflected immediately).
|
|
940
|
-
*/
|
|
941
|
-
function ruleRoleOptions(roles) {
|
|
942
|
-
return [INHERIT_ROLE_ID, ...new Set(roles.list.map((role) => role.id.trim()))];
|
|
943
|
-
}
|
|
944
|
-
/** Project the role rules into editable rows (provider/model classified). */
|
|
945
|
-
function rulesToRows(rules, catalog) {
|
|
946
|
-
return rules.map((rule) => ({
|
|
947
|
-
provider: classifyProvider(rule.provider ?? "", catalog),
|
|
948
|
-
model: classifyModel(rule.provider ?? "", rule.model ?? "", catalog),
|
|
949
|
-
role: rule.role
|
|
950
|
-
}));
|
|
951
|
-
}
|
|
952
|
-
/** Rebuild the role rules from edited rows; empty provider/model drop out. */
|
|
953
|
-
function rowsToRules(rows) {
|
|
954
|
-
return rows.map((row) => ({
|
|
955
|
-
...row.provider === null ? {} : { provider: selectionToRaw(row.provider) },
|
|
956
|
-
...row.model === null ? {} : { model: selectionToRaw(row.model) },
|
|
957
|
-
role: row.role.trim()
|
|
958
|
-
})).filter((rule) => rule.role !== "");
|
|
959
|
-
}
|
|
960
804
|
/** Controller joining Settings reads, writes, and pushed invalidations. */
|
|
961
805
|
var FallbacksSettingsController = class {
|
|
962
806
|
api;
|
|
@@ -976,10 +820,7 @@ window.__ModuleLoader__.load({
|
|
|
976
820
|
configuredProviders: [],
|
|
977
821
|
groups: [],
|
|
978
822
|
catalogEpoch: 0,
|
|
979
|
-
|
|
980
|
-
switchesError: null,
|
|
981
|
-
switches: [],
|
|
982
|
-
subagentPolicy: void 0
|
|
823
|
+
currentSession: void 0
|
|
983
824
|
});
|
|
984
825
|
/** Read guard: a newer load() supersedes an older one's publish. */
|
|
985
826
|
readGeneration = 0;
|
|
@@ -990,22 +831,12 @@ window.__ModuleLoader__.load({
|
|
|
990
831
|
*/
|
|
991
832
|
writeGeneration = 0;
|
|
992
833
|
catalogGeneration = 0;
|
|
993
|
-
switchesGeneration = 0;
|
|
994
834
|
/** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
|
|
995
835
|
namespaces = /* @__PURE__ */ new Map();
|
|
996
|
-
currentSession;
|
|
997
|
-
/**
|
|
998
|
-
* The store's dispose signal (qc3 F-003): created alongside the store
|
|
999
|
-
* lifecycle, threaded into every `session.follow` call, aborted once in
|
|
1000
|
-
* {@link dispose} — a follow opening still mid-await at plugin unload
|
|
1001
|
-
* (HMR / fiber dispose, or a wedged gateway stream) is cancelled promptly
|
|
1002
|
-
* instead of staying open until the mount abort or socket failure.
|
|
1003
|
-
*/
|
|
1004
|
-
followAbort = new AbortController();
|
|
1005
836
|
/**
|
|
1006
837
|
* @param api - the Remote namespace faces the store reads (settings
|
|
1007
838
|
* describe `writable` + namespace directory, llm provider directory,
|
|
1008
|
-
* session model catalog
|
|
839
|
+
* session model catalog) — `ctx.remote` cast once at
|
|
1009
840
|
* the mount boundary.
|
|
1010
841
|
* @param rpc - the connection's generic RPC caller for the host gateway
|
|
1011
842
|
* channel (`/api`), injected from the connection handle.
|
|
@@ -1053,8 +884,7 @@ window.__ModuleLoader__.load({
|
|
|
1053
884
|
}
|
|
1054
885
|
if ("seeds" in getResult.value) seeds = parseSeedsWire(getResult.value.seeds);
|
|
1055
886
|
}
|
|
1056
|
-
|
|
1057
|
-
this.accept(config, writable, legacyKeys, seeds, subagentPolicy);
|
|
887
|
+
this.accept(config, writable, legacyKeys, seeds);
|
|
1058
888
|
} catch (error) {
|
|
1059
889
|
if (generation !== this.readGeneration) return;
|
|
1060
890
|
if (writeGenerationAtStart !== this.writeGeneration) return;
|
|
@@ -1103,75 +933,13 @@ window.__ModuleLoader__.load({
|
|
|
1103
933
|
}
|
|
1104
934
|
}
|
|
1105
935
|
/**
|
|
1106
|
-
* Record the current session the
|
|
1107
|
-
*
|
|
1108
|
-
* immediately; an idle block only records the id — the section's mount
|
|
1109
|
-
* effect performs the first read.
|
|
1110
|
-
* @param sessionId - the session whose history is summarized; undefined
|
|
1111
|
-
* (no current session) resolves to the empty state.
|
|
936
|
+
* Record the current session the conversation switch strip follows
|
|
937
|
+
* (switch events ride the session stream — no client history read).
|
|
1112
938
|
*/
|
|
1113
939
|
setCurrentSession(sessionId) {
|
|
1114
|
-
if (sessionId === this.currentSession) return;
|
|
1115
|
-
this.currentSession = sessionId;
|
|
1116
|
-
if (this.store.getSnapshot().switchesStatus !== "idle") this.loadSwitches();
|
|
1117
|
-
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Read the recent-switch summary for the current session (spec §2.5 D-5):
|
|
1120
|
-
* one tail page (`maxMessages` = {@link SWITCHES_HISTORY_PAGE}) read from
|
|
1121
|
-
* the `session/follow` opening snapshot — the host has no unary tail-page
|
|
1122
|
-
* read (`session/page` needs the follow opening frame's cursor), so the
|
|
1123
|
-
* stream is cancelled right after its first frame —
|
|
1124
|
-
* `fallbacks/switch` events extracted newest-first capped at
|
|
1125
|
-
* {@link RECENT_SWITCH_LIMIT}. No current session → honest empty ready
|
|
1126
|
-
* state (no RPC); a read failure lands `switchesStatus: 'error'` and never
|
|
1127
|
-
* touches the settings state (the form keeps editing/saving normally).
|
|
1128
|
-
* @returns nothing; {@link store} carries success or failure.
|
|
1129
|
-
*/
|
|
1130
|
-
async loadSwitches() {
|
|
1131
|
-
const generation = ++this.switchesGeneration;
|
|
1132
|
-
const sessionId = this.currentSession;
|
|
1133
|
-
if (sessionId === void 0) {
|
|
1134
|
-
this.store.update((state) => {
|
|
1135
|
-
state.switchesStatus = "ready";
|
|
1136
|
-
state.switchesError = null;
|
|
1137
|
-
state.switches = [];
|
|
1138
|
-
});
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
940
|
this.store.update((state) => {
|
|
1142
|
-
state.
|
|
1143
|
-
state.switchesError = null;
|
|
941
|
+
state.currentSession = sessionId;
|
|
1144
942
|
});
|
|
1145
|
-
try {
|
|
1146
|
-
const iterator = this.api.session.follow({
|
|
1147
|
-
address: {
|
|
1148
|
-
kind: "session",
|
|
1149
|
-
sessionId
|
|
1150
|
-
},
|
|
1151
|
-
maxMessages: 50
|
|
1152
|
-
}, this.followAbort.signal)[Symbol.asyncIterator]();
|
|
1153
|
-
let first;
|
|
1154
|
-
try {
|
|
1155
|
-
first = await iterator.next();
|
|
1156
|
-
} finally {
|
|
1157
|
-
await iterator.return?.();
|
|
1158
|
-
}
|
|
1159
|
-
if (generation !== this.switchesGeneration) return;
|
|
1160
|
-
if (first.done || first.value.type !== "snapshot") throw new Error("session follow closed before its opening snapshot");
|
|
1161
|
-
const switches = extractRecentSwitches(first.value.records);
|
|
1162
|
-
this.store.update((state) => {
|
|
1163
|
-
state.switchesStatus = "ready";
|
|
1164
|
-
state.switchesError = null;
|
|
1165
|
-
state.switches = switches;
|
|
1166
|
-
});
|
|
1167
|
-
} catch (error) {
|
|
1168
|
-
if (generation !== this.switchesGeneration) return;
|
|
1169
|
-
const wire = error;
|
|
1170
|
-
this.store.update((state) => {
|
|
1171
|
-
state.switchesStatus = "error";
|
|
1172
|
-
state.switchesError = typeof wire?.message === "string" ? wire.message : messageOf(error);
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
943
|
}
|
|
1176
944
|
/**
|
|
1177
945
|
* Persist the full edited configuration through the gateway channel
|
|
@@ -1203,30 +971,6 @@ window.__ModuleLoader__.load({
|
|
|
1203
971
|
}
|
|
1204
972
|
}
|
|
1205
973
|
/**
|
|
1206
|
-
* Explicit "sync to host allowlist" write (plan
|
|
1207
|
-
* role-chain-allowlist-visibility T2): the gateway endpoint merges the
|
|
1208
|
-
* CURRENT allowlist with this plugin's exact chain routes plus the virtual
|
|
1209
|
-
* `FallbacksChain/Auto` route and writes only `allowedModels`. The button
|
|
1210
|
-
* click is the ONLY trigger — the store never syncs on its own.
|
|
1211
|
-
* @returns true when the write landed (the card then reads the fresh
|
|
1212
|
-
* snapshot from the store); false on a refused/failed call, leaving the
|
|
1213
|
-
* last accepted state untouched. A response discarded by the write
|
|
1214
|
-
* generation guard is still a landed write — dispose() is the only
|
|
1215
|
-
* invalidator, so it must not report failure.
|
|
1216
|
-
*/
|
|
1217
|
-
async syncAllowlist() {
|
|
1218
|
-
if (!this.store.getSnapshot().writable) return false;
|
|
1219
|
-
const generation = ++this.writeGeneration;
|
|
1220
|
-
try {
|
|
1221
|
-
const result = await this.rpc.call("/api", "fallbacks/sync-allowlist", { args: {} });
|
|
1222
|
-
if (!result.ok) return false;
|
|
1223
|
-
if (generation === this.writeGeneration) this.acceptWire(result.value);
|
|
1224
|
-
return true;
|
|
1225
|
-
} catch {
|
|
1226
|
-
return false;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
/**
|
|
1230
974
|
* Reset to composition defaults through the gateway channel
|
|
1231
975
|
* (`/api/fallbacks/reset` — the fallbacks-specific third method; the host
|
|
1232
976
|
* clears the user layer via `settings.replace(ns, {})`, the removal path a
|
|
@@ -1255,15 +999,13 @@ window.__ModuleLoader__.load({
|
|
|
1255
999
|
this.readGeneration += 1;
|
|
1256
1000
|
this.writeGeneration += 1;
|
|
1257
1001
|
this.catalogGeneration += 1;
|
|
1258
|
-
this.switchesGeneration += 1;
|
|
1259
1002
|
this.namespaces = /* @__PURE__ */ new Map();
|
|
1260
|
-
this.followAbort.abort();
|
|
1261
1003
|
}
|
|
1262
1004
|
/**
|
|
1263
|
-
* Unpack one write response (set / reset
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1266
|
-
*
|
|
1005
|
+
* Unpack one write response (set / reset) into the store snapshot. The
|
|
1006
|
+
* additive fields (legacyKeys §9, seeds §9.4) follow the W-1/F-1 rule:
|
|
1007
|
+
* absent on the wire keeps the last accepted value — only a `get` may
|
|
1008
|
+
* settle server truth.
|
|
1267
1009
|
*/
|
|
1268
1010
|
acceptWire(value) {
|
|
1269
1011
|
const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
|
|
@@ -1277,8 +1019,7 @@ window.__ModuleLoader__.load({
|
|
|
1277
1019
|
const wireSeeds = value.seeds;
|
|
1278
1020
|
if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
|
|
1279
1021
|
}
|
|
1280
|
-
|
|
1281
|
-
this.accept(config, true, legacyKeys, seeds, subagentPolicy);
|
|
1022
|
+
this.accept(config, true, legacyKeys, seeds);
|
|
1282
1023
|
}
|
|
1283
1024
|
/**
|
|
1284
1025
|
* Publish a settled load: `status` ready, `writable` from describe, and —
|
|
@@ -1296,7 +1037,7 @@ window.__ModuleLoader__.load({
|
|
|
1296
1037
|
* only when a real config resolved — a transient channel-down keeps the
|
|
1297
1038
|
* last accepted badge state.
|
|
1298
1039
|
*/
|
|
1299
|
-
accept(config, writable, legacyKeys, seeds
|
|
1040
|
+
accept(config, writable, legacyKeys, seeds) {
|
|
1300
1041
|
const parsed = config === void 0 ? void 0 : parseFallbacksConfig(config);
|
|
1301
1042
|
this.store.update((state) => {
|
|
1302
1043
|
state.status = "ready";
|
|
@@ -1305,7 +1046,6 @@ window.__ModuleLoader__.load({
|
|
|
1305
1046
|
state.present = parsed !== void 0;
|
|
1306
1047
|
state.legacyKeys = parsed === void 0 ? state.legacyKeys : legacyKeys;
|
|
1307
1048
|
state.seeds = parsed === void 0 ? state.seeds : seeds;
|
|
1308
|
-
state.subagentPolicy = parsed === void 0 ? state.subagentPolicy : subagentPolicy;
|
|
1309
1049
|
if (parsed !== void 0) state.config = parsed;
|
|
1310
1050
|
state.configuredProviders = configuredProvidersOf(state.providers, this.namespaces);
|
|
1311
1051
|
});
|
|
@@ -1336,17 +1076,6 @@ window.__ModuleLoader__.load({
|
|
|
1336
1076
|
if (controller.store.getSnapshot().catalogStatus === "idle") return;
|
|
1337
1077
|
controller.loadCatalog();
|
|
1338
1078
|
}
|
|
1339
|
-
/**
|
|
1340
|
-
* Refetch the recent-switch summary after `settings/document-updated`
|
|
1341
|
-
* (fallbacks ns) / `connection/reset` only when the status block has already
|
|
1342
|
-
* been read once
|
|
1343
|
-
* (the switches twin of {@link refreshFallbacksIfLoaded}).
|
|
1344
|
-
* @param controller - the fallbacks settings controller.
|
|
1345
|
-
*/
|
|
1346
|
-
function refreshSwitchesIfLoaded(controller) {
|
|
1347
|
-
if (controller.store.getSnapshot().switchesStatus === "idle") return;
|
|
1348
|
-
controller.loadSwitches();
|
|
1349
|
-
}
|
|
1350
1079
|
//#endregion
|
|
1351
1080
|
//#region src/client/locales.ts
|
|
1352
1081
|
/**
|
|
@@ -1378,18 +1107,13 @@ window.__ModuleLoader__.load({
|
|
|
1378
1107
|
"cooldownMs.label": "冷却时长(毫秒)",
|
|
1379
1108
|
"cooldownMs.hint": "冷却期内模型不再入选",
|
|
1380
1109
|
"cooldownMs.tooltip": "被切离/失败的模型在冷却期内不再入选。",
|
|
1381
|
-
"maxSwitchesPerStep.label": "
|
|
1110
|
+
"maxSwitchesPerStep.label": "单次请求行走最大切换",
|
|
1382
1111
|
"maxSwitchesPerStep.hint": "超过后停止切换",
|
|
1383
|
-
"maxSwitchesPerStep.tooltip": "
|
|
1384
|
-
"
|
|
1385
|
-
"alwaysModeRetryCap.hint": "达到上限次数后切换;0 表示禁用",
|
|
1386
|
-
"alwaysModeRetryCap.tooltip": "retryPolicy 为 always 的模型在同一请求内重试达到该次数后切换;0 表示禁用。",
|
|
1112
|
+
"maxSwitchesPerStep.tooltip": "超过后不再切换,以原始错误语义结束本次请求,防止链循环放大延迟。",
|
|
1113
|
+
"virtualOnly.rule": "只有模型选择为 FallbacksChain/<链 id> 的会话(含继承该路由的子代理)会执行降级链;选择真实模型的会话没有插件降级,仅由 llm-retry 处理重试。",
|
|
1387
1114
|
"advanced.label": "高级选项",
|
|
1388
1115
|
"advanced.expand": "展开高级选项",
|
|
1389
1116
|
"advanced.collapse": "收起高级选项",
|
|
1390
|
-
"roleAutoMatch.label": "启用角色自动匹配",
|
|
1391
|
-
"roleAutoMatch.hint": "规则未命中时,由模型自选最贴近的已声明角色",
|
|
1392
|
-
"roleAutoMatch.tooltip": "规则未命中时,模型会自动从已声明角色(id + persona)中选择最匹配者并注入该角色的链;关闭后未命中规则时按现状回落(inherit / rootChain)。",
|
|
1393
1117
|
"rootChain.label": "默认降级链",
|
|
1394
1118
|
"rootChain.tooltip": "未命中任何分时槽时先走这条降级链;全部失败后落到下面的默认模型。",
|
|
1395
1119
|
"defaultModel.label": "默认模型",
|
|
@@ -1401,21 +1125,26 @@ window.__ModuleLoader__.load({
|
|
|
1401
1125
|
"chains.selector.modelPlaceholder": "选择 model",
|
|
1402
1126
|
"chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
|
|
1403
1127
|
"chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
|
|
1404
|
-
"mainAgent.label": "主代理",
|
|
1405
1128
|
"subagents.label": "子代理",
|
|
1406
|
-
"
|
|
1407
|
-
"
|
|
1408
|
-
"
|
|
1409
|
-
"
|
|
1410
|
-
"
|
|
1411
|
-
"
|
|
1412
|
-
"
|
|
1413
|
-
"
|
|
1414
|
-
"
|
|
1415
|
-
"
|
|
1416
|
-
"
|
|
1417
|
-
"
|
|
1418
|
-
"
|
|
1129
|
+
"virtualChain.label": "虚拟链",
|
|
1130
|
+
"virtualChain.hint": "每条链是一张独立的虚拟路由,可各自配置分时、默认降级链与链尾。",
|
|
1131
|
+
"chains.tab.add": "新建链",
|
|
1132
|
+
"chains.tab.menu": "管理当前链:重命名 / 删除",
|
|
1133
|
+
"chains.name.label": "链名称",
|
|
1134
|
+
"chains.name.hint": "仅用于展示(tab 标题与模型选择器前缀);id「{id}」是模型选择器里的路由,创建后不可改。",
|
|
1135
|
+
"chains.name.tooltip": "给这条虚拟链起个可读的名字;实际路由是 FallbacksChain/<链 id>,改名不影响已选该路由的会话。",
|
|
1136
|
+
"chains.tab.renameLabel": "链名(仅展示名,id「{id}」不变)",
|
|
1137
|
+
"chains.tab.renameDone": "完成",
|
|
1138
|
+
"chains.tab.delete": "删除该链",
|
|
1139
|
+
"chains.tab.deleteDisabled": "删除该链(至少保留一条链)",
|
|
1140
|
+
"chains.tab.dirty": "(未保存修改)",
|
|
1141
|
+
"chains.tab.error": "(校验错误)",
|
|
1142
|
+
"chains.empty.title": "尚未配置任何降级链",
|
|
1143
|
+
"chains.empty.body": "未配置链时,模型选择器中不会出现任何 FallbacksChain/* 虚拟路由;已选 FallbacksChain 路由的会话将没有插件降级,仅由宿主 llm-retry 处理重试。",
|
|
1144
|
+
"chains.empty.create": "+ 新建第一条链",
|
|
1145
|
+
"chains.chainFoot.hint": "仅保存当前链:选择器 · 链尾 · 分时槽 · 链名",
|
|
1146
|
+
"chains.chainFoot.discard": "放弃修改",
|
|
1147
|
+
"chains.chainFoot.save": "保存",
|
|
1419
1148
|
"timeSlots.label": "分时槽设置",
|
|
1420
1149
|
"timeSlots.hint": "自上而下第一条命中生效;全时段行固定最后",
|
|
1421
1150
|
"timeSlots.tz.label": "时区(UTC±)",
|
|
@@ -1423,7 +1152,7 @@ window.__ModuleLoader__.load({
|
|
|
1423
1152
|
"timeSlots.name": "名称",
|
|
1424
1153
|
"timeSlots.expand": "展开该行",
|
|
1425
1154
|
"timeSlots.collapse": "收起该行",
|
|
1426
|
-
"timeSlots.tooltip": "命中行的模型链成为 root
|
|
1155
|
+
"timeSlots.tooltip": "命中行的模型链成为 root 生效链(取代全时段链);未命中任何行时使用全时段链。分时切换是路由种子而非失败决策:不消耗冷却、不计入单次请求行走的切换上限。",
|
|
1427
1156
|
"timeSlots.addPreset": "添加预设",
|
|
1428
1157
|
"timeSlots.addCustom": "添加自定义时段",
|
|
1429
1158
|
"timeSlots.presetPlaceholder": "选择预设",
|
|
@@ -1463,8 +1192,6 @@ window.__ModuleLoader__.load({
|
|
|
1463
1192
|
"allDay.pro": "官方 Pro(deepseek-official/deepseek-v4-pro)",
|
|
1464
1193
|
"allDay.nonconforming": "当前默认模型不合法:请选择官方 Flash 或 Pro 后保存",
|
|
1465
1194
|
"roles.list.label": "角色实体",
|
|
1466
|
-
"roles.list.hint": "先声明角色,规则才能引用",
|
|
1467
|
-
"roles.list.tooltip": "角色 id 须匹配 /^[a-z0-9-]{1,32}$/ 且唯一;\"inherit\" 为保留字,不能用作角色 id。",
|
|
1468
1195
|
"roles.id": "id",
|
|
1469
1196
|
"roles.id.hint": "小写字母/数字/连字符,1–32 字符",
|
|
1470
1197
|
"roles.idPlaceholder": "例如 reviewer",
|
|
@@ -1476,35 +1203,24 @@ window.__ModuleLoader__.load({
|
|
|
1476
1203
|
"roles.seedSource.bundled": "内置",
|
|
1477
1204
|
"roles.seedSource.external": "外部",
|
|
1478
1205
|
"roles.seedSource.user": "用户",
|
|
1479
|
-
"roles.
|
|
1480
|
-
"roles.
|
|
1481
|
-
"roles.
|
|
1482
|
-
"roles.
|
|
1206
|
+
"roles.list.hint": "角色是该角色的身份与人格;可在「指定降级链」中手动指定一条命名虚拟链",
|
|
1207
|
+
"roles.chain.label": "指定降级链",
|
|
1208
|
+
"roles.chain.unspecified": "未指定(沿用 dsh 默认)",
|
|
1209
|
+
"roles.chain.hint": "手动指定该角色使用的虚拟链;未指定时沿用 dsh 默认:继承委派它的代理所选链。",
|
|
1210
|
+
"roles.chain.tooltip": "手动指定该角色使用的命名虚拟链:派发到该角色的子代理会挂到 FallbacksChain/<链 id>,由那条链承接降级。未指定则插件不干预。",
|
|
1211
|
+
"roles.chainMeta.unspecified": "未指定降级链",
|
|
1483
1212
|
"roles.add": "添加角色",
|
|
1484
1213
|
"roles.remove": "删除该角色",
|
|
1485
1214
|
"roles.expand": "展开该角色",
|
|
1486
1215
|
"roles.collapse": "收起该角色",
|
|
1487
|
-
"roles.selector.add": "添加选择器",
|
|
1488
|
-
"roles.rules": "角色规则",
|
|
1489
|
-
"roles.rules.hint": "仅对子代理生效:顺序匹配 provider/model,未命中 → inherit(root 链)",
|
|
1490
|
-
"roles.rules.tooltip": "规则仅对子代理生效(root 请求不匹配规则):命中后走对应角色的链;未命中走内置 inherit(rootChain)。",
|
|
1491
|
-
"roles.rule.provider": "provider",
|
|
1492
|
-
"roles.rule.provider.any": "任意",
|
|
1493
|
-
"roles.rule.model": "model",
|
|
1494
|
-
"roles.rule.model.any": "任意",
|
|
1495
|
-
"roles.rule.role": "角色",
|
|
1496
|
-
"roles.rule.role.inherit": "inherit(内置:root 链)",
|
|
1497
|
-
"roles.rule.roleSelectPlaceholder": "选择角色",
|
|
1498
|
-
"roles.rule.roleUndeclared.short": "(未声明)",
|
|
1499
|
-
"roles.addRule": "添加规则",
|
|
1500
|
-
"roles.removeRule": "删除该规则",
|
|
1501
1216
|
"validation.blocked": "配置校验未通过,保存被拦截:",
|
|
1502
1217
|
"validation.roleIdFormat": "角色 id \"{id}\" 不符合格式 /^[a-z0-9-]{1,32}$/",
|
|
1503
1218
|
"validation.roleIdReserved": "\"inherit\" 为保留角色 id,不能用于角色实体",
|
|
1504
1219
|
"validation.roleIdDuplicate": "角色 id \"{id}\" 重复",
|
|
1505
|
-
"validation.
|
|
1506
|
-
"validation.
|
|
1507
|
-
"validation.
|
|
1220
|
+
"validation.chainIdFormat": "链 id \"{id}\" 不符合格式 /^[A-Za-z0-9][A-Za-z0-9-]{0,31}$/",
|
|
1221
|
+
"validation.chainIdDuplicate": "链 id \"{id}\" 重复",
|
|
1222
|
+
"validation.chainLabelRequired": "链 \"{id}\" 缺少展示名(label)",
|
|
1223
|
+
"validation.chainEntriesRequired": "默认降级链不能为空:请至少添加一条选择器。",
|
|
1508
1224
|
"validation.allDayRequired": "默认模型必须二选一:官方 Flash 或 Pro",
|
|
1509
1225
|
"validation.slotChainRequired": "分时槽未配置模型:请至少添加一条链选择器",
|
|
1510
1226
|
"validation.slotWindow": "分时槽开始/结束时间须为 HH:mm 格式",
|
|
@@ -1514,7 +1230,7 @@ window.__ModuleLoader__.load({
|
|
|
1514
1230
|
"validation.slotPresetDuplicate": "预设 \"{preset}\" 已存在:每个预设只能添加一行",
|
|
1515
1231
|
"validation.slotPresetFrozen": "预设窗口是冻结代码常量:预设行不能携带 start/end/days",
|
|
1516
1232
|
"validation.selector": "选择器 \"{entry}\" 非法:{message}",
|
|
1517
|
-
"legacy.banner": "
|
|
1233
|
+
"legacy.banner": "检测到旧格式字段({keys}):已自动升级为命名链——原默认降级链成为第 0 条链「默认链」,角色与人格保持不变,旧的角色数组链已置为「未指定」(可在下方重新指定)。保存一次即可固化;插件不会主动改写你的配置文件。",
|
|
1518
1234
|
"catalog.empty": "暂无可用模型:请先在模型页添加模型,添加后此处将自动可选。",
|
|
1519
1235
|
"catalog.error": "模型目录读取失败:{message}",
|
|
1520
1236
|
"catalog.partial": "部分 provider 模型查询失败:{message}",
|
|
@@ -1522,24 +1238,9 @@ window.__ModuleLoader__.load({
|
|
|
1522
1238
|
"catalog.outside.tooltip": "不在当前模型目录,可保留原值并保存;新增条目仅可从目录选择。",
|
|
1523
1239
|
"catalog.outside.short": " (目录外)",
|
|
1524
1240
|
"catalog.unconfigured.short": " (未配置)",
|
|
1525
|
-
"status.title": "运行状态(只读)",
|
|
1526
|
-
"status.switches.label": "最近切换:",
|
|
1527
|
-
"status.switches.empty": "本会话暂无 fallback 切换。",
|
|
1528
|
-
"status.switches.error": "切换历史读取失败:{message}",
|
|
1529
|
-
"status.switches.compact": "最近 {count} 次 · {from} → {to}({role} · {reason})",
|
|
1530
|
-
"status.switches.compact.roleInject": "最近 {count} 次 · {role} → {to}({reason})",
|
|
1531
1241
|
"status.switches.reason.trigger-code": "触发失败码",
|
|
1532
1242
|
"status.switches.reason.always-cap": "always 模式上限",
|
|
1533
1243
|
"status.switches.reason.role-inject": "角色注入",
|
|
1534
|
-
"general.title": "模型故障降级",
|
|
1535
|
-
"general.enabled": "已启用",
|
|
1536
|
-
"general.disabled": "未启用",
|
|
1537
|
-
"general.unknown": "未知",
|
|
1538
|
-
"general.unavailable": "状态通道暂不可达",
|
|
1539
|
-
"general.switch": "最近切换:{from} → {to}({role} · {reason})",
|
|
1540
|
-
"general.switch.roleInject": "最近切换:{role} → {to}({reason})",
|
|
1541
|
-
"general.switch.empty": "本会话暂无切换",
|
|
1542
|
-
"general.error": "状态读取失败:{message}",
|
|
1543
1244
|
"chat.switch.title": "模型已降级",
|
|
1544
1245
|
"chat.switch.summary": "{from} → {to}({reason})",
|
|
1545
1246
|
"chat.switch.summary.roleInject": "({reason})",
|
|
@@ -1574,18 +1275,13 @@ window.__ModuleLoader__.load({
|
|
|
1574
1275
|
"cooldownMs.label": "Cooldown (milliseconds)",
|
|
1575
1276
|
"cooldownMs.hint": "Models stay out of candidacy during cooldown",
|
|
1576
1277
|
"cooldownMs.tooltip": "Switched-away or failed models stay out of candidacy during the cooldown window.",
|
|
1577
|
-
"maxSwitchesPerStep.label": "Max switches per
|
|
1278
|
+
"maxSwitchesPerStep.label": "Max switches per request walk",
|
|
1578
1279
|
"maxSwitchesPerStep.hint": "Stops switching beyond the cap",
|
|
1579
|
-
"maxSwitchesPerStep.tooltip": "Beyond this
|
|
1580
|
-
"
|
|
1581
|
-
"alwaysModeRetryCap.hint": "Switches after the cap; 0 disables",
|
|
1582
|
-
"alwaysModeRetryCap.tooltip": "Models whose retryPolicy is always switch after this many retries within one request; 0 disables.",
|
|
1280
|
+
"maxSwitchesPerStep.tooltip": "Beyond this no further switch happens and the request ends with the original error semantics, preventing chain loops from amplifying latency.",
|
|
1281
|
+
"virtualOnly.rule": "Only sessions whose model selection is FallbacksChain/<chain id> (including subagents that inherit that route) walk a chain; a real-model selection gets no plugin fallback — llm-retry alone handles its retries.",
|
|
1583
1282
|
"advanced.label": "Advanced options",
|
|
1584
1283
|
"advanced.expand": "Expand advanced options",
|
|
1585
1284
|
"advanced.collapse": "Collapse advanced options",
|
|
1586
|
-
"roleAutoMatch.label": "Enable role auto-match",
|
|
1587
|
-
"roleAutoMatch.hint": "On rules-miss, the model picks the closest declared role",
|
|
1588
|
-
"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.",
|
|
1589
1285
|
"rootChain.label": "Default fallback chain",
|
|
1590
1286
|
"rootChain.tooltip": "Walked first whenever no time slot matches; if every entry fails, the default model below is the last fallback.",
|
|
1591
1287
|
"defaultModel.label": "Default model",
|
|
@@ -1597,21 +1293,26 @@ window.__ModuleLoader__.load({
|
|
|
1597
1293
|
"chains.selector.modelPlaceholder": "Select model",
|
|
1598
1294
|
"chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
|
|
1599
1295
|
"chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
|
|
1600
|
-
"mainAgent.label": "Main agent",
|
|
1601
1296
|
"subagents.label": "Subagents",
|
|
1602
|
-
"
|
|
1603
|
-
"
|
|
1604
|
-
"
|
|
1605
|
-
"
|
|
1606
|
-
"
|
|
1607
|
-
"
|
|
1608
|
-
"
|
|
1609
|
-
"
|
|
1610
|
-
"
|
|
1611
|
-
"
|
|
1612
|
-
"
|
|
1613
|
-
"
|
|
1614
|
-
"
|
|
1297
|
+
"virtualChain.label": "Virtual chains",
|
|
1298
|
+
"virtualChain.hint": "Each chain is a standalone virtual route with its own time slots, default chain and chain tail.",
|
|
1299
|
+
"chains.tab.add": "New chain",
|
|
1300
|
+
"chains.tab.menu": "Manage this chain: rename / delete",
|
|
1301
|
+
"chains.name.label": "Chain name",
|
|
1302
|
+
"chains.name.hint": "Display only (tab title and picker prefix); the id \"{id}\" is the picker route and cannot change after creation.",
|
|
1303
|
+
"chains.name.tooltip": "Give this virtual chain a readable name; the actual route is FallbacksChain/<chain id>, so renaming does not affect sessions already on it.",
|
|
1304
|
+
"chains.tab.renameLabel": "Chain name (display only; id \"{id}\" stays)",
|
|
1305
|
+
"chains.tab.renameDone": "Done",
|
|
1306
|
+
"chains.tab.delete": "Delete this chain",
|
|
1307
|
+
"chains.tab.deleteDisabled": "Delete this chain (keep at least one)",
|
|
1308
|
+
"chains.tab.dirty": " (unsaved changes)",
|
|
1309
|
+
"chains.tab.error": " (validation error)",
|
|
1310
|
+
"chains.empty.title": "No chains configured",
|
|
1311
|
+
"chains.empty.body": "With no chain configured, no FallbacksChain/* virtual route appears in the model picker; sessions on a FallbacksChain route get no plugin fallback — the host llm-retry alone handles retries.",
|
|
1312
|
+
"chains.empty.create": "+ Create the first chain",
|
|
1313
|
+
"chains.chainFoot.hint": "Saves this chain only: selectors · tail · time slots · name",
|
|
1314
|
+
"chains.chainFoot.discard": "Discard",
|
|
1315
|
+
"chains.chainFoot.save": "Save",
|
|
1615
1316
|
"timeSlots.label": "Time slots",
|
|
1616
1317
|
"timeSlots.hint": "First match from top to bottom wins; the all-day row is always last",
|
|
1617
1318
|
"timeSlots.tz.label": "Timezone (UTC±)",
|
|
@@ -1619,7 +1320,7 @@ window.__ModuleLoader__.load({
|
|
|
1619
1320
|
"timeSlots.name": "Name",
|
|
1620
1321
|
"timeSlots.expand": "Expand this row",
|
|
1621
1322
|
"timeSlots.collapse": "Collapse this row",
|
|
1622
|
-
"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-
|
|
1323
|
+
"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-request-walk switch cap.",
|
|
1623
1324
|
"timeSlots.addPreset": "Add preset",
|
|
1624
1325
|
"timeSlots.addCustom": "Add custom time slot",
|
|
1625
1326
|
"timeSlots.presetPlaceholder": "Select a preset",
|
|
@@ -1659,8 +1360,7 @@ window.__ModuleLoader__.load({
|
|
|
1659
1360
|
"allDay.pro": "Official Pro (deepseek-official/deepseek-v4-pro)",
|
|
1660
1361
|
"allDay.nonconforming": "The current default model is not valid: pick official Flash or Pro before saving",
|
|
1661
1362
|
"roles.list.label": "Declared roles",
|
|
1662
|
-
"roles.list.hint": "
|
|
1663
|
-
"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.",
|
|
1363
|
+
"roles.list.hint": "A role is the identity + persona; optionally point it at one named chain via 指定降级链",
|
|
1664
1364
|
"roles.id": "ID",
|
|
1665
1365
|
"roles.id.hint": "lowercase letters, digits, hyphens; 1–32 chars",
|
|
1666
1366
|
"roles.idPlaceholder": "e.g. reviewer",
|
|
@@ -1672,35 +1372,23 @@ window.__ModuleLoader__.load({
|
|
|
1672
1372
|
"roles.seedSource.bundled": "bundled",
|
|
1673
1373
|
"roles.seedSource.external": "external",
|
|
1674
1374
|
"roles.seedSource.user": "User",
|
|
1675
|
-
"roles.
|
|
1676
|
-
"roles.
|
|
1677
|
-
"roles.
|
|
1678
|
-
"roles.
|
|
1375
|
+
"roles.chain.label": "Specified chain",
|
|
1376
|
+
"roles.chain.unspecified": "Not specified (dsh default)",
|
|
1377
|
+
"roles.chain.hint": "Manually specify the named virtual chain this role uses; when unspecified the dsh default applies: the child inherits the delegating agent's chain.",
|
|
1378
|
+
"roles.chain.tooltip": "Manually specify the named virtual chain for this role: children dispatched under it mount on FallbacksChain/<chain id> and that chain owns the fallback. Unspecified ⇒ the plugin does not intervene.",
|
|
1379
|
+
"roles.chainMeta.unspecified": "No chain specified",
|
|
1679
1380
|
"roles.add": "Add role",
|
|
1680
1381
|
"roles.remove": "Remove this role",
|
|
1681
1382
|
"roles.expand": "Expand this role",
|
|
1682
1383
|
"roles.collapse": "Collapse this role",
|
|
1683
|
-
"roles.selector.add": "Add selector",
|
|
1684
|
-
"roles.rules": "Role rules",
|
|
1685
|
-
"roles.rules.hint": "Subagents only: matches provider/model in order; no match → inherit (root chain)",
|
|
1686
|
-
"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).",
|
|
1687
|
-
"roles.rule.provider": "provider",
|
|
1688
|
-
"roles.rule.provider.any": "Any",
|
|
1689
|
-
"roles.rule.model": "model",
|
|
1690
|
-
"roles.rule.model.any": "Any",
|
|
1691
|
-
"roles.rule.role": "role",
|
|
1692
|
-
"roles.rule.role.inherit": "inherit (built-in: root chain)",
|
|
1693
|
-
"roles.rule.roleSelectPlaceholder": "Select role",
|
|
1694
|
-
"roles.rule.roleUndeclared.short": " (undeclared)",
|
|
1695
|
-
"roles.addRule": "Add rule",
|
|
1696
|
-
"roles.removeRule": "Remove this rule",
|
|
1697
1384
|
"validation.blocked": "Configuration validation failed; save was blocked: ",
|
|
1698
1385
|
"validation.roleIdFormat": "Role id \"{id}\" does not match /^[a-z0-9-]{1,32}$/",
|
|
1699
1386
|
"validation.roleIdReserved": "\"inherit\" is a reserved role id and cannot be declared",
|
|
1700
1387
|
"validation.roleIdDuplicate": "Duplicate role id \"{id}\"",
|
|
1701
|
-
"validation.
|
|
1702
|
-
"validation.
|
|
1703
|
-
"validation.
|
|
1388
|
+
"validation.chainIdFormat": "Chain id \"{id}\" does not match /^[A-Za-z0-9][A-Za-z0-9-]{0,31}$/",
|
|
1389
|
+
"validation.chainIdDuplicate": "Duplicate chain id \"{id}\"",
|
|
1390
|
+
"validation.chainLabelRequired": "Chain \"{id}\" is missing a display label",
|
|
1391
|
+
"validation.chainEntriesRequired": "The default degradation chain cannot be empty: add at least one selector.",
|
|
1704
1392
|
"validation.allDayRequired": "The default model must be exactly one official model (Flash or Pro)",
|
|
1705
1393
|
"validation.slotChainRequired": "Time-slot row has no models: add at least one chain entry",
|
|
1706
1394
|
"validation.slotWindow": "Time-slot start/end must use HH:mm format",
|
|
@@ -1710,7 +1398,7 @@ window.__ModuleLoader__.load({
|
|
|
1710
1398
|
"validation.slotPresetDuplicate": "Preset \"{preset}\" already exists: at most one row per preset",
|
|
1711
1399
|
"validation.slotPresetFrozen": "Preset windows are frozen code constants: a preset row cannot carry start/end/days",
|
|
1712
1400
|
"validation.selector": "Invalid selector \"{entry}\": {message}",
|
|
1713
|
-
"legacy.banner": "Legacy
|
|
1401
|
+
"legacy.banner": "Legacy fields detected ({keys}): automatically upgraded to named chains — your previous default chain became chain #0 「默认链」, roles and personas are unchanged, and old per-role chain arrays now read as \"not specified\" (re-attach them below). Save once to persist this; the plugin never rewrites your config file on its own.",
|
|
1714
1402
|
"catalog.empty": "No models yet: add a model on the Models page first; options will appear here automatically.",
|
|
1715
1403
|
"catalog.error": "Model catalog read failed: {message}",
|
|
1716
1404
|
"catalog.partial": "Some provider model lookups failed: {message}",
|
|
@@ -1718,24 +1406,9 @@ window.__ModuleLoader__.load({
|
|
|
1718
1406
|
"catalog.outside.tooltip": "Not in the current model catalog; you can keep the original value and save it (new entries are restricted to the catalog).",
|
|
1719
1407
|
"catalog.outside.short": " (outside catalog)",
|
|
1720
1408
|
"catalog.unconfigured.short": " (not configured)",
|
|
1721
|
-
"status.title": "Runtime status (read-only)",
|
|
1722
|
-
"status.switches.label": "Recent switches: ",
|
|
1723
|
-
"status.switches.empty": "No fallback switches in this session yet.",
|
|
1724
|
-
"status.switches.error": "Switch history read failed: {message}",
|
|
1725
|
-
"status.switches.compact": "last {count} · {from} → {to} ({role} · {reason})",
|
|
1726
|
-
"status.switches.compact.roleInject": "last {count} · {role} → {to} ({reason})",
|
|
1727
1409
|
"status.switches.reason.trigger-code": "trigger code",
|
|
1728
1410
|
"status.switches.reason.always-cap": "always-mode cap",
|
|
1729
1411
|
"status.switches.reason.role-inject": "role inject",
|
|
1730
|
-
"general.title": "Model failover",
|
|
1731
|
-
"general.enabled": "Enabled",
|
|
1732
|
-
"general.disabled": "Disabled",
|
|
1733
|
-
"general.unknown": "Unknown",
|
|
1734
|
-
"general.unavailable": "Status channel unavailable",
|
|
1735
|
-
"general.switch": "Last switch: {from} → {to} ({role} · {reason})",
|
|
1736
|
-
"general.switch.roleInject": "Last switch: {role} → {to} ({reason})",
|
|
1737
|
-
"general.switch.empty": "No switches this session",
|
|
1738
|
-
"general.error": "Status read failed: {message}",
|
|
1739
1412
|
"chat.switch.title": "Model downgraded",
|
|
1740
1413
|
"chat.switch.summary": "{from} → {to} ({reason})",
|
|
1741
1414
|
"chat.switch.summary.roleInject": "({reason})",
|
|
@@ -1753,8 +1426,8 @@ window.__ModuleLoader__.load({
|
|
|
1753
1426
|
/** The settings section's dictionary namespace. */
|
|
1754
1427
|
const NS = "fallbacks";
|
|
1755
1428
|
/**
|
|
1756
|
-
* Reason → locale key map for switch summaries (
|
|
1757
|
-
*
|
|
1429
|
+
* Reason → locale key map for switch summaries (shared by the conversation
|
|
1430
|
+
* node). All
|
|
1758
1431
|
* reasons resolve from ONE key family (`status.switches.reason.*`) — the
|
|
1759
1432
|
* shared reason vocabulary must not mix dictionary families (qc1 F-004). The
|
|
1760
1433
|
* session log is durable and forward-compatible: a reason value outside the
|
|
@@ -1788,13 +1461,13 @@ window.__ModuleLoader__.load({
|
|
|
1788
1461
|
}
|
|
1789
1462
|
//#endregion
|
|
1790
1463
|
//#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/FallbacksCard.module.css.mjs
|
|
1791
|
-
const css$3 = "\n\n\n._20fd0e45_root {\n min-width: 0;\n}\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._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._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n\n._ba240d82_sectionHeading {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 4px 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n._ddbc1b49_sectionHeadingText {\n display: block;\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._c41f9db7_sectionActions ._f0864181_discard,\n._c41f9db7_sectionActions ._ccff7e48_save {\n padding: 3px 12px;\n font-size: 12px;\n line-height: 18px;\n}\n\n\n._b116c758_sectionToggle {\n appearance: none;\n border: 0;\n background: none;\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 0;\n font: inherit;\n color: inherit;\n cursor: pointer;\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: 1px;\n}\n\n\n._80101203_advancedBody {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._3e95886d_numberFields {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\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\n._94c91bfd_fieldset > ._67826267_field,\n._94c91bfd_fieldset > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._67826267_field + ._3e95886d_numberFields,\n._94c91bfd_fieldset > ._9998065c_checkboxRow + ._67826267_field {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n\n._9998065c_checkboxRow ._06389b18_checkbox {\n margin-top: 3px;\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\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\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._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 position: relative;\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._a6244318_infoHint::after {\n content: attr(data-tip);\n position: absolute;\n top: 50%;\n left: calc(100% + 8px);\n transform: translateY(-50%);\n z-index: 10;\n box-sizing: border-box;\n width: max-content;\n max-width: 280px;\n padding: 5px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n font-weight: 400;\n white-space: normal;\n text-align: left;\n pointer-events: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n._a6244318_infoHint:hover::after,\n._a6244318_infoHint:focus-visible::after {\n opacity: 1;\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: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 8px 12px;\n resize: vertical;\n}\n\n\n._1285c19b_personaBriefRow {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n}\n\n._4b8f7345_personaBrief {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._2686400e_personaBriefEmpty {\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._4e4a07be_personaFull {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus-visible {\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: 12px;\n}\n\n._3e95886d_numberFields ._67826267_field {\n padding: 0;\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._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(-90deg);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: none;\n}\n\n\n._cf3961bb_chevronFlip {\n transform: rotate(180deg);\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: 0 1 auto;\n min-width: 0;\n max-width: 40%;\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._52d6df87_seedBadge {\n flex: none;\n max-width: 40%;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\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-layer-1);\n color: var(--dsw-alias-label-secondary);\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\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\n._1a0fa2e1_selectorRowDragging {\n opacity: 0.45;\n}\n\n._688095f2_selectorRowOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n\n._7489fed0_selectorGrip {\n flex: none;\n}\n\n._7489fed0_selectorGrip:disabled {\n display: none;\n}\n\n\n._28237b53_chainBlock {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n\n._5694c1de_selectorLine {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: nowrap;\n \n min-height: 32px;\n}\n\n\n._2f156f38_selectorIndex {\n flex: none;\n width: 1.5em;\n text-align: right;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n}\n\n\n._4843ae21_selectorProviderCell {\n flex: 0 1 clamp(140px, 20%, 220px);\n min-width: 0;\n}\n\n._df828181_selectorModelCell {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n._df828181_selectorModelCell > select._f9d86f7b_input {\n max-width: 100%;\n}\n\n\n._fe6d05a5_selectorHint {\n display: flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n}\n\n\n._d04ee9fe_selectorHintText {\n flex: 0 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._fe6d05a5_selectorHint > ._a6244318_infoHint {\n flex: none;\n margin-left: 4px;\n}\n\n\n._ec7c2fac_selectorRemove {\n flex: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n\n._ec7c2fac_selectorRemove:disabled {\n display: none;\n}\n\n\n._240cbbf8_selectorRow:hover ._ec7c2fac_selectorRemove:not(:disabled),\n._240cbbf8_selectorRow:focus-within ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 1;\n}\n\n@media (hover: none) {\n ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 0.7;\n }\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._711248a9_outlineButton {\n appearance: none;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._711248a9_outlineButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._711248a9_outlineButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._711248a9_outlineButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton {\n box-sizing: border-box;\n align-self: flex-start;\n height: 26px;\n padding: 0 8px;\n border: 1px solid transparent;\n gap: 4px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._28237b53_chainBlock > ._28202c90_addButton:hover:not(:disabled) {\n border-color: transparent;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\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._f0864181_discard,\n._ccff7e48_save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--dsw-radius-md);\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n._f0864181_discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n._f0864181_discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._ccff7e48_save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n._f0864181_discard:disabled,\n._ccff7e48_save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._f0864181_discard:focus-visible,\n._ccff7e48_save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\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._700006c9_policyStatus {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._aa3fae4a_policyStatusText {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n._d90f6286_policyStatusSync {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n\n\n._579f87fb_policyStatusLive:empty {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n._d4b67a81_policyStatusLabel {\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._278bb4b3_policyStatusLine {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n}\n\n._da6387e5_policyStatusWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after,\n ._a6244318_infoHint::after {\n transition: none;\n }\n\n ._631094a0_chevron {\n transition: none;\n }\n}\n";
|
|
1792
|
-
const tagId$
|
|
1793
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
1464
|
+
const css$2 = "\n\n\n._20fd0e45_root {\n min-width: 0;\n}\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._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._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n\n._ba240d82_sectionHeading {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 4px 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n._ddbc1b49_sectionHeadingText {\n display: block;\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._c41f9db7_sectionActions ._f0864181_discard,\n._c41f9db7_sectionActions ._ccff7e48_save {\n padding: 3px 12px;\n font-size: 12px;\n line-height: 18px;\n}\n\n\n._b116c758_sectionToggle {\n appearance: none;\n border: 0;\n background: none;\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 0;\n font: inherit;\n color: inherit;\n cursor: pointer;\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: 1px;\n}\n\n\n._80101203_advancedBody {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._3e95886d_numberFields {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\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\n._94c91bfd_fieldset > ._67826267_field,\n._94c91bfd_fieldset > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._67826267_field + ._3e95886d_numberFields {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\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\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 position: relative;\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._a6244318_infoHint::after {\n content: attr(data-tip);\n position: absolute;\n top: 50%;\n left: calc(100% + 8px);\n transform: translateY(-50%);\n z-index: 10;\n box-sizing: border-box;\n width: max-content;\n max-width: 280px;\n padding: 5px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n font-weight: 400;\n white-space: normal;\n text-align: left;\n pointer-events: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n._a6244318_infoHint:hover::after,\n._a6244318_infoHint:focus-visible::after {\n opacity: 1;\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: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 8px 12px;\n resize: vertical;\n}\n\n\n._1285c19b_personaBriefRow {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n}\n\n._4b8f7345_personaBrief {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._2686400e_personaBriefEmpty {\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._4e4a07be_personaFull {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus-visible {\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: 12px;\n}\n\n._3e95886d_numberFields ._67826267_field {\n padding: 0;\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._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(-90deg);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: none;\n}\n\n\n._cf3961bb_chevronFlip {\n transform: rotate(180deg);\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: 0 1 auto;\n min-width: 0;\n max-width: 40%;\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._52d6df87_seedBadge {\n flex: none;\n max-width: 40%;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\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-layer-1);\n color: var(--dsw-alias-label-secondary);\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\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\n._1a0fa2e1_selectorRowDragging {\n opacity: 0.45;\n}\n\n._688095f2_selectorRowOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n\n._7489fed0_selectorGrip {\n flex: none;\n}\n\n._7489fed0_selectorGrip:disabled {\n display: none;\n}\n\n\n._28237b53_chainBlock {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n\n._5694c1de_selectorLine {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: nowrap;\n \n min-height: 32px;\n}\n\n\n._2f156f38_selectorIndex {\n flex: none;\n width: 1.5em;\n text-align: right;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n}\n\n\n._4843ae21_selectorProviderCell {\n flex: 0 1 clamp(140px, 20%, 220px);\n min-width: 0;\n}\n\n._df828181_selectorModelCell {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n._df828181_selectorModelCell > select._f9d86f7b_input {\n max-width: 100%;\n}\n\n\n._fe6d05a5_selectorHint {\n display: flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n}\n\n\n._d04ee9fe_selectorHintText {\n flex: 0 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._fe6d05a5_selectorHint > ._a6244318_infoHint {\n flex: none;\n margin-left: 4px;\n}\n\n\n._ec7c2fac_selectorRemove {\n flex: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n\n._ec7c2fac_selectorRemove:disabled {\n display: none;\n}\n\n\n._240cbbf8_selectorRow:hover ._ec7c2fac_selectorRemove:not(:disabled),\n._240cbbf8_selectorRow:focus-within ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 1;\n}\n\n@media (hover: none) {\n ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 0.7;\n }\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._711248a9_outlineButton {\n appearance: none;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._711248a9_outlineButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._711248a9_outlineButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._711248a9_outlineButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton {\n box-sizing: border-box;\n align-self: flex-start;\n height: 26px;\n padding: 0 8px;\n border: 1px solid transparent;\n gap: 4px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._28237b53_chainBlock > ._28202c90_addButton:hover:not(:disabled) {\n border-color: transparent;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\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._f0864181_discard,\n._ccff7e48_save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--dsw-radius-md);\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n._f0864181_discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n._f0864181_discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._ccff7e48_save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n._f0864181_discard:disabled,\n._ccff7e48_save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._f0864181_discard:focus-visible,\n._ccff7e48_save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\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._700006c9_policyStatus {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._aa3fae4a_policyStatusText {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n._d90f6286_policyStatusSync {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n\n\n._579f87fb_policyStatusLive:empty {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n._d4b67a81_policyStatusLabel {\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._278bb4b3_policyStatusLine {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n}\n\n._da6387e5_policyStatusWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after,\n ._a6244318_infoHint::after {\n transition: none;\n }\n\n ._631094a0_chevron {\n transition: none;\n }\n}\n\n\n._71f663ad_tabsWrap {\n position: relative;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n max-width: 720px;\n}\n\n._0c1dcd2d_tabs {\n display: flex;\n align-items: center;\n gap: 4px;\n overflow-x: auto;\n scrollbar-width: none;\n padding: 0 2px;\n}\n._0c1dcd2d_tabs::-webkit-scrollbar { display: none; }\n\n._98f72e4c_tab {\n appearance: none;\n border: none;\n background: none;\n font: inherit;\n color: var(--dsw-alias-label-secondary);\n padding: 6px 10px;\n cursor: pointer;\n position: relative;\n white-space: nowrap;\n border-radius: 6px 6px 0 0;\n}\n._98f72e4c_tab:hover { background: var(--dsw-alias-interactive-bg-hover); }\n._98f72e4c_tab[aria-selected='true'] {\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n._98f72e4c_tab[aria-selected='true']::after {\n content: '';\n position: absolute;\n left: 6px;\n right: 6px;\n bottom: -1px;\n height: 2px;\n border-radius: 2px 2px 0 0;\n background: var(--dsw-alias-brand-primary);\n}\n._98f72e4c_tab:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n._0ebd08c8_dirtyPip {\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-left: 4px;\n border-radius: 50%;\n background: var(--dsw-alias-state-warn-primary);\n vertical-align: middle;\n}\n\n._52953be4_errorMark {\n display: inline-block;\n margin-left: 4px;\n color: var(--dsw-alias-state-error-primary);\n font-weight: 700;\n vertical-align: middle;\n}\n\n._f65332f7_tabAdd {\n appearance: none;\n border: none;\n background: none;\n font: inherit;\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px;\n cursor: pointer;\n border-radius: 6px;\n}\n._f65332f7_tabAdd:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n._f65332f7_tabAdd:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }\n._f65332f7_tabAdd:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n._30e90023_tabMenu { position: relative; display: inline-flex; }\n\n._43fc138e_menuBtn {\n appearance: none;\n border: none;\n background: none;\n font: inherit;\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px;\n cursor: pointer;\n border-radius: 6px;\n}\n._43fc138e_menuBtn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n._43fc138e_menuBtn:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }\n\n._6674f868_menuPanel {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n z-index: 20;\n min-width: 240px;\n background: var(--dsw-alias-bg-layer-3);\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-md, 8px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n padding: 10px;\n display: grid;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._a4112fce_menuInput { width: 100%; }\n\n._54106171_menuActions { display: flex; justify-content: flex-end; }\n\n._b76100ea_menuItemDanger {\n appearance: none;\n border: none;\n background: none;\n font: inherit;\n color: var(--dsw-alias-state-error-primary);\n text-align: left;\n padding: 6px 4px;\n cursor: pointer;\n border-radius: 6px;\n}\n._b76100ea_menuItemDanger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-danger); }\n._b76100ea_menuItemDanger:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }\n\n._6e67618d_fadeL,\n._50673253_fadeR {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 24px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n}\n._6e67618d_fadeL { left: 0; background: linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent); }\n._50673253_fadeR { right: 0; background: linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent); }\n._71f663ad_tabsWrap:has([data-tablist][data-scroll-left='1']) ._6e67618d_fadeL,\n._71f663ad_tabsWrap:has([data-tablist][data-scroll-right='1']) ._50673253_fadeR { opacity: 1; }\n\n._a87096d3_emptyState {\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md, 8px);\n padding: 24px;\n margin: 12px 0;\n display: grid;\n gap: 8px;\n justify-items: start;\n color: var(--dsw-alias-label-secondary);\n}\n._a87096d3_emptyState strong { color: var(--dsw-alias-label-primary); }\n\n._5f2c1c0a_chainBody { min-width: 0; }\n\n._142cbcb6_chainFoot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n flex-wrap: wrap;\n}\n\n._3fa8f843_chainFootHint {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n margin-left: auto;\n}\n";
|
|
1465
|
+
const tagId$2 = "dsh-llm-fallbacks/FallbacksCard.module.css";
|
|
1466
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
1794
1467
|
const tag = document.createElement("style");
|
|
1795
1468
|
tag.dataset.plugin = "dsh-llm-fallbacks";
|
|
1796
|
-
tag.dataset.pluginCss = tagId$
|
|
1797
|
-
tag.textContent = css$
|
|
1469
|
+
tag.dataset.pluginCss = tagId$2;
|
|
1470
|
+
tag.textContent = css$2;
|
|
1798
1471
|
document.head.appendChild(tag);
|
|
1799
1472
|
}
|
|
1800
1473
|
var FallbacksCard_module_css_default = {
|
|
@@ -1813,18 +1486,14 @@ window.__ModuleLoader__.load({
|
|
|
1813
1486
|
"sectionToggle": "_b116c758_sectionToggle",
|
|
1814
1487
|
"advancedBody": "_80101203_advancedBody",
|
|
1815
1488
|
"field": "_67826267_field",
|
|
1816
|
-
"checkboxRow": "_9998065c_checkboxRow",
|
|
1817
1489
|
"numberFields": "_3e95886d_numberFields",
|
|
1818
|
-
"checkbox": "_06389b18_checkbox",
|
|
1819
1490
|
"fieldLabel": "_13e68c3f_fieldLabel",
|
|
1820
1491
|
"hint": "_4bc809b8_hint",
|
|
1821
|
-
"checkLabel": "_02c2f6df_checkLabel",
|
|
1822
|
-
"checkLabelTitle": "_f2d47237_checkLabelTitle",
|
|
1823
|
-
"checkLabelDesc": "_c1bed8ea_checkLabelDesc",
|
|
1824
1492
|
"slotPresetName": "_b1962759_slotPresetName",
|
|
1825
1493
|
"infoHint": "_a6244318_infoHint",
|
|
1826
1494
|
"infoHintDisabled": "_e655c840_infoHintDisabled",
|
|
1827
1495
|
"optionRow": "_a2521bb0_optionRow",
|
|
1496
|
+
"checkbox": "_06389b18_checkbox",
|
|
1828
1497
|
"dayRow": "_c4ecbdf3_dayRow",
|
|
1829
1498
|
"dayCell": "_0330c1ed_dayCell",
|
|
1830
1499
|
"input": "_f9d86f7b_input",
|
|
@@ -1885,92 +1554,71 @@ window.__ModuleLoader__.load({
|
|
|
1885
1554
|
"policyStatusLive": "_579f87fb_policyStatusLive",
|
|
1886
1555
|
"policyStatusLabel": "_d4b67a81_policyStatusLabel",
|
|
1887
1556
|
"policyStatusLine": "_278bb4b3_policyStatusLine",
|
|
1888
|
-
"policyStatusWarn": "_da6387e5_policyStatusWarn"
|
|
1557
|
+
"policyStatusWarn": "_da6387e5_policyStatusWarn",
|
|
1558
|
+
"tabsWrap": "_71f663ad_tabsWrap",
|
|
1559
|
+
"tabs": "_0c1dcd2d_tabs",
|
|
1560
|
+
"tab": "_98f72e4c_tab",
|
|
1561
|
+
"dirtyPip": "_0ebd08c8_dirtyPip",
|
|
1562
|
+
"errorMark": "_52953be4_errorMark",
|
|
1563
|
+
"tabAdd": "_f65332f7_tabAdd",
|
|
1564
|
+
"tabMenu": "_30e90023_tabMenu",
|
|
1565
|
+
"menuBtn": "_43fc138e_menuBtn",
|
|
1566
|
+
"menuPanel": "_6674f868_menuPanel",
|
|
1567
|
+
"menuInput": "_a4112fce_menuInput",
|
|
1568
|
+
"menuActions": "_54106171_menuActions",
|
|
1569
|
+
"menuItemDanger": "_b76100ea_menuItemDanger",
|
|
1570
|
+
"fadeL": "_6e67618d_fadeL",
|
|
1571
|
+
"fadeR": "_50673253_fadeR",
|
|
1572
|
+
"emptyState": "_a87096d3_emptyState",
|
|
1573
|
+
"chainBody": "_5f2c1c0a_chainBody",
|
|
1574
|
+
"chainFoot": "_142cbcb6_chainFoot",
|
|
1575
|
+
"chainFootHint": "_3fa8f843_chainFootHint"
|
|
1889
1576
|
};
|
|
1890
1577
|
//#endregion
|
|
1891
1578
|
//#region src/client/FallbacksCard.tsx
|
|
1892
1579
|
/**
|
|
1893
|
-
* Fallbacks settings card — the `fallbacks` plugin card on the web
|
|
1894
|
-
*
|
|
1895
|
-
*
|
|
1896
|
-
* the upstream bash/agent-loop/web-search cards and the advisor card, in
|
|
1897
|
-
* registration order); owner props are empty and all data flows
|
|
1898
|
-
* through {@link FallbacksSettingsController}.
|
|
1580
|
+
* Fallbacks settings card — the `fallbacks` plugin card on the web Plugins
|
|
1581
|
+
* page (named virtual chains, plan named-virtual-chains T5; the FROZEN UI
|
|
1582
|
+
* mockup `.mstar/design/named-virtual-chains/settings-mockup.html`).
|
|
1899
1583
|
*
|
|
1900
|
-
*
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1907
|
-
*
|
|
1908
|
-
*
|
|
1909
|
-
*
|
|
1910
|
-
* headings. The card is ALWAYS open —
|
|
1911
|
-
* nothing needs card-level disclosure state; the degraded /
|
|
1912
|
-
* error / read-only / migration notices render flat atop the form (the
|
|
1913
|
-
* unavailable notice stays always-visible per KD-G5 — the documented
|
|
1914
|
-
* divergence from the advisor, whose unavailable card renders nothing: the
|
|
1915
|
-
* fallbacks skeleton stays the USABLE last-known form and saves are
|
|
1916
|
-
* attempted).
|
|
1584
|
+
* Flat form vocabulary unchanged: fields at 12px vertical padding with 0.5px
|
|
1585
|
+
* hairline separators, 34px inputs on the 0.5px l4 border, per-section
|
|
1586
|
+
* Discard + Save pairs. The 虚拟链 group is TAB-PER-CHAIN: underline tabs on
|
|
1587
|
+
* the 0.5px l2 hairline (active label = label-primary + 2px brand underline,
|
|
1588
|
+
* dirty pip, error "!" mark), a "+" add affordance and a "⋯" chain menu
|
|
1589
|
+
* (delete, ≥1 chain), WAI-ARIA tabs with roving tabindex.
|
|
1590
|
+
* Each tab body carries 链名称 (the single label editor — the ⋯ menu binds to
|
|
1591
|
+
* the same draft field) followed by the SHIPPED block order 分时槽设置 →
|
|
1592
|
+
* 默认降级链 → 默认模型(链尾) plus a per-tab Discard/Save writing ONLY that
|
|
1593
|
+
* chain (block ids per chain, error text under the owning block).
|
|
1917
1594
|
*
|
|
1918
|
-
* The
|
|
1919
|
-
*
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1595
|
+
* The 子代理 section is role cards: id + persona + 指定降级链 (a MANUAL
|
|
1596
|
+
* named-chain pointer; 未指定(沿用 dsh 默认) = the plugin does not
|
|
1597
|
+
* intervene). No rules, no per-role chain entries, no host-policy block.
|
|
1598
|
+
* The 高级选项 group is unchanged; the 运行状态(只读) block and the
|
|
1599
|
+
* General settings row are REMOVED with the client switch-history read.
|
|
1922
1600
|
*
|
|
1923
|
-
*
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
* bottom Save outweighed it. Each section's Save writes ONLY that section's
|
|
1927
|
-
* fields (patch = the last ACCEPTED config with the section's fields
|
|
1928
|
-
* replaced — the ride-along protection, PR #62 UX round 3), gated by that
|
|
1929
|
-
* section's dirty term and validation bucket only (KD-U1: save =
|
|
1930
|
-
* `!sectionDirty || saving || !writable`, discard = `!sectionDirty ||
|
|
1931
|
-
* saving`; Save additionally carries `!writable` — W-1, qc2 fix wave: the
|
|
1932
|
-
* dirty-implies-writable assumption does not hold for this in-place-draft
|
|
1933
|
-
* store). Violations render live under their OWNING section heading, and a
|
|
1934
|
-
* store write failure renders under the section whose Save was last
|
|
1935
|
-
* clicked (`lastSaveSection`). Discard is kept — our staged edits survive
|
|
1936
|
-
* refresh, the advisor's documented divergence from the official
|
|
1937
|
-
* no-discard form — a pure client-side revert of THAT section's editors to
|
|
1938
|
-
* the last accepted config.
|
|
1601
|
+
* Validation blocks the owning save only: chain id format/duplicates,
|
|
1602
|
+
* missing label, per-chain slot guards, tail conformance, role id checks.
|
|
1603
|
+
* A non-empty `state.legacyKeys` renders the migration banner at the top.
|
|
1939
1604
|
*
|
|
1940
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1943
|
-
* and the roles block (block 2 — declared role entity cards from
|
|
1944
|
-
* `roles.list` plus the rule rows from `roles.rules`, whose role field is a
|
|
1945
|
-
* dropdown bound to the declared ids + the built-in `inherit`, same-page
|
|
1946
|
-
* live). Row-level disclosures that are CONTENT keep their expand/collapse:
|
|
1947
|
-
* the time-slot row cards and the role entity cards (with the seeded
|
|
1948
|
-
* persona briefs) stay collapsible — a read-only view forces them open so
|
|
1949
|
-
* the configs stay reachable. The 主代理 / 子代理 / 高级选项 group labels
|
|
1950
|
-
* are semantic `<h2>` headings (T1); 高级选项 is a content-level disclosure
|
|
1951
|
-
* collapsed by default (T3 — same expand/collapse contract as the rows, a
|
|
1952
|
-
* read-only view forces it open).
|
|
1953
|
-
*
|
|
1954
|
-
* Validation: id format/reserved word/duplicates, undeclared rule role
|
|
1955
|
-
* references, illegal selectors, and a role with no chain entries (no model
|
|
1956
|
-
* config) block the write (the Save disabled gate) with per-section banners
|
|
1957
|
-
* + inline red borders / hints (never touching the store error path); a
|
|
1958
|
-
* non-empty `state.legacyKeys` renders the migration banner at the top.
|
|
1959
|
-
* The reset-to-defaults affordance is GONE from the card (PR #62 UX
|
|
1960
|
-
* round 3) — the gateway RPC `fallbacks/reset` and the store
|
|
1961
|
-
* `resetToDefaults()` stay as host APIs, only the card UI was removed.
|
|
1962
|
-
*
|
|
1963
|
-
* The AC-7 read-only status block (recent-switch summary, D-5) stays at the
|
|
1964
|
-
* bottom, flat.
|
|
1965
|
-
*
|
|
1966
|
-
* Presentation is ui-primitives-free (T4, advisor parity + reference §4
|
|
1967
|
-
* risk): Button/Tooltip and the five icon imports are replaced by
|
|
1968
|
-
* self-drawn styled elements — inline SVG icons (plus / trash / chevron /
|
|
1969
|
-
* grip) and the CSS `data-tip` tooltip bubble (the card's existing
|
|
1970
|
-
* icon-button pattern); every color resolves through a `--dsw-alias-*`
|
|
1971
|
-
* token so the card adapts to the light/dark theme.
|
|
1605
|
+
* Presentation stays ui-primitives-free: inline SVG icons and the CSS
|
|
1606
|
+
* `data-tip` tooltip bubble; every color resolves through a
|
|
1607
|
+
* `--dsw-alias-*` token (light/dark parity).
|
|
1972
1608
|
*/
|
|
1973
|
-
|
|
1609
|
+
function collectInvalidRoleIds(rows) {
|
|
1610
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1611
|
+
for (const row of rows) {
|
|
1612
|
+
const id = row.id.trim();
|
|
1613
|
+
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
1614
|
+
}
|
|
1615
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
1616
|
+
for (const row of rows) {
|
|
1617
|
+
const id = row.id.trim();
|
|
1618
|
+
if (!ROLE_ID_PATTERN.test(id) || id === "inherit" || (counts.get(id) ?? 0) > 1) invalid.add(id);
|
|
1619
|
+
}
|
|
1620
|
+
return invalid;
|
|
1621
|
+
}
|
|
1974
1622
|
const SLOT_PRESET_IDS = [
|
|
1975
1623
|
"liang-peak",
|
|
1976
1624
|
"liang-valley",
|
|
@@ -2114,98 +1762,18 @@ window.__ModuleLoader__.load({
|
|
|
2114
1762
|
const offset = tzUtcOffset(tz);
|
|
2115
1763
|
return offset === "" ? tz : `${tz} (${offset})`;
|
|
2116
1764
|
}
|
|
2117
|
-
/** Persist tz: presets lock UTC+8; custom-only uses the host zone; else keep the accepted value. */
|
|
2118
|
-
function resolvedSlotTz(rows, fallback) {
|
|
2119
|
-
if (rows.some((row) => row.kind === "preset")) return "Asia/Shanghai";
|
|
2120
|
-
if (rows.some((row) => row.kind === "custom")) return hostTimeZone();
|
|
2121
|
-
return fallback === "" ? "Asia/Shanghai" : fallback;
|
|
2122
|
-
}
|
|
2123
1765
|
/** Strict 24h `HH:mm` — the resolver's HHMM_RE twin (drift-guarded by the gateway reject-on-save). */
|
|
2124
1766
|
const HHMM_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
2125
|
-
/**
|
|
2126
|
-
* The 默认模型 value for a chain: the official id when the chain TAIL
|
|
2127
|
-
* is that model (Flash or Pro XOR — leading 默认降级链 entries allowed);
|
|
2128
|
-
* `''` for an empty chain or a chain whose last entry is not official
|
|
2129
|
-
* (the panel reads back unselected and save validation blocks the value).
|
|
2130
|
-
*/
|
|
2131
|
-
function allDayModelOf(chain) {
|
|
2132
|
-
const tail = chain.length >= 1 ? chain[chain.length - 1] : void 0;
|
|
2133
|
-
return tail !== void 0 && isOfficialAllDayId(tail) ? tail : "";
|
|
2134
|
-
}
|
|
2135
|
-
/**
|
|
2136
|
-
* The 默认降级链 editor row: the leading entries BEFORE the official tail
|
|
2137
|
-
* (Flash or Pro), or the whole chain while the tail is not official (the
|
|
2138
|
-
* draft rides the accepted value until a 默认模型 pick).
|
|
2139
|
-
*/
|
|
2140
|
-
function allDayChainRowOf(chain, catalog) {
|
|
2141
|
-
return rootChainToRows(allDayModelOf(chain) === "" ? chain : chain.slice(0, -1), catalog)[0];
|
|
2142
|
-
}
|
|
2143
|
-
/** Split scalars from the row editors (rootChain / role entities / role rules). */
|
|
2144
1767
|
function scalarsOf(config) {
|
|
2145
1768
|
return {
|
|
2146
1769
|
triggerCodes: [...config.triggerCodes],
|
|
2147
1770
|
cooldownMs: config.cooldownMs,
|
|
2148
1771
|
revertPolicy: config.revertPolicy,
|
|
2149
1772
|
maxSwitchesPerStep: config.maxSwitchesPerStep,
|
|
2150
|
-
alwaysModeRetryCap: config.alwaysModeRetryCap,
|
|
2151
|
-
roleAutoMatch: config.roleAutoMatch,
|
|
2152
1773
|
tz: config.tz ?? "Asia/Shanghai"
|
|
2153
1774
|
};
|
|
2154
1775
|
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Assemble the full config the row editors + scalars describe. The rebuilt
|
|
2157
|
-
* `roles.list` comes from the rows, with the schema-reserved
|
|
2158
|
-
* `prompt`/`permissions` merged back from the last accepted config by role
|
|
2159
|
-
* id (see {@link mergeRoleExtras}) so a save never silently drops them
|
|
2160
|
-
* (T2 reviewer minor #2). `presets` (spec §9.4) follows the same rule at
|
|
2161
|
-
* the top level: no presets UI this iteration (R-001 re-defer), so the
|
|
2162
|
-
* draft carries the accepted value through untouched — a clean draft stays
|
|
2163
|
-
* equal to the accepted config and a save never drops the key.
|
|
2164
|
-
* `roleAutoMatch` follows the same rule (config-model mirror of `presets`):
|
|
2165
|
-
* the draft carries the scalar's value through untouched. The scalar is
|
|
2166
|
-
* ALWAYS defined — the gateway composition resolves the schema default
|
|
2167
|
-
* `true` even for a legacy config that never declared the key — so the
|
|
2168
|
-
* toggle always renders (default on) and a save persists the resolved value
|
|
2169
|
-
* (AC-7 re-scope, PM decision 2026-08-17 Option A).
|
|
2170
|
-
*
|
|
2171
|
-
* All-day: rootChain is composed from the 默认降级链 editor's leading
|
|
2172
|
-
* selectors plus the 默认模型 tail (exactly one official model — Flash or
|
|
2173
|
-
* Pro XOR). While no tail is selected the ACCEPTED chain rides through
|
|
2174
|
-
* untouched. `timeSlots` is rebuilt from the slot rows every render. `tz`
|
|
2175
|
-
* is a card scalar: preset rows lock it to Asia/Shanghai; custom rows
|
|
2176
|
-
* follow the selected timezone.
|
|
2177
|
-
*/
|
|
2178
|
-
function assembleConfig(scalars, allDayModel, acceptedRootChain, allDayChainRow, roleRows, ruleRows, originalRoles, presets, roleAutoMatch, timeSlotRows) {
|
|
2179
|
-
const list = mergeRoleExtras(roleRows, originalRoles);
|
|
2180
|
-
const trailingChain = rowsToRootChain([allDayChainRow]);
|
|
2181
|
-
const tz = resolvedSlotTz(timeSlotRows, scalars.tz);
|
|
2182
|
-
return {
|
|
2183
|
-
triggerCodes: [...scalars.triggerCodes],
|
|
2184
|
-
rootChain: allDayModel === "" ? [...acceptedRootChain] : [...trailingChain, allDayModel],
|
|
2185
|
-
roles: {
|
|
2186
|
-
list,
|
|
2187
|
-
rules: rowsToRules(ruleRows)
|
|
2188
|
-
},
|
|
2189
|
-
cooldownMs: scalars.cooldownMs,
|
|
2190
|
-
revertPolicy: scalars.revertPolicy,
|
|
2191
|
-
maxSwitchesPerStep: scalars.maxSwitchesPerStep,
|
|
2192
|
-
alwaysModeRetryCap: scalars.alwaysModeRetryCap,
|
|
2193
|
-
...presets === void 0 ? {} : { presets },
|
|
2194
|
-
roleAutoMatch,
|
|
2195
|
-
timeSlots: rowsToTimeSlots(timeSlotRows),
|
|
2196
|
-
tz
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
/**
|
|
2200
|
-
* Badge label for one row's provenance (plan role-card-seeded-ux): every
|
|
2201
|
-
* row states where its seed state comes from. The three reserved labels
|
|
2202
|
-
* localize — `bundled`, `external` (the shared unnamed-producer slice;
|
|
2203
|
-
* plan seeds-source-and-persona-width), and the operator's own rows —
|
|
2204
|
-
* while a live seed declared under a registered set name renders that
|
|
2205
|
-
* name VERBATIM (case-preserved — no case-folding). `null` = no badge: a
|
|
2206
|
-
* currently-seeded row whose wire entry lacks `source` (gateway version
|
|
2207
|
-
* skew) — the degradation path, never an error.
|
|
2208
|
-
*/
|
|
1776
|
+
/** Badge label for one row's provenance; `null` = no badge (version skew). */
|
|
2209
1777
|
function seedBadgeLabel(seed, t) {
|
|
2210
1778
|
if (seed === void 0) return t("roles.seedSource.user");
|
|
2211
1779
|
if (seed.source === void 0) return null;
|
|
@@ -2215,105 +1783,66 @@ window.__ModuleLoader__.load({
|
|
|
2215
1783
|
return seed.source;
|
|
2216
1784
|
}
|
|
2217
1785
|
/**
|
|
2218
|
-
*
|
|
2219
|
-
*
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
2222
|
-
* rootChain and role chains. Returns one localized message per violation,
|
|
2223
|
-
* bucketed by the section that owns the offending field (主代理: allDay /
|
|
2224
|
-
* timeSlots / slot* / tz / default model / default chain; 子代理: role* /
|
|
2225
|
-
* rule*; 高级选项: trigger / cooldown / revert / always / roleAutoMatch —
|
|
2226
|
-
* the scalars are never validated, so the advanced bucket stays empty
|
|
2227
|
-
* today). A non-empty bucket blocks THAT section's Save write only
|
|
2228
|
-
* (per-section saves, plan fallbacks-card-section-ux) — a sibling section
|
|
2229
|
-
* with a clean bucket still saves. `persona` is free text and never
|
|
2230
|
-
* validated.
|
|
2231
|
-
*
|
|
2232
|
-
* `seedInfo` is the live trimmed-id → seed-entry map derived from
|
|
2233
|
-
* `state.seeds` (spec §9.4): the empty-chain block relaxes for seeded ids
|
|
2234
|
-
* only (spec §9.6 / AC-3 — a seeded role's chain is legitimately empty by
|
|
2235
|
-
* design, R4, and the row must stay persistable); non-seeded behavior is
|
|
2236
|
-
* byte-identical.
|
|
1786
|
+
* Per-chain validation (named virtual chains): chain id format/duplicates +
|
|
1787
|
+
* label presence, the 默认模型 tail conformance, selector legality across
|
|
1788
|
+
* entries and slot rows, and the per-chain slot-row guards. Roles: id
|
|
1789
|
+
* format/reserved/duplicates. Advanced scalars are never validated.
|
|
2237
1790
|
*/
|
|
2238
|
-
function validateDraft(
|
|
2239
|
-
const
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
if (
|
|
2247
|
-
|
|
2248
|
-
if (
|
|
2249
|
-
|
|
2250
|
-
for (const entry of role.chain ?? []) try {
|
|
1791
|
+
function validateDraft(chains, roles, t) {
|
|
1792
|
+
const chainBuckets = chains.map(() => []);
|
|
1793
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1794
|
+
chains.forEach((chain, index) => {
|
|
1795
|
+
const bucket = chainBuckets[index];
|
|
1796
|
+
if (!CHAIN_ID_PATTERN.test(chain.id)) bucket.push(t("validation.chainIdFormat", { id: chain.id }));
|
|
1797
|
+
if (seenIds.has(chain.id)) bucket.push(t("validation.chainIdDuplicate", { id: chain.id }));
|
|
1798
|
+
seenIds.add(chain.id);
|
|
1799
|
+
if (chain.label === "") bucket.push(t("validation.chainLabelRequired", { id: chain.id }));
|
|
1800
|
+
const tail = chain.entries.length >= 1 ? chain.entries[chain.entries.length - 1] : void 0;
|
|
1801
|
+
if (tail === void 0 || !isOfficialAllDayId(tail)) bucket.push(t("validation.allDayRequired"));
|
|
1802
|
+
for (const entry of chain.entries) try {
|
|
2251
1803
|
parseSelector(entry);
|
|
2252
1804
|
} catch (error) {
|
|
2253
|
-
|
|
1805
|
+
bucket.push(t("validation.selector", {
|
|
2254
1806
|
entry,
|
|
2255
1807
|
message: error.message
|
|
2256
1808
|
}));
|
|
2257
1809
|
}
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
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"));
|
|
2280
|
-
if (row.days !== void 0 && row.days.some((day) => !Number.isInteger(day) || day < 0 || day > 6)) errors.main.push(t("validation.slotDays"));
|
|
2281
|
-
}
|
|
2282
|
-
for (const entry of row.chain) try {
|
|
2283
|
-
parseSelector(entry);
|
|
2284
|
-
} catch (error) {
|
|
2285
|
-
errors.main.push(t("validation.selector", {
|
|
2286
|
-
entry,
|
|
2287
|
-
message: error.message
|
|
2288
|
-
}));
|
|
1810
|
+
const seenSlotPresets = /* @__PURE__ */ new Set();
|
|
1811
|
+
for (const row of chain.timeSlots ?? []) {
|
|
1812
|
+
if (row.kind !== "preset" && row.kind !== "custom") bucket.push(t("validation.slotKind"));
|
|
1813
|
+
if (row.kind === "preset") {
|
|
1814
|
+
if (typeof row.preset !== "string" || !SLOT_PRESET_IDS.includes(row.preset)) bucket.push(t("validation.slotPresetUnknown", { preset: row.preset }));
|
|
1815
|
+
else if (seenSlotPresets.has(row.preset)) bucket.push(t("validation.slotPresetDuplicate", { preset: row.preset }));
|
|
1816
|
+
else seenSlotPresets.add(row.preset);
|
|
1817
|
+
if (row.start !== void 0 || row.end !== void 0 || row.days !== void 0 && row.days.length > 0) bucket.push(t("validation.slotPresetFrozen"));
|
|
1818
|
+
} else if (row.kind === "custom") {
|
|
1819
|
+
if (typeof row.start !== "string" || typeof row.end !== "string" || !HHMM_RE.test(row.start) || !HHMM_RE.test(row.end)) bucket.push(t("validation.slotWindow"));
|
|
1820
|
+
if (row.days !== void 0 && row.days.some((day) => !Number.isInteger(day) || day < 0 || day > 6)) bucket.push(t("validation.slotDays"));
|
|
1821
|
+
}
|
|
1822
|
+
for (const entry of row.chain) try {
|
|
1823
|
+
parseSelector(entry);
|
|
1824
|
+
} catch (error) {
|
|
1825
|
+
bucket.push(t("validation.selector", {
|
|
1826
|
+
entry,
|
|
1827
|
+
message: error.message
|
|
1828
|
+
}));
|
|
1829
|
+
}
|
|
1830
|
+
if (row.chain.length === 0) bucket.push(t("validation.slotChainRequired"));
|
|
2289
1831
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
const
|
|
2293
|
-
for (const
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
* / duplicate) — drives the inline red border on the offending id input
|
|
2299
|
-
* (live: the border tracks the draft, matching the live Save disabled
|
|
2300
|
-
* gate). Derived once per render into a Set (qc3 F-3): a duplicate scan
|
|
2301
|
-
* inside the render loop would be O(N²) per row; here the whole derivation
|
|
2302
|
-
* is O(N) and each row's check is a single Set lookup. Selector errors stay
|
|
2303
|
-
* on the banner only (plan Task 3 inline-scope rule).
|
|
2304
|
-
*/
|
|
2305
|
-
function collectInvalidRoleIds(rows) {
|
|
2306
|
-
const counts = /* @__PURE__ */ new Map();
|
|
2307
|
-
for (const row of rows) {
|
|
2308
|
-
const id = row.id.trim();
|
|
2309
|
-
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
2310
|
-
}
|
|
2311
|
-
const invalid = /* @__PURE__ */ new Set();
|
|
2312
|
-
for (const row of rows) {
|
|
2313
|
-
const id = row.id.trim();
|
|
2314
|
-
if (!ROLE_ID_PATTERN.test(id) || id === "inherit" || (counts.get(id) ?? 0) > 1) invalid.add(id);
|
|
1832
|
+
});
|
|
1833
|
+
const roleErrors = [];
|
|
1834
|
+
const declaredIds = /* @__PURE__ */ new Set();
|
|
1835
|
+
for (const role of roles) {
|
|
1836
|
+
if (!ROLE_ID_PATTERN.test(role.id)) roleErrors.push(t("validation.roleIdFormat", { id: role.id }));
|
|
1837
|
+
if (role.id === "inherit") roleErrors.push(t("validation.roleIdReserved"));
|
|
1838
|
+
if (declaredIds.has(role.id)) roleErrors.push(t("validation.roleIdDuplicate", { id: role.id }));
|
|
1839
|
+
declaredIds.add(role.id);
|
|
2315
1840
|
}
|
|
2316
|
-
return
|
|
1841
|
+
return {
|
|
1842
|
+
chains: chainBuckets,
|
|
1843
|
+
roles: roleErrors,
|
|
1844
|
+
advanced: []
|
|
1845
|
+
};
|
|
2317
1846
|
}
|
|
2318
1847
|
/** Parse a number input, clamped to a non-negative integer. */
|
|
2319
1848
|
function parseCount(raw) {
|
|
@@ -2370,60 +1899,6 @@ window.__ModuleLoader__.load({
|
|
|
2370
1899
|
});
|
|
2371
1900
|
}
|
|
2372
1901
|
/**
|
|
2373
|
-
* The virtual all-fallbacks route the host carve-out recognizes (features.md):
|
|
2374
|
-
* a subagent spawned on it is not allowlist-filtered, so it is the
|
|
2375
|
-
* recommendation when the allowlist does not already carry it.
|
|
2376
|
-
*/
|
|
2377
|
-
const VIRTUAL_ALL_ROUTE = {
|
|
2378
|
-
provider: "FallbacksChain",
|
|
2379
|
-
model: "Auto"
|
|
2380
|
-
};
|
|
2381
|
-
/**
|
|
2382
|
-
* Parse one raw selector into an exact route; wildcard and unparseable
|
|
2383
|
-
* entries resolve to `undefined` (they are not exact routes — never expanded,
|
|
2384
|
-
* never counted). The selector parser is the single grammar authority.
|
|
2385
|
-
*/
|
|
2386
|
-
function exactRouteOf(raw) {
|
|
2387
|
-
let parsed;
|
|
2388
|
-
try {
|
|
2389
|
-
parsed = parseSelector(raw);
|
|
2390
|
-
} catch {
|
|
2391
|
-
return;
|
|
2392
|
-
}
|
|
2393
|
-
return parsed.model === void 0 ? void 0 : {
|
|
2394
|
-
provider: parsed.provider,
|
|
2395
|
-
model: parsed.model
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* The whole chain as one line (plan fallbacks-chain-reorder T2, user request):
|
|
2400
|
-
* every non-blank entry in walk order joined with `>`, or `undefined` for an
|
|
2401
|
-
* empty/blank chain so the caller keeps its own summary (a role's raw strategy
|
|
2402
|
-
* token). The card ellipsizes long summaries; the full string rides `title`.
|
|
2403
|
-
*
|
|
2404
|
-
* With `allowedModels` (host policy enabled) exact entries the policy cannot
|
|
2405
|
-
* use are dropped, so the title tells the truth about what will run; wildcard
|
|
2406
|
-
* entries stay (their resolved routes are unknown client-side) and an
|
|
2407
|
-
* unparseable entry is kept rather than hidden.
|
|
2408
|
-
*/
|
|
2409
|
-
function chainSummary(selectors, allowedModels) {
|
|
2410
|
-
const entries = selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
|
|
2411
|
-
const visible = allowedModels === void 0 ? entries : entries.filter((entry) => {
|
|
2412
|
-
const route = exactRouteOf(entry);
|
|
2413
|
-
return route === void 0 || routeOnAllowlist(route, allowedModels);
|
|
2414
|
-
});
|
|
2415
|
-
return visible.length === 0 ? void 0 : visible.join(" > ");
|
|
2416
|
-
}
|
|
2417
|
-
/** How many selected role-chain exact entries the host allowlist does not carry. */
|
|
2418
|
-
function offAllowlistEntryCount(roles, allowedModels) {
|
|
2419
|
-
let count = 0;
|
|
2420
|
-
for (const role of roles) for (const entry of role.chain ?? []) {
|
|
2421
|
-
const route = exactRouteOf(entry);
|
|
2422
|
-
if (route !== void 0 && !routeOnAllowlist(route, allowedModels)) count += 1;
|
|
2423
|
-
}
|
|
2424
|
-
return count;
|
|
2425
|
-
}
|
|
2426
|
-
/**
|
|
2427
1902
|
* Pure chain reorder (plan fallbacks-chain-reorder T1): a copy of `list` with
|
|
2428
1903
|
* the entry at `from` re-inserted at `to`; an out-of-range or same-position
|
|
2429
1904
|
* move returns `list` untouched (the end-clamping the keyboard and the drop
|
|
@@ -2444,9 +1919,8 @@ window.__ModuleLoader__.load({
|
|
|
2444
1919
|
* icon. The visible provider/model field labels are gone — the two selects
|
|
2445
1920
|
* carry their accessible name on `aria-label` (the visible labels repeated on
|
|
2446
1921
|
* every row and cost a whole line).
|
|
2447
|
-
* The GUI never offers a `provider/*` wildcard
|
|
2448
|
-
*
|
|
2449
|
-
* stays a legal YAML entry, so a wildcard row read back from the server
|
|
1922
|
+
* The GUI never offers a `provider/*` wildcard — but `provider/*` stays a
|
|
1923
|
+
* legal YAML entry in any chain, so a wildcard row read back from the server
|
|
2450
1924
|
* renders with the legacy-conversion hint and an enabled model select:
|
|
2451
1925
|
* picking a model converts the row to an exact entry (the patch carries
|
|
2452
1926
|
* `wildcard: false`). The provider options are the catalog providers
|
|
@@ -2688,11 +2162,9 @@ window.__ModuleLoader__.load({
|
|
|
2688
2162
|
});
|
|
2689
2163
|
}
|
|
2690
2164
|
/**
|
|
2691
|
-
* Render the Fallbacks settings card inside the plugin-config section
|
|
2692
|
-
*
|
|
2693
|
-
* the page's plugin title/description
|
|
2694
|
-
* @param props - slot-delivered injected dependencies and the synthesized t seat.
|
|
2695
|
-
* @returns the card.
|
|
2165
|
+
* Render the Fallbacks settings card inside the plugin-config section
|
|
2166
|
+
* (named virtual chains): the 虚拟链 tab group, the 子代理 role cards and
|
|
2167
|
+
* the 高级选项 group, flat under the page's plugin title/description.
|
|
2696
2168
|
*/
|
|
2697
2169
|
function FallbacksCard({ controller, useSnapshot, t }) {
|
|
2698
2170
|
const state = useSnapshot((snapshot) => snapshot);
|
|
@@ -2700,51 +2172,39 @@ window.__ModuleLoader__.load({
|
|
|
2700
2172
|
const snapshot = controller.store.getSnapshot();
|
|
2701
2173
|
if (snapshot.status === "idle") controller.load();
|
|
2702
2174
|
if (snapshot.catalogStatus === "idle") controller.loadCatalog();
|
|
2703
|
-
if (snapshot.switchesStatus === "idle") controller.loadSwitches();
|
|
2704
2175
|
}, [controller]);
|
|
2705
2176
|
const [scalars, setScalars] = (0, react.useState)(() => scalarsOf(defaultFallbacksConfig));
|
|
2706
|
-
const [
|
|
2707
|
-
const [
|
|
2708
|
-
const [timeSlotRows, setTimeSlotRows] = (0, react.useState)(() => timeSlotsToRows(defaultFallbacksConfig.timeSlots ?? []));
|
|
2177
|
+
const [chainDrafts, setChainDrafts] = (0, react.useState)(() => chainsToDrafts(defaultFallbacksConfig.chains));
|
|
2178
|
+
const [activeChain, setActiveChain] = (0, react.useState)(0);
|
|
2709
2179
|
const [roleRows, setRoleRows] = (0, react.useState)(() => rolesToRows(defaultFallbacksConfig.roles.list));
|
|
2710
|
-
const [ruleRows, setRuleRows] = (0, react.useState)(() => rulesToRows(defaultFallbacksConfig.roles.rules));
|
|
2711
2180
|
const [presetToAdd, setPresetToAdd] = (0, react.useState)("");
|
|
2712
2181
|
const [lastSaveSection, setLastSaveSection] = (0, react.useState)(null);
|
|
2182
|
+
const [lastSaveChain, setLastSaveChain] = (0, react.useState)(null);
|
|
2713
2183
|
const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
|
|
2184
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
2714
2185
|
const seededConfigKey = (0, react.useRef)(null);
|
|
2715
|
-
const
|
|
2716
|
-
const
|
|
2717
|
-
const
|
|
2718
|
-
const
|
|
2186
|
+
const firstSeedDone = (0, react.useRef)(false);
|
|
2187
|
+
const chains = chainDrafts.map(draftToChain);
|
|
2188
|
+
const chainsDirty = JSON.stringify(chains) !== JSON.stringify(state.config.chains);
|
|
2189
|
+
const acceptedChainOf = (id) => state.config.chains.find((chain) => chain.id === id);
|
|
2190
|
+
const perChainDirty = chainDrafts.map((chainDraft) => {
|
|
2191
|
+
const accepted = acceptedChainOf(chainDraft.id);
|
|
2192
|
+
return accepted === void 0 || JSON.stringify(draftToChain(chainDraft)) !== JSON.stringify(accepted);
|
|
2193
|
+
});
|
|
2194
|
+
const structureDirty = chainDrafts.length !== state.config.chains.length || chainDrafts.some((draft, index) => state.config.chains[index]?.id !== draft.id);
|
|
2195
|
+
const rolesDraft = { list: mergeRoleExtras(roleRows, state.config.roles.list) };
|
|
2196
|
+
const rolesDirty = JSON.stringify(rolesDraft) !== JSON.stringify(state.config.roles);
|
|
2719
2197
|
const advancedDirty = JSON.stringify([
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
draft.alwaysModeRetryCap,
|
|
2725
|
-
draft.roleAutoMatch
|
|
2198
|
+
scalars.triggerCodes,
|
|
2199
|
+
scalars.cooldownMs,
|
|
2200
|
+
scalars.revertPolicy,
|
|
2201
|
+
scalars.maxSwitchesPerStep
|
|
2726
2202
|
]) !== JSON.stringify([
|
|
2727
2203
|
state.config.triggerCodes,
|
|
2728
2204
|
state.config.cooldownMs,
|
|
2729
2205
|
state.config.revertPolicy,
|
|
2730
|
-
state.config.maxSwitchesPerStep
|
|
2731
|
-
state.config.alwaysModeRetryCap,
|
|
2732
|
-
state.config.roleAutoMatch
|
|
2206
|
+
state.config.maxSwitchesPerStep
|
|
2733
2207
|
]);
|
|
2734
|
-
const enabledPolicy = state.subagentPolicy?.state === "enabled" ? state.subagentPolicy : void 0;
|
|
2735
|
-
const allowedModels = enabledPolicy?.allowedModels;
|
|
2736
|
-
const offAllowlistCount = enabledPolicy === void 0 ? 0 : offAllowlistEntryCount(draft.roles.list, enabledPolicy.allowedModels);
|
|
2737
|
-
const virtualRouteAllowed = enabledPolicy !== void 0 && routeOnAllowlist(VIRTUAL_ALL_ROUTE, enabledPolicy.allowedModels);
|
|
2738
|
-
const [syncStatus, setSyncStatus] = (0, react.useState)("idle");
|
|
2739
|
-
const syncDirtyHintId = `fallbacks-policy-sync-dirty-${(0, react.useId)().replace(/:/g, "")}`;
|
|
2740
|
-
const syncAllowlist = () => {
|
|
2741
|
-
if (syncStatus === "pending") return;
|
|
2742
|
-
setSyncStatus("pending");
|
|
2743
|
-
controller.syncAllowlist().then((ok) => {
|
|
2744
|
-
setSyncStatus(ok ? "ok" : "error");
|
|
2745
|
-
});
|
|
2746
|
-
};
|
|
2747
|
-
const firstSeedDone = (0, react.useRef)(false);
|
|
2748
2208
|
(0, react.useEffect)(() => {
|
|
2749
2209
|
if (state.status !== "ready") return;
|
|
2750
2210
|
const key = JSON.stringify(state.config);
|
|
@@ -2753,35 +2213,21 @@ window.__ModuleLoader__.load({
|
|
|
2753
2213
|
const firstSeed = !firstSeedDone.current;
|
|
2754
2214
|
firstSeedDone.current = true;
|
|
2755
2215
|
const catalog = catalogOf(state);
|
|
2756
|
-
if (firstSeed || !
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
setScalars((prev) => ({
|
|
2761
|
-
...prev,
|
|
2762
|
-
tz: state.config.tz ?? "Asia/Shanghai"
|
|
2763
|
-
}));
|
|
2764
|
-
}
|
|
2765
|
-
if (firstSeed || !subDirty) {
|
|
2766
|
-
setRoleRows(rolesToRows(state.config.roles.list, catalog));
|
|
2767
|
-
setRuleRows(rulesToRows(state.config.roles.rules, catalog));
|
|
2216
|
+
if (firstSeed || !chainsDirty) {
|
|
2217
|
+
const drafts = chainsToDrafts(state.config.chains, catalog);
|
|
2218
|
+
setChainDrafts(drafts);
|
|
2219
|
+
setActiveChain((index) => Math.min(index, Math.max(0, drafts.length - 1)));
|
|
2768
2220
|
}
|
|
2221
|
+
if (firstSeed || !rolesDirty) setRoleRows(rolesToRows(state.config.roles.list));
|
|
2769
2222
|
if (firstSeed || !advancedDirty) setScalars((prev) => ({
|
|
2770
2223
|
...prev,
|
|
2771
2224
|
triggerCodes: [...state.config.triggerCodes],
|
|
2772
2225
|
cooldownMs: state.config.cooldownMs,
|
|
2773
2226
|
revertPolicy: state.config.revertPolicy,
|
|
2774
2227
|
maxSwitchesPerStep: state.config.maxSwitchesPerStep,
|
|
2775
|
-
|
|
2776
|
-
roleAutoMatch: state.config.roleAutoMatch
|
|
2228
|
+
tz: state.config.tz ?? "Asia/Shanghai"
|
|
2777
2229
|
}));
|
|
2778
|
-
}, [
|
|
2779
|
-
state.status,
|
|
2780
|
-
state.config,
|
|
2781
|
-
mainDirty,
|
|
2782
|
-
subDirty,
|
|
2783
|
-
advancedDirty
|
|
2784
|
-
]);
|
|
2230
|
+
}, [state.status, state.config]);
|
|
2785
2231
|
const updateScalars = (mutator) => {
|
|
2786
2232
|
setScalars((prev) => {
|
|
2787
2233
|
const next = {
|
|
@@ -2792,139 +2238,195 @@ window.__ModuleLoader__.load({
|
|
|
2792
2238
|
return next;
|
|
2793
2239
|
});
|
|
2794
2240
|
};
|
|
2241
|
+
const patchActiveDraft = (mutator) => {
|
|
2242
|
+
setChainDrafts((drafts) => drafts.map((draft, index) => index === activeChain ? mutator({
|
|
2243
|
+
...draft,
|
|
2244
|
+
slots: draft.slots.map((row) => ({
|
|
2245
|
+
...row,
|
|
2246
|
+
selectors: row.selectors.map((s) => ({ ...s }))
|
|
2247
|
+
})),
|
|
2248
|
+
selectors: draft.selectors.map((s) => ({ ...s }))
|
|
2249
|
+
}) : draft));
|
|
2250
|
+
};
|
|
2795
2251
|
const updateTimeSlotRow = (index, patch) => {
|
|
2796
|
-
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
...
|
|
2252
|
+
patchActiveDraft((draft) => {
|
|
2253
|
+
const slots = draft.slots.map((row) => ({ ...row }));
|
|
2254
|
+
slots[index] = {
|
|
2255
|
+
...slots[index],
|
|
2800
2256
|
...patch
|
|
2801
2257
|
};
|
|
2802
|
-
return
|
|
2258
|
+
return {
|
|
2259
|
+
...draft,
|
|
2260
|
+
slots
|
|
2261
|
+
};
|
|
2803
2262
|
});
|
|
2804
2263
|
};
|
|
2805
2264
|
const updateTimeSlotSelector = (rowIndex, selectorIndex, patch) => {
|
|
2806
|
-
|
|
2807
|
-
const
|
|
2265
|
+
patchActiveDraft((draft) => {
|
|
2266
|
+
const slots = draft.slots.map((row) => ({
|
|
2808
2267
|
...row,
|
|
2809
2268
|
selectors: row.selectors.map((selector) => ({ ...selector }))
|
|
2810
2269
|
}));
|
|
2811
|
-
const selectors =
|
|
2270
|
+
const selectors = slots[rowIndex].selectors;
|
|
2812
2271
|
selectors[selectorIndex] = {
|
|
2813
2272
|
...selectors[selectorIndex],
|
|
2814
2273
|
...patch
|
|
2815
2274
|
};
|
|
2816
|
-
return
|
|
2275
|
+
return {
|
|
2276
|
+
...draft,
|
|
2277
|
+
slots
|
|
2278
|
+
};
|
|
2817
2279
|
});
|
|
2818
2280
|
};
|
|
2819
2281
|
const addTimeSlotSelector = (rowIndex) => {
|
|
2820
|
-
|
|
2821
|
-
...
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2282
|
+
patchActiveDraft((draft) => ({
|
|
2283
|
+
...draft,
|
|
2284
|
+
slots: draft.slots.map((row, index) => index === rowIndex ? {
|
|
2285
|
+
...row,
|
|
2286
|
+
selectors: [...row.selectors, {
|
|
2287
|
+
wildcard: false,
|
|
2288
|
+
provider: null,
|
|
2289
|
+
model: null
|
|
2290
|
+
}]
|
|
2291
|
+
} : row)
|
|
2292
|
+
}));
|
|
2828
2293
|
};
|
|
2829
2294
|
const removeTimeSlotSelector = (rowIndex, selectorIndex) => {
|
|
2830
|
-
|
|
2831
|
-
...
|
|
2832
|
-
|
|
2833
|
-
|
|
2295
|
+
patchActiveDraft((draft) => ({
|
|
2296
|
+
...draft,
|
|
2297
|
+
slots: draft.slots.map((row, index) => index === rowIndex ? {
|
|
2298
|
+
...row,
|
|
2299
|
+
selectors: row.selectors.filter((_, sIndex) => sIndex !== selectorIndex)
|
|
2300
|
+
} : row)
|
|
2301
|
+
}));
|
|
2834
2302
|
};
|
|
2835
2303
|
const addPresetSlotRow = () => {
|
|
2836
2304
|
if (presetToAdd === "") return;
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2305
|
+
patchActiveDraft((draft) => ({
|
|
2306
|
+
...draft,
|
|
2307
|
+
slots: [...draft.slots, {
|
|
2308
|
+
kind: "preset",
|
|
2309
|
+
preset: presetToAdd,
|
|
2310
|
+
start: "",
|
|
2311
|
+
end: "",
|
|
2312
|
+
days: [],
|
|
2313
|
+
name: "",
|
|
2314
|
+
collapsed: false,
|
|
2315
|
+
selectors: []
|
|
2316
|
+
}]
|
|
2317
|
+
}));
|
|
2848
2318
|
setPresetToAdd("");
|
|
2849
2319
|
};
|
|
2850
2320
|
const addCustomSlotRow = () => {
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2321
|
+
patchActiveDraft((draft) => ({
|
|
2322
|
+
...draft,
|
|
2323
|
+
slots: [...draft.slots, {
|
|
2324
|
+
kind: "custom",
|
|
2325
|
+
start: "",
|
|
2326
|
+
end: "",
|
|
2327
|
+
days: [],
|
|
2328
|
+
name: "",
|
|
2329
|
+
collapsed: false,
|
|
2330
|
+
selectors: []
|
|
2331
|
+
}]
|
|
2332
|
+
}));
|
|
2860
2333
|
};
|
|
2861
2334
|
const removeTimeSlotRow = (index) => {
|
|
2862
|
-
|
|
2335
|
+
patchActiveDraft((draft) => ({
|
|
2336
|
+
...draft,
|
|
2337
|
+
slots: draft.slots.filter((_, rowIndex) => rowIndex !== index)
|
|
2338
|
+
}));
|
|
2863
2339
|
};
|
|
2864
2340
|
const moveTimeSlotRow = (index, delta) => {
|
|
2865
|
-
|
|
2341
|
+
patchActiveDraft((draft) => {
|
|
2866
2342
|
const target = index + delta;
|
|
2867
|
-
if (target < 0 || target >=
|
|
2868
|
-
const
|
|
2869
|
-
const moved =
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
return
|
|
2343
|
+
if (target < 0 || target >= draft.slots.length) return draft;
|
|
2344
|
+
const slots = [...draft.slots];
|
|
2345
|
+
const moved = slots[index];
|
|
2346
|
+
slots[index] = slots[target];
|
|
2347
|
+
slots[target] = moved;
|
|
2348
|
+
return {
|
|
2349
|
+
...draft,
|
|
2350
|
+
slots
|
|
2351
|
+
};
|
|
2873
2352
|
});
|
|
2874
2353
|
};
|
|
2875
2354
|
const [draggedSlotIndex, setDraggedSlotIndex] = (0, react.useState)(null);
|
|
2876
2355
|
const [overSlotIndex, setOverSlotIndex] = (0, react.useState)(null);
|
|
2877
2356
|
const reorderTimeSlotRow = (from, to) => {
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
const [moved] =
|
|
2882
|
-
|
|
2883
|
-
return
|
|
2357
|
+
patchActiveDraft((draft) => {
|
|
2358
|
+
const slots = [...draft.slots];
|
|
2359
|
+
if (from === to || from < 0 || to < 0 || from >= slots.length || to >= slots.length) return draft;
|
|
2360
|
+
const [moved] = slots.splice(from, 1);
|
|
2361
|
+
slots.splice(to, 0, moved);
|
|
2362
|
+
return {
|
|
2363
|
+
...draft,
|
|
2364
|
+
slots
|
|
2365
|
+
};
|
|
2884
2366
|
});
|
|
2885
2367
|
};
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
...
|
|
2889
|
-
|
|
2368
|
+
const moveSlotSelector = (rowIndex, from, to) => {
|
|
2369
|
+
patchActiveDraft((draft) => ({
|
|
2370
|
+
...draft,
|
|
2371
|
+
slots: draft.slots.map((slot, slotIndex) => slotIndex === rowIndex ? {
|
|
2372
|
+
...slot,
|
|
2373
|
+
selectors: moveItem(slot.selectors, from, to)
|
|
2374
|
+
} : slot)
|
|
2375
|
+
}));
|
|
2376
|
+
};
|
|
2377
|
+
const updateChainSelector = (selectorIndex, patch) => {
|
|
2378
|
+
patchActiveDraft((draft) => {
|
|
2379
|
+
const selectors = draft.selectors.map((selector, index) => index === selectorIndex ? {
|
|
2890
2380
|
...selector,
|
|
2891
2381
|
...patch
|
|
2892
|
-
} : selector)
|
|
2893
|
-
|
|
2382
|
+
} : selector);
|
|
2383
|
+
return {
|
|
2384
|
+
...draft,
|
|
2385
|
+
selectors
|
|
2386
|
+
};
|
|
2387
|
+
});
|
|
2894
2388
|
};
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2897
|
-
...
|
|
2898
|
-
selectors: [...
|
|
2389
|
+
const addChainSelector = () => {
|
|
2390
|
+
patchActiveDraft((draft) => ({
|
|
2391
|
+
...draft,
|
|
2392
|
+
selectors: [...draft.selectors, {
|
|
2899
2393
|
wildcard: false,
|
|
2900
2394
|
provider: null,
|
|
2901
2395
|
model: null
|
|
2902
2396
|
}]
|
|
2903
2397
|
}));
|
|
2904
2398
|
};
|
|
2905
|
-
const
|
|
2906
|
-
|
|
2907
|
-
...
|
|
2908
|
-
selectors:
|
|
2399
|
+
const removeChainSelector = (selectorIndex) => {
|
|
2400
|
+
patchActiveDraft((draft) => ({
|
|
2401
|
+
...draft,
|
|
2402
|
+
selectors: draft.selectors.filter((_, index) => index !== selectorIndex)
|
|
2909
2403
|
}));
|
|
2910
2404
|
};
|
|
2911
|
-
const
|
|
2912
|
-
|
|
2913
|
-
...
|
|
2914
|
-
selectors: moveItem(
|
|
2405
|
+
const reorderChainSelector = (from, to) => {
|
|
2406
|
+
patchActiveDraft((draft) => ({
|
|
2407
|
+
...draft,
|
|
2408
|
+
selectors: moveItem(draft.selectors, from, to)
|
|
2915
2409
|
}));
|
|
2916
2410
|
};
|
|
2917
|
-
const
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2411
|
+
const addChain = () => {
|
|
2412
|
+
let added = chainDrafts.length;
|
|
2413
|
+
setChainDrafts((drafts) => {
|
|
2414
|
+
added = drafts.length;
|
|
2415
|
+
return [...drafts, {
|
|
2416
|
+
id: newChainId(drafts),
|
|
2417
|
+
label: "",
|
|
2418
|
+
selectors: [],
|
|
2419
|
+
slots: []
|
|
2420
|
+
}];
|
|
2421
|
+
});
|
|
2422
|
+
setActiveChain(added);
|
|
2423
|
+
setMenuOpen(false);
|
|
2922
2424
|
};
|
|
2923
|
-
const
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2425
|
+
const deleteActiveChain = () => {
|
|
2426
|
+
if (chainDrafts.length <= 1) return;
|
|
2427
|
+
setChainDrafts((drafts) => drafts.filter((_, index) => index !== activeChain));
|
|
2428
|
+
setActiveChain((index) => Math.max(0, index - 1));
|
|
2429
|
+
setMenuOpen(false);
|
|
2928
2430
|
};
|
|
2929
2431
|
const updateRoleRow = (index, patch) => {
|
|
2930
2432
|
setRoleRows((rows) => {
|
|
@@ -2936,42 +2438,11 @@ window.__ModuleLoader__.load({
|
|
|
2936
2438
|
return next;
|
|
2937
2439
|
});
|
|
2938
2440
|
};
|
|
2939
|
-
const updateRoleSelector = (roleIndex, selectorIndex, patch) => {
|
|
2940
|
-
setRoleRows((rows) => {
|
|
2941
|
-
const next = rows.map((row) => ({
|
|
2942
|
-
...row,
|
|
2943
|
-
selectors: row.selectors.map((selector) => ({ ...selector }))
|
|
2944
|
-
}));
|
|
2945
|
-
const selectors = next[roleIndex].selectors;
|
|
2946
|
-
selectors[selectorIndex] = {
|
|
2947
|
-
...selectors[selectorIndex],
|
|
2948
|
-
...patch
|
|
2949
|
-
};
|
|
2950
|
-
return next;
|
|
2951
|
-
});
|
|
2952
|
-
};
|
|
2953
|
-
const addRoleSelector = (roleIndex) => {
|
|
2954
|
-
setRoleRows((rows) => rows.map((row, index) => index === roleIndex ? {
|
|
2955
|
-
...row,
|
|
2956
|
-
selectors: [...row.selectors, {
|
|
2957
|
-
wildcard: false,
|
|
2958
|
-
provider: null,
|
|
2959
|
-
model: null
|
|
2960
|
-
}]
|
|
2961
|
-
} : row));
|
|
2962
|
-
};
|
|
2963
|
-
const removeRoleSelector = (roleIndex, selectorIndex) => {
|
|
2964
|
-
setRoleRows((rows) => rows.map((row, index) => index === roleIndex ? {
|
|
2965
|
-
...row,
|
|
2966
|
-
selectors: row.selectors.filter((_, sIndex) => sIndex !== selectorIndex)
|
|
2967
|
-
} : row));
|
|
2968
|
-
};
|
|
2969
2441
|
const addRole = () => {
|
|
2970
2442
|
setRoleRows((rows) => [...rows, {
|
|
2971
2443
|
id: "",
|
|
2972
2444
|
persona: "",
|
|
2973
|
-
|
|
2974
|
-
fallback: "inherit-root",
|
|
2445
|
+
chain: "",
|
|
2975
2446
|
collapsed: true,
|
|
2976
2447
|
personaOpen: false
|
|
2977
2448
|
}]);
|
|
@@ -2979,146 +2450,145 @@ window.__ModuleLoader__.load({
|
|
|
2979
2450
|
const removeRole = (index) => {
|
|
2980
2451
|
setRoleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
|
|
2981
2452
|
};
|
|
2982
|
-
const updateRuleRow = (index, patch) => {
|
|
2983
|
-
setRuleRows((rows) => {
|
|
2984
|
-
const next = rows.map((row) => ({ ...row }));
|
|
2985
|
-
next[index] = {
|
|
2986
|
-
...next[index],
|
|
2987
|
-
...patch
|
|
2988
|
-
};
|
|
2989
|
-
return next;
|
|
2990
|
-
});
|
|
2991
|
-
};
|
|
2992
2453
|
const saving = state.status === "saving";
|
|
2993
2454
|
const writable = state.writable;
|
|
2994
2455
|
const advancedVisible = advancedOpen || !writable;
|
|
2995
2456
|
const cooldownMsTip = `${t("cooldownMs.tooltip")} ${t("defaults.prefix")}: ${defaultFallbacksConfig.cooldownMs}`;
|
|
2996
2457
|
const maxSwitchesPerStepTip = `${t("maxSwitchesPerStep.tooltip")} ${t("defaults.prefix")}: ${defaultFallbacksConfig.maxSwitchesPerStep}`;
|
|
2997
|
-
const alwaysModeRetryCapTip = `${t("alwaysModeRetryCap.tooltip")} ${t("defaults.prefix")}: ${defaultFallbacksConfig.alwaysModeRetryCap}`;
|
|
2998
2458
|
const unknownCodes = scalars.triggerCodes.filter((code) => !KNOWN_TRIGGER_CODES.includes(code));
|
|
2999
|
-
const presetsPresent =
|
|
2459
|
+
const presetsPresent = (chainDrafts[activeChain]?.slots ?? []).some((row) => row.kind === "preset");
|
|
3000
2460
|
const glmConfigured = state.configuredProviders.some((entry) => entry.provider === "zai-coding-cn");
|
|
3001
|
-
const
|
|
3002
|
-
const activeSlotIndex =
|
|
3003
|
-
|
|
2461
|
+
const acceptedChain = chainDrafts[activeChain] === void 0 ? void 0 : acceptedChainOf(chainDrafts[activeChain].id);
|
|
2462
|
+
const activeSlotIndex = (() => {
|
|
2463
|
+
if (acceptedChain === void 0) return -1;
|
|
2464
|
+
const winner = resolveSlotState(acceptedChain, /* @__PURE__ */ new Date(), scalars.tz);
|
|
2465
|
+
if (winner.winner === "all-day") return -1;
|
|
2466
|
+
return acceptedChain.timeSlots?.indexOf(winner.winner) ?? -1;
|
|
2467
|
+
})();
|
|
2468
|
+
const invalidSlotRows = collectInvalidSlotRows(chainDrafts[activeChain]?.slots ?? []);
|
|
3004
2469
|
const invalidRoleIds = collectInvalidRoleIds(roleRows);
|
|
3005
|
-
const invalidSlotRows = collectInvalidSlotRows(timeSlotRows);
|
|
3006
2470
|
const seedInfo = /* @__PURE__ */ new Map();
|
|
3007
2471
|
for (const seed of state.seeds) seedInfo.set(seed.id.trim(), {
|
|
3008
2472
|
overridden: seed.overridden,
|
|
3009
2473
|
source: seed.source
|
|
3010
2474
|
});
|
|
3011
|
-
const liveErrors = validateDraft(
|
|
3012
|
-
const
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
else if (state.switchesStatus === "loading") switchesLine = t("loading");
|
|
3016
|
-
else if (latestSwitch === void 0) switchesLine = t("status.switches.empty");
|
|
3017
|
-
else {
|
|
3018
|
-
const reasonKey = SWITCH_REASON_KEYS[latestSwitch.reason];
|
|
3019
|
-
const params = {
|
|
3020
|
-
count: String(state.switches.length),
|
|
3021
|
-
from: `${latestSwitch.from.provider}/${latestSwitch.from.model}`,
|
|
3022
|
-
to: `${latestSwitch.to.provider}/${latestSwitch.to.model}`,
|
|
3023
|
-
role: latestSwitch.role,
|
|
3024
|
-
reason: reasonKey === void 0 ? latestSwitch.reason : t(reasonKey)
|
|
3025
|
-
};
|
|
3026
|
-
switchesLine = latestSwitch.reason === "role-inject" ? t("status.switches.compact.roleInject", params) : t("status.switches.compact", params);
|
|
3027
|
-
}
|
|
2475
|
+
const liveErrors = validateDraft(chains, rolesDraft.list, t);
|
|
2476
|
+
const activeErrors = liveErrors.chains[activeChain] ?? [];
|
|
2477
|
+
const anyChainErrors = liveErrors.chains.some((bucket) => bucket.length > 0);
|
|
2478
|
+
const activeDirty = (perChainDirty[activeChain] ?? false) || structureDirty;
|
|
3028
2479
|
const catalogSeededSections = (0, react.useRef)({
|
|
3029
2480
|
epoch: null,
|
|
3030
|
-
|
|
3031
|
-
|
|
2481
|
+
chains: false,
|
|
2482
|
+
roles: false
|
|
3032
2483
|
});
|
|
3033
2484
|
(0, react.useEffect)(() => {
|
|
3034
2485
|
if (state.catalogStatus !== "ready") return;
|
|
3035
2486
|
const seed = catalogSeededSections.current;
|
|
3036
2487
|
if (seed.epoch !== state.catalogEpoch) {
|
|
3037
2488
|
seed.epoch = state.catalogEpoch;
|
|
3038
|
-
seed.
|
|
3039
|
-
seed.
|
|
2489
|
+
seed.chains = false;
|
|
2490
|
+
seed.roles = false;
|
|
3040
2491
|
}
|
|
3041
2492
|
const catalog = catalogOf(state);
|
|
3042
|
-
if (!
|
|
3043
|
-
seed.
|
|
3044
|
-
|
|
3045
|
-
setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
|
|
3046
|
-
setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
|
|
2493
|
+
if (!chainsDirty && !seed.chains) {
|
|
2494
|
+
seed.chains = true;
|
|
2495
|
+
setChainDrafts(chainsToDrafts(state.config.chains, catalog));
|
|
3047
2496
|
}
|
|
3048
|
-
if (!
|
|
3049
|
-
seed.
|
|
3050
|
-
setRoleRows(rolesToRows(state.config.roles.list
|
|
3051
|
-
setRuleRows(rulesToRows(state.config.roles.rules, catalog));
|
|
2497
|
+
if (!rolesDirty && !seed.roles) {
|
|
2498
|
+
seed.roles = true;
|
|
2499
|
+
setRoleRows(rolesToRows(state.config.roles.list));
|
|
3052
2500
|
}
|
|
3053
2501
|
}, [
|
|
3054
2502
|
state.catalogStatus,
|
|
3055
2503
|
state.catalogEpoch,
|
|
3056
|
-
state.config
|
|
3057
|
-
mainDirty,
|
|
3058
|
-
subDirty
|
|
2504
|
+
state.config
|
|
3059
2505
|
]);
|
|
3060
|
-
const
|
|
3061
|
-
const
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
timeSlots: draft.timeSlots,
|
|
3067
|
-
tz: draft.tz
|
|
3068
|
-
};
|
|
3069
|
-
case "sub": return {
|
|
3070
|
-
...base,
|
|
3071
|
-
roles: draft.roles
|
|
3072
|
-
};
|
|
3073
|
-
case "advanced": return {
|
|
3074
|
-
...base,
|
|
3075
|
-
triggerCodes: draft.triggerCodes,
|
|
3076
|
-
cooldownMs: draft.cooldownMs,
|
|
3077
|
-
revertPolicy: draft.revertPolicy,
|
|
3078
|
-
maxSwitchesPerStep: draft.maxSwitchesPerStep,
|
|
3079
|
-
alwaysModeRetryCap: draft.alwaysModeRetryCap,
|
|
3080
|
-
roleAutoMatch: draft.roleAutoMatch
|
|
3081
|
-
};
|
|
3082
|
-
}
|
|
2506
|
+
const chainsPatch = () => {
|
|
2507
|
+
const activeId = chainDrafts[activeChain]?.id;
|
|
2508
|
+
return chainDrafts.map((draft) => {
|
|
2509
|
+
if (draft.id === activeId) return draftToChain(draft);
|
|
2510
|
+
return acceptedChainOf(draft.id) ?? draftToChain(draft);
|
|
2511
|
+
});
|
|
3083
2512
|
};
|
|
3084
2513
|
const saveSection = (section) => {
|
|
3085
|
-
if (
|
|
3086
|
-
|
|
3087
|
-
|
|
2514
|
+
if (section === "chains") {
|
|
2515
|
+
if (anyChainErrors) return;
|
|
2516
|
+
setLastSaveSection("chains");
|
|
2517
|
+
setLastSaveChain(activeChain);
|
|
2518
|
+
controller.save({
|
|
2519
|
+
...state.config,
|
|
2520
|
+
chains: chainsPatch()
|
|
2521
|
+
});
|
|
2522
|
+
return;
|
|
2523
|
+
}
|
|
2524
|
+
if (section === "sub") {
|
|
2525
|
+
if (liveErrors.roles.length > 0) return;
|
|
2526
|
+
setLastSaveSection("sub");
|
|
2527
|
+
controller.save({
|
|
2528
|
+
...state.config,
|
|
2529
|
+
roles: rolesDraft
|
|
2530
|
+
});
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
setLastSaveSection("advanced");
|
|
2534
|
+
controller.save({
|
|
2535
|
+
...state.config,
|
|
2536
|
+
triggerCodes: scalars.triggerCodes,
|
|
2537
|
+
cooldownMs: scalars.cooldownMs,
|
|
2538
|
+
revertPolicy: scalars.revertPolicy,
|
|
2539
|
+
maxSwitchesPerStep: scalars.maxSwitchesPerStep
|
|
2540
|
+
});
|
|
3088
2541
|
};
|
|
3089
|
-
const
|
|
3090
|
-
const
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
...prev,
|
|
3098
|
-
tz: state.config.tz ?? "Asia/Shanghai"
|
|
3099
|
-
}));
|
|
3100
|
-
break;
|
|
3101
|
-
case "sub":
|
|
3102
|
-
setRoleRows(rolesToRows(state.config.roles.list, catalog));
|
|
3103
|
-
setRuleRows(rulesToRows(state.config.roles.rules, catalog));
|
|
3104
|
-
break;
|
|
3105
|
-
case "advanced": setScalars((prev) => ({
|
|
3106
|
-
...prev,
|
|
3107
|
-
triggerCodes: [...state.config.triggerCodes],
|
|
3108
|
-
cooldownMs: state.config.cooldownMs,
|
|
3109
|
-
revertPolicy: state.config.revertPolicy,
|
|
3110
|
-
maxSwitchesPerStep: state.config.maxSwitchesPerStep,
|
|
3111
|
-
alwaysModeRetryCap: state.config.alwaysModeRetryCap,
|
|
3112
|
-
roleAutoMatch: state.config.roleAutoMatch
|
|
3113
|
-
}));
|
|
2542
|
+
const discardChain = () => {
|
|
2543
|
+
const activeId = chainDrafts[activeChain]?.id;
|
|
2544
|
+
if (activeId === void 0) return;
|
|
2545
|
+
const accepted = acceptedChainOf(activeId);
|
|
2546
|
+
if (accepted === void 0) {
|
|
2547
|
+
setChainDrafts((drafts) => drafts.filter((_, index) => index !== activeChain));
|
|
2548
|
+
setActiveChain((index) => Math.max(0, index - 1));
|
|
2549
|
+
return;
|
|
3114
2550
|
}
|
|
2551
|
+
const reverted = chainsToDrafts([accepted], catalogOf(state))[0];
|
|
2552
|
+
setChainDrafts((drafts) => drafts.map((draft, index) => index === activeChain ? reverted : draft));
|
|
2553
|
+
};
|
|
2554
|
+
const discardSection = (section) => {
|
|
2555
|
+
if (section === "sub") setRoleRows(rolesToRows(state.config.roles.list));
|
|
2556
|
+
else setScalars((prev) => ({
|
|
2557
|
+
...prev,
|
|
2558
|
+
triggerCodes: [...state.config.triggerCodes],
|
|
2559
|
+
cooldownMs: state.config.cooldownMs,
|
|
2560
|
+
revertPolicy: state.config.revertPolicy,
|
|
2561
|
+
maxSwitchesPerStep: state.config.maxSwitchesPerStep
|
|
2562
|
+
}));
|
|
3115
2563
|
};
|
|
3116
2564
|
(0, react.useEffect)(() => {
|
|
3117
|
-
if (state.status === "ready")
|
|
2565
|
+
if (state.status === "ready") {
|
|
2566
|
+
setLastSaveSection(null);
|
|
2567
|
+
setLastSaveChain(null);
|
|
2568
|
+
}
|
|
3118
2569
|
}, [state.status]);
|
|
3119
2570
|
const degradedLatch = (0, react.useRef)(false);
|
|
3120
2571
|
if (state.status === "ready") degradedLatch.current = !state.present;
|
|
3121
2572
|
const degraded = state.status === "ready" ? !state.present : degradedLatch.current;
|
|
2573
|
+
const tabRefs = (0, react.useRef)([]);
|
|
2574
|
+
const onTabKeyDown = (event, index) => {
|
|
2575
|
+
const count = chainDrafts.length;
|
|
2576
|
+
if (count === 0) return;
|
|
2577
|
+
let target = null;
|
|
2578
|
+
if (event.key === "ArrowRight") target = (index + 1) % count;
|
|
2579
|
+
else if (event.key === "ArrowLeft") target = (index - 1 + count) % count;
|
|
2580
|
+
else if (event.key === "Home") target = 0;
|
|
2581
|
+
else if (event.key === "End") target = count - 1;
|
|
2582
|
+
if (target === null) return;
|
|
2583
|
+
event.preventDefault();
|
|
2584
|
+
setActiveChain(target);
|
|
2585
|
+
setMenuOpen(false);
|
|
2586
|
+
tabRefs.current[target].focus();
|
|
2587
|
+
};
|
|
2588
|
+
const activeDraft = chainDrafts[activeChain];
|
|
2589
|
+
const timeSlotRows = activeDraft?.slots ?? [];
|
|
2590
|
+
const tailError = activeErrors.includes(t("validation.allDayRequired")) ? t("validation.allDayRequired") : null;
|
|
2591
|
+
const slotErrors = activeErrors.filter((message) => message !== tailError);
|
|
3122
2592
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3123
2593
|
className: FallbacksCard_module_css_default.root,
|
|
3124
2594
|
children: [
|
|
@@ -3127,6 +2597,10 @@ window.__ModuleLoader__.load({
|
|
|
3127
2597
|
role: "status",
|
|
3128
2598
|
children: t("legacy.banner", { keys: state.legacyKeys.join(", ") })
|
|
3129
2599
|
}),
|
|
2600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2601
|
+
className: FallbacksCard_module_css_default.notice,
|
|
2602
|
+
children: t("virtualOnly.rule")
|
|
2603
|
+
}),
|
|
3130
2604
|
state.status === "error" && state.error !== null && lastSaveSection === null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3131
2605
|
className: FallbacksCard_module_css_default.noticeRow,
|
|
3132
2606
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -3158,471 +2632,695 @@ window.__ModuleLoader__.load({
|
|
|
3158
2632
|
children: [
|
|
3159
2633
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
|
|
3160
2634
|
className: FallbacksCard_module_css_default.sectionHeading,
|
|
3161
|
-
id: "fallbacks-
|
|
3162
|
-
"aria-label": t("
|
|
2635
|
+
id: "fallbacks-virtual-chains",
|
|
2636
|
+
"aria-label": t("virtualChain.label"),
|
|
3163
2637
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3164
2638
|
className: FallbacksCard_module_css_default.sectionHeadingText,
|
|
3165
|
-
children: t("
|
|
3166
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3167
|
-
className: FallbacksCard_module_css_default.
|
|
3168
|
-
children:
|
|
3169
|
-
type: "button",
|
|
3170
|
-
className: FallbacksCard_module_css_default.discard,
|
|
3171
|
-
disabled: !mainDirty || saving,
|
|
3172
|
-
onClick: () => {
|
|
3173
|
-
discardSection("main");
|
|
3174
|
-
},
|
|
3175
|
-
children: t("discard")
|
|
3176
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3177
|
-
type: "button",
|
|
3178
|
-
className: FallbacksCard_module_css_default.save,
|
|
3179
|
-
disabled: !writable || saving || !mainDirty,
|
|
3180
|
-
onClick: () => {
|
|
3181
|
-
saveSection("main");
|
|
3182
|
-
},
|
|
3183
|
-
children: saving ? t("save.saving") : t("save")
|
|
3184
|
-
})]
|
|
2639
|
+
children: t("virtualChain.label")
|
|
2640
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2641
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2642
|
+
children: t("virtualChain.hint")
|
|
3185
2643
|
})]
|
|
3186
2644
|
}),
|
|
3187
|
-
liveErrors.main.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3188
|
-
className: FallbacksCard_module_css_default.error,
|
|
3189
|
-
role: "alert",
|
|
3190
|
-
children: `${t("validation.blocked")}${liveErrors.main.join("; ")}`
|
|
3191
|
-
}),
|
|
3192
|
-
lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3193
|
-
className: FallbacksCard_module_css_default.error,
|
|
3194
|
-
role: "alert",
|
|
3195
|
-
children: t("error.generic", { message: state.error })
|
|
3196
|
-
}),
|
|
3197
2645
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3198
|
-
className: FallbacksCard_module_css_default.
|
|
3199
|
-
|
|
3200
|
-
"aria-labelledby": "fallbacks-time-slots",
|
|
2646
|
+
className: FallbacksCard_module_css_default.tabsWrap,
|
|
2647
|
+
"data-tabs-wrap": "",
|
|
3201
2648
|
children: [
|
|
3202
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
3203
|
-
className: FallbacksCard_module_css_default.
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
2649
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2650
|
+
className: FallbacksCard_module_css_default.tabs,
|
|
2651
|
+
role: "tablist",
|
|
2652
|
+
"aria-label": t("virtualChain.label"),
|
|
2653
|
+
"data-tablist": "",
|
|
2654
|
+
children: [
|
|
2655
|
+
chainDrafts.map((chainDraft, index) => {
|
|
2656
|
+
const selected = index === activeChain;
|
|
2657
|
+
const dirty = perChainDirty[index] ?? false;
|
|
2658
|
+
const invalid = (liveErrors.chains[index] ?? []).length > 0;
|
|
2659
|
+
const name = (chainDraft.label === "" ? chainDraft.id : chainDraft.label) + (dirty ? t("chains.tab.dirty") : "") + (invalid ? t("chains.tab.error") : "");
|
|
2660
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2661
|
+
ref: (node) => {
|
|
2662
|
+
tabRefs.current[index] = node;
|
|
2663
|
+
},
|
|
2664
|
+
type: "button",
|
|
2665
|
+
role: "tab",
|
|
2666
|
+
"data-chain-tab": chainDraft.id,
|
|
2667
|
+
"aria-selected": selected,
|
|
2668
|
+
"aria-controls": `fallbacks-chain-panel-${chainDraft.id}`,
|
|
2669
|
+
tabIndex: selected ? 0 : -1,
|
|
2670
|
+
className: FallbacksCard_module_css_default.tab,
|
|
2671
|
+
"aria-label": name,
|
|
2672
|
+
onClick: () => {
|
|
2673
|
+
setActiveChain(index);
|
|
2674
|
+
setMenuOpen(false);
|
|
2675
|
+
},
|
|
2676
|
+
onKeyDown: (event) => {
|
|
2677
|
+
onTabKeyDown(event, index);
|
|
2678
|
+
},
|
|
2679
|
+
children: [
|
|
2680
|
+
chainDraft.label === "" ? chainDraft.id : chainDraft.label,
|
|
2681
|
+
dirty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2682
|
+
className: FallbacksCard_module_css_default.dirtyPip,
|
|
2683
|
+
title: t("chains.tab.dirty"),
|
|
2684
|
+
"aria-hidden": "true",
|
|
2685
|
+
"data-dirty-pip": ""
|
|
2686
|
+
}),
|
|
2687
|
+
invalid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2688
|
+
className: FallbacksCard_module_css_default.errorMark,
|
|
2689
|
+
"aria-hidden": "true",
|
|
2690
|
+
"data-error-mark": "",
|
|
2691
|
+
children: "!"
|
|
2692
|
+
})
|
|
2693
|
+
]
|
|
2694
|
+
}, chainDraft.id);
|
|
2695
|
+
}),
|
|
2696
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2697
|
+
type: "button",
|
|
2698
|
+
className: FallbacksCard_module_css_default.tabAdd,
|
|
2699
|
+
title: t("chains.tab.add"),
|
|
2700
|
+
"aria-label": t("chains.tab.add"),
|
|
2701
|
+
disabled: !writable,
|
|
2702
|
+
onClick: addChain,
|
|
2703
|
+
"data-tab-add": "",
|
|
2704
|
+
children: "+"
|
|
2705
|
+
}),
|
|
2706
|
+
chainDrafts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2707
|
+
className: FallbacksCard_module_css_default.tabMenu,
|
|
2708
|
+
"data-tab-menu": "",
|
|
2709
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2710
|
+
type: "button",
|
|
2711
|
+
className: FallbacksCard_module_css_default.menuBtn,
|
|
2712
|
+
"aria-haspopup": "true",
|
|
2713
|
+
"aria-expanded": menuOpen,
|
|
2714
|
+
"aria-label": t("chains.tab.menu"),
|
|
2715
|
+
"data-menu-btn": "",
|
|
2716
|
+
disabled: !writable,
|
|
2717
|
+
onClick: () => {
|
|
2718
|
+
setMenuOpen((open) => !open);
|
|
2719
|
+
},
|
|
2720
|
+
children: "⋯"
|
|
2721
|
+
}), menuOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2722
|
+
className: FallbacksCard_module_css_default.menuPanel,
|
|
2723
|
+
role: "menu",
|
|
2724
|
+
"aria-label": t("chains.tab.menu"),
|
|
2725
|
+
"data-menu-panel": "",
|
|
2726
|
+
children: [
|
|
2727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("chains.tab.renameLabel", { id: activeDraft?.id ?? "" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2728
|
+
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.menuInput}`,
|
|
2729
|
+
value: activeDraft?.label ?? "",
|
|
2730
|
+
"data-menu-input": "",
|
|
2731
|
+
disabled: !writable,
|
|
2732
|
+
onChange: (event) => {
|
|
2733
|
+
patchActiveDraft((draft) => ({
|
|
2734
|
+
...draft,
|
|
2735
|
+
label: event.target.value
|
|
2736
|
+
}));
|
|
2737
|
+
}
|
|
2738
|
+
})] }),
|
|
2739
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2740
|
+
className: FallbacksCard_module_css_default.menuActions,
|
|
2741
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2742
|
+
type: "button",
|
|
2743
|
+
className: FallbacksCard_module_css_default.outlineButton,
|
|
2744
|
+
onClick: () => {
|
|
2745
|
+
setMenuOpen(false);
|
|
2746
|
+
},
|
|
2747
|
+
"data-menu-apply": "",
|
|
2748
|
+
children: t("chains.tab.renameDone")
|
|
2749
|
+
})
|
|
2750
|
+
}),
|
|
2751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2752
|
+
type: "button",
|
|
2753
|
+
className: FallbacksCard_module_css_default.menuItemDanger,
|
|
2754
|
+
disabled: chainDrafts.length <= 1,
|
|
2755
|
+
onClick: deleteActiveChain,
|
|
2756
|
+
"data-menu-delete": "",
|
|
2757
|
+
children: chainDrafts.length <= 1 ? t("chains.tab.deleteDisabled") : t("chains.tab.delete")
|
|
2758
|
+
})
|
|
2759
|
+
]
|
|
2760
|
+
})]
|
|
2761
|
+
})
|
|
2762
|
+
]
|
|
3211
2763
|
}),
|
|
3212
2764
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3213
|
-
className: FallbacksCard_module_css_default.
|
|
3214
|
-
|
|
2765
|
+
className: FallbacksCard_module_css_default.fadeL,
|
|
2766
|
+
"aria-hidden": "true"
|
|
3215
2767
|
}),
|
|
3216
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3217
|
-
className: FallbacksCard_module_css_default.
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
2768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2769
|
+
className: FallbacksCard_module_css_default.fadeR,
|
|
2770
|
+
"aria-hidden": "true"
|
|
2771
|
+
})
|
|
2772
|
+
]
|
|
2773
|
+
}),
|
|
2774
|
+
chainDrafts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2775
|
+
className: FallbacksCard_module_css_default.emptyState,
|
|
2776
|
+
"data-empty-state": "",
|
|
2777
|
+
children: [
|
|
2778
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("chains.empty.title") }),
|
|
2779
|
+
t("chains.empty.body"),
|
|
2780
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
|
|
2781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
|
|
2782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2783
|
+
type: "button",
|
|
2784
|
+
className: FallbacksCard_module_css_default.outlineButton,
|
|
2785
|
+
onClick: addChain,
|
|
2786
|
+
"data-empty-create": "",
|
|
2787
|
+
children: t("chains.empty.create")
|
|
2788
|
+
})
|
|
2789
|
+
]
|
|
2790
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2791
|
+
className: FallbacksCard_module_css_default.chainBody,
|
|
2792
|
+
role: "tabpanel",
|
|
2793
|
+
id: `fallbacks-chain-panel-${activeDraft.id}`,
|
|
2794
|
+
"aria-labelledby": `fallbacks-chain-tab-${activeDraft.id}`,
|
|
2795
|
+
"data-chain-panel": "",
|
|
2796
|
+
children: [
|
|
2797
|
+
slotErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2798
|
+
className: FallbacksCard_module_css_default.error,
|
|
2799
|
+
role: "alert",
|
|
2800
|
+
children: `${t("validation.blocked")}${slotErrors.join("; ")}`
|
|
2801
|
+
}),
|
|
2802
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2803
|
+
className: FallbacksCard_module_css_default.field,
|
|
2804
|
+
role: "group",
|
|
2805
|
+
"aria-labelledby": `fallbacks-chain-name-${activeDraft.id}`,
|
|
2806
|
+
children: [
|
|
2807
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2808
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2809
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2810
|
+
id: `fallbacks-chain-name-${activeDraft.id}`,
|
|
2811
|
+
children: t("chains.name.label")
|
|
2812
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2813
|
+
label: t("chains.name.tooltip"),
|
|
2814
|
+
disabled: !writable
|
|
2815
|
+
})]
|
|
2816
|
+
}),
|
|
2817
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2818
|
+
className: FallbacksCard_module_css_default.input,
|
|
2819
|
+
value: activeDraft.label,
|
|
2820
|
+
placeholder: activeDraft.id,
|
|
2821
|
+
"aria-label": t("chains.name.label"),
|
|
2822
|
+
"data-chain-name": "",
|
|
2823
|
+
disabled: !writable,
|
|
2824
|
+
onChange: (event) => {
|
|
2825
|
+
patchActiveDraft((draft) => ({
|
|
2826
|
+
...draft,
|
|
2827
|
+
label: event.target.value
|
|
2828
|
+
}));
|
|
2829
|
+
}
|
|
2830
|
+
}),
|
|
2831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2832
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2833
|
+
children: t("chains.name.hint", { id: activeDraft.id })
|
|
2834
|
+
})
|
|
2835
|
+
]
|
|
2836
|
+
}),
|
|
2837
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2838
|
+
className: FallbacksCard_module_css_default.field,
|
|
2839
|
+
role: "group",
|
|
2840
|
+
"aria-labelledby": `fallbacks-time-slots-${activeDraft.id}`,
|
|
2841
|
+
children: [
|
|
2842
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2843
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2844
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2845
|
+
id: `fallbacks-time-slots-${activeDraft.id}`,
|
|
2846
|
+
children: t("timeSlots.label")
|
|
2847
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2848
|
+
label: t("timeSlots.tooltip"),
|
|
2849
|
+
disabled: !writable
|
|
2850
|
+
})]
|
|
2851
|
+
}),
|
|
2852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2853
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2854
|
+
children: t("timeSlots.hint")
|
|
2855
|
+
}),
|
|
2856
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2857
|
+
className: FallbacksCard_module_css_default.list,
|
|
2858
|
+
children: timeSlotRows.map((row, index) => {
|
|
2859
|
+
const invalidWindow = invalidSlotRows.has(index);
|
|
2860
|
+
const chainEmpty = row.selectors.every((selector) => selectorRowToRaw(selector) === "");
|
|
2861
|
+
const firstModel = row.selectors.map(selectorRowToRaw).find((entry) => entry !== "");
|
|
2862
|
+
const slotExpanded = !row.collapsed || !writable;
|
|
2863
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2864
|
+
className: `${FallbacksCard_module_css_default.editorCard} ${draggedSlotIndex === index ? FallbacksCard_module_css_default.slotCardDragging : ""} ${overSlotIndex === index && draggedSlotIndex !== null && draggedSlotIndex !== index ? FallbacksCard_module_css_default.slotCardOver : ""}`,
|
|
2865
|
+
onDragOver: (event) => {
|
|
2866
|
+
if (draggedSlotIndex === null) return;
|
|
2867
|
+
event.preventDefault();
|
|
2868
|
+
if (overSlotIndex !== index) setOverSlotIndex(index);
|
|
3281
2869
|
},
|
|
3282
|
-
|
|
2870
|
+
onDrop: (event) => {
|
|
2871
|
+
event.preventDefault();
|
|
2872
|
+
const from = draggedSlotIndex;
|
|
3283
2873
|
setDraggedSlotIndex(null);
|
|
3284
2874
|
setOverSlotIndex(null);
|
|
2875
|
+
if (from !== null && from !== index) reorderTimeSlotRow(from, index);
|
|
3285
2876
|
},
|
|
3286
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3294
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3295
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3296
|
-
children: t("timeSlots.preset.name")
|
|
3297
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3298
|
-
className: FallbacksCard_module_css_default.slotPresetName,
|
|
3299
|
-
children: t(`timeSlots.preset.${row.preset}.label`)
|
|
3300
|
-
})]
|
|
3301
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3302
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3303
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3304
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3305
|
-
children: t("timeSlots.preset.windowLabel")
|
|
3306
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3307
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3308
|
-
children: t(`timeSlots.preset.${row.preset}.window`)
|
|
3309
|
-
})]
|
|
3310
|
-
})]
|
|
3311
|
-
}),
|
|
3312
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3313
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3314
|
-
children: t("timeSlots.preset.chainsOnly")
|
|
3315
|
-
}),
|
|
3316
|
-
(row.preset === "glm-peak" || row.preset === "glm-valley") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3317
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3318
|
-
children: t("timeSlots.preset.glm.note")
|
|
3319
|
-
})
|
|
3320
|
-
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3321
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3322
|
-
className: FallbacksCard_module_css_default.field,
|
|
3323
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3324
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3325
|
-
children: t("timeSlots.name")
|
|
3326
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3327
|
-
className: FallbacksCard_module_css_default.input,
|
|
3328
|
-
value: row.name,
|
|
3329
|
-
placeholder: t("timeSlots.name"),
|
|
3330
|
-
"aria-label": t("timeSlots.name"),
|
|
2877
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2878
|
+
className: FallbacksCard_module_css_default.collapseRow,
|
|
2879
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2880
|
+
type: "button",
|
|
2881
|
+
className: FallbacksCard_module_css_default.collapseToggle,
|
|
2882
|
+
"aria-expanded": slotExpanded,
|
|
2883
|
+
"aria-label": t(slotExpanded ? "timeSlots.collapse" : "timeSlots.expand"),
|
|
3331
2884
|
disabled: !writable,
|
|
3332
|
-
|
|
3333
|
-
updateTimeSlotRow(index, {
|
|
3334
|
-
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
"aria-label": t("timeSlots.end"),
|
|
3375
|
-
disabled: !writable,
|
|
3376
|
-
onChange: (event) => {
|
|
3377
|
-
updateTimeSlotRow(index, { end: event.target.value });
|
|
3378
|
-
}
|
|
3379
|
-
})]
|
|
2885
|
+
onClick: () => {
|
|
2886
|
+
updateTimeSlotRow(index, { collapsed: !row.collapsed });
|
|
2887
|
+
},
|
|
2888
|
+
children: [
|
|
2889
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, { className: slotExpanded ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron }),
|
|
2890
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2891
|
+
className: FallbacksCard_module_css_default.collapseTitle,
|
|
2892
|
+
children: row.kind === "preset" ? t(`timeSlots.preset.${row.preset}.label`) : row.name !== "" ? row.name : `custom ${row.start}-${row.end}`
|
|
2893
|
+
}),
|
|
2894
|
+
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", {
|
|
2895
|
+
className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagHighCost}`,
|
|
2896
|
+
children: t("timeSlots.preset.highCost")
|
|
2897
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2898
|
+
className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagMultiplier}`,
|
|
2899
|
+
children: t("timeSlots.preset.multiplier", { n: row.preset === "liang-peak" ? "2" : "3" })
|
|
2900
|
+
})] }),
|
|
2901
|
+
activeSlotIndex === index && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2902
|
+
className: `${FallbacksCard_module_css_default.slotTag} ${FallbacksCard_module_css_default.slotTagActive}`,
|
|
2903
|
+
children: t("timeSlots.active")
|
|
2904
|
+
}),
|
|
2905
|
+
firstModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2906
|
+
className: FallbacksCard_module_css_default.collapseMeta,
|
|
2907
|
+
children: firstModel
|
|
2908
|
+
})
|
|
2909
|
+
]
|
|
2910
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2911
|
+
type: "button",
|
|
2912
|
+
className: FallbacksCard_module_css_default.dragHandle,
|
|
2913
|
+
draggable: writable,
|
|
2914
|
+
"data-tip": t("timeSlots.drag"),
|
|
2915
|
+
"aria-label": t("timeSlots.drag"),
|
|
2916
|
+
disabled: !writable,
|
|
2917
|
+
onDragStart: () => {
|
|
2918
|
+
if (!writable) return;
|
|
2919
|
+
setDraggedSlotIndex(index);
|
|
2920
|
+
setOverSlotIndex(index);
|
|
2921
|
+
},
|
|
2922
|
+
onDragEnd: () => {
|
|
2923
|
+
setDraggedSlotIndex(null);
|
|
2924
|
+
setOverSlotIndex(null);
|
|
2925
|
+
},
|
|
2926
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GripIcon, {})
|
|
3380
2927
|
})]
|
|
3381
|
-
}),
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
2928
|
+
}), slotExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2929
|
+
row.kind === "preset" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2930
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2931
|
+
className: FallbacksCard_module_css_default.ruleGrid,
|
|
2932
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2933
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
2934
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2935
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2936
|
+
children: t("timeSlots.preset.name")
|
|
2937
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2938
|
+
className: FallbacksCard_module_css_default.slotPresetName,
|
|
2939
|
+
children: t(`timeSlots.preset.${row.preset}.label`)
|
|
2940
|
+
})]
|
|
2941
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2942
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
2943
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2944
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2945
|
+
children: t("timeSlots.preset.windowLabel")
|
|
2946
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2947
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2948
|
+
children: t(`timeSlots.preset.${row.preset}.window`)
|
|
2949
|
+
})]
|
|
2950
|
+
})]
|
|
2951
|
+
}),
|
|
3385
2952
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3386
|
-
className: FallbacksCard_module_css_default.
|
|
3387
|
-
children: t("timeSlots.
|
|
2953
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2954
|
+
children: t("timeSlots.preset.chainsOnly")
|
|
2955
|
+
}),
|
|
2956
|
+
(row.preset === "glm-peak" || row.preset === "glm-valley") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2957
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2958
|
+
children: t("timeSlots.preset.glm.note")
|
|
2959
|
+
})
|
|
2960
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2961
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2962
|
+
className: FallbacksCard_module_css_default.field,
|
|
2963
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2964
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2965
|
+
children: t("timeSlots.name")
|
|
2966
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2967
|
+
className: FallbacksCard_module_css_default.input,
|
|
2968
|
+
value: row.name,
|
|
2969
|
+
placeholder: t("timeSlots.name"),
|
|
2970
|
+
"aria-label": t("timeSlots.name"),
|
|
2971
|
+
disabled: !writable,
|
|
2972
|
+
onChange: (event) => {
|
|
2973
|
+
updateTimeSlotRow(index, { name: event.target.value });
|
|
2974
|
+
}
|
|
2975
|
+
})]
|
|
2976
|
+
}),
|
|
2977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2978
|
+
className: FallbacksCard_module_css_default.field,
|
|
2979
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2980
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2981
|
+
children: t("timeSlots.tz.label")
|
|
2982
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2983
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2984
|
+
"aria-label": t("timeSlots.tz.label"),
|
|
2985
|
+
children: tzDisplayLabel(presetsPresent ? "Asia/Shanghai" : hostTimeZone())
|
|
2986
|
+
})]
|
|
3388
2987
|
}),
|
|
3389
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3390
|
-
className: FallbacksCard_module_css_default.
|
|
3391
|
-
children:
|
|
3392
|
-
className: FallbacksCard_module_css_default.
|
|
3393
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3394
|
-
|
|
3395
|
-
|
|
2988
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2989
|
+
className: FallbacksCard_module_css_default.ruleGrid,
|
|
2990
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2991
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
2992
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2993
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2994
|
+
children: t("timeSlots.start")
|
|
2995
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2996
|
+
className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
|
|
2997
|
+
value: row.start,
|
|
2998
|
+
placeholder: "09:00",
|
|
2999
|
+
"aria-label": t("timeSlots.start"),
|
|
3000
|
+
disabled: !writable,
|
|
3001
|
+
onChange: (event) => {
|
|
3002
|
+
updateTimeSlotRow(index, { start: event.target.value });
|
|
3003
|
+
}
|
|
3004
|
+
})]
|
|
3005
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3006
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
3007
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3008
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3009
|
+
children: t("timeSlots.end")
|
|
3010
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3011
|
+
className: `${FallbacksCard_module_css_default.input} ${invalidWindow ? FallbacksCard_module_css_default.inputInvalid : ""}`,
|
|
3012
|
+
value: row.end,
|
|
3013
|
+
placeholder: "18:00",
|
|
3014
|
+
"aria-label": t("timeSlots.end"),
|
|
3396
3015
|
disabled: !writable,
|
|
3397
|
-
onChange: () => {
|
|
3398
|
-
updateTimeSlotRow(index, {
|
|
3016
|
+
onChange: (event) => {
|
|
3017
|
+
updateTimeSlotRow(index, { end: event.target.value });
|
|
3399
3018
|
}
|
|
3400
|
-
})
|
|
3401
|
-
}
|
|
3019
|
+
})]
|
|
3020
|
+
})]
|
|
3402
3021
|
}),
|
|
3403
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3023
|
+
className: FallbacksCard_module_css_default.field,
|
|
3024
|
+
children: [
|
|
3025
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3026
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3027
|
+
children: t("timeSlots.days")
|
|
3028
|
+
}),
|
|
3029
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3030
|
+
className: FallbacksCard_module_css_default.dayRow,
|
|
3031
|
+
children: SLOT_WEEKDAYS.map((day, dayIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3032
|
+
className: FallbacksCard_module_css_default.dayCell,
|
|
3033
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3034
|
+
type: "checkbox",
|
|
3035
|
+
checked: row.days.includes(dayIndex),
|
|
3036
|
+
disabled: !writable,
|
|
3037
|
+
onChange: () => {
|
|
3038
|
+
updateTimeSlotRow(index, { days: row.days.includes(dayIndex) ? row.days.filter((existing) => existing !== dayIndex) : [...row.days, dayIndex] });
|
|
3039
|
+
}
|
|
3040
|
+
}), t(`timeSlots.day.${day}`)]
|
|
3041
|
+
}, day))
|
|
3042
|
+
}),
|
|
3043
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3044
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3045
|
+
children: t("timeSlots.days.hint")
|
|
3046
|
+
})
|
|
3047
|
+
]
|
|
3048
|
+
}),
|
|
3049
|
+
(row.start !== "" || row.end !== "") && !(HHMM_RE.test(row.start) && HHMM_RE.test(row.end)) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3404
3050
|
className: FallbacksCard_module_css_default.hint,
|
|
3405
|
-
children: t("
|
|
3051
|
+
children: t("validation.slotWindow")
|
|
3406
3052
|
})
|
|
3407
|
-
]
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3053
|
+
] }),
|
|
3054
|
+
chainEmpty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3055
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3056
|
+
children: t("validation.slotChainRequired")
|
|
3057
|
+
}),
|
|
3058
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3059
|
+
className: FallbacksCard_module_css_default.chainBlock,
|
|
3060
|
+
"data-chain-block": "",
|
|
3061
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3062
|
+
selectors: row.selectors,
|
|
3063
|
+
catalog: catalogOf(state),
|
|
3064
|
+
configuredProviders: state.configuredProviders,
|
|
3065
|
+
disabled: !writable,
|
|
3066
|
+
t,
|
|
3067
|
+
onChange: (selectorIndex, patch) => {
|
|
3068
|
+
updateTimeSlotSelector(index, selectorIndex, patch);
|
|
3069
|
+
},
|
|
3070
|
+
onRemove: (selectorIndex) => {
|
|
3071
|
+
removeTimeSlotSelector(index, selectorIndex);
|
|
3072
|
+
},
|
|
3073
|
+
onMove: (from, to) => {
|
|
3074
|
+
moveSlotSelector(index, from, to);
|
|
3075
|
+
}
|
|
3076
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3077
|
+
type: "button",
|
|
3078
|
+
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3079
|
+
disabled: !writable,
|
|
3080
|
+
onClick: () => {
|
|
3081
|
+
addTimeSlotSelector(index);
|
|
3082
|
+
},
|
|
3083
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.selector.add")]
|
|
3084
|
+
})]
|
|
3085
|
+
}),
|
|
3086
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3087
|
+
className: FallbacksCard_module_css_default.cardFoot,
|
|
3088
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3089
|
+
className: FallbacksCard_module_css_default.rowActions,
|
|
3090
|
+
children: [
|
|
3091
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3092
|
+
type: "button",
|
|
3093
|
+
className: FallbacksCard_module_css_default.iconButton,
|
|
3094
|
+
"data-tip": t("timeSlots.moveUp"),
|
|
3095
|
+
"aria-label": t("timeSlots.moveUp"),
|
|
3096
|
+
disabled: !writable || index === 0,
|
|
3097
|
+
onClick: () => {
|
|
3098
|
+
moveTimeSlotRow(index, -1);
|
|
3099
|
+
},
|
|
3100
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, { className: FallbacksCard_module_css_default.chevronFlip })
|
|
3101
|
+
}),
|
|
3102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3103
|
+
type: "button",
|
|
3104
|
+
className: FallbacksCard_module_css_default.iconButton,
|
|
3105
|
+
"data-tip": t("timeSlots.moveDown"),
|
|
3106
|
+
"aria-label": t("timeSlots.moveDown"),
|
|
3107
|
+
disabled: !writable || index === timeSlotRows.length - 1,
|
|
3108
|
+
onClick: () => {
|
|
3109
|
+
moveTimeSlotRow(index, 1);
|
|
3110
|
+
},
|
|
3111
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, {})
|
|
3112
|
+
}),
|
|
3113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3114
|
+
type: "button",
|
|
3115
|
+
className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
|
|
3116
|
+
"data-tip": t("timeSlots.remove"),
|
|
3117
|
+
"aria-label": t("timeSlots.remove"),
|
|
3118
|
+
onClick: () => {
|
|
3119
|
+
removeTimeSlotRow(index);
|
|
3120
|
+
},
|
|
3121
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
|
|
3122
|
+
})
|
|
3123
|
+
]
|
|
3124
|
+
})
|
|
3125
|
+
})
|
|
3126
|
+
] })]
|
|
3127
|
+
}, index);
|
|
3128
|
+
})
|
|
3129
|
+
}),
|
|
3130
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3131
|
+
className: FallbacksCard_module_css_default.slotAddRow,
|
|
3132
|
+
children: [
|
|
3133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3134
|
+
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
3135
|
+
value: presetToAdd,
|
|
3136
|
+
"aria-label": t("timeSlots.presetPlaceholder"),
|
|
3137
|
+
disabled: !writable,
|
|
3138
|
+
onChange: (event) => {
|
|
3139
|
+
setPresetToAdd(event.target.value);
|
|
3140
|
+
},
|
|
3141
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3142
|
+
value: "",
|
|
3143
|
+
children: t("timeSlots.presetPlaceholder")
|
|
3144
|
+
}), SLOT_PRESET_IDS.filter((id) => !timeSlotRows.some((row) => row.kind === "preset" && row.preset === id)).map((id) => {
|
|
3145
|
+
const glmUnconfigured = !glmConfigured && (id === "glm-peak" || id === "glm-valley");
|
|
3146
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3147
|
+
value: id,
|
|
3148
|
+
disabled: glmUnconfigured,
|
|
3149
|
+
children: [t(`timeSlots.preset.${id}.label`), glmUnconfigured ? t("timeSlots.preset.glm.unconfigured") : null]
|
|
3150
|
+
}, id);
|
|
3151
|
+
})]
|
|
3152
|
+
}),
|
|
3153
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3154
|
+
type: "button",
|
|
3155
|
+
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3156
|
+
disabled: !writable || presetToAdd === "",
|
|
3157
|
+
onClick: addPresetSlotRow,
|
|
3158
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.addPreset")]
|
|
3417
3159
|
}),
|
|
3418
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
3160
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3161
|
+
type: "button",
|
|
3162
|
+
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3163
|
+
disabled: !writable,
|
|
3164
|
+
onClick: addCustomSlotRow,
|
|
3165
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.addCustom")]
|
|
3166
|
+
})
|
|
3167
|
+
]
|
|
3168
|
+
})
|
|
3169
|
+
]
|
|
3170
|
+
}),
|
|
3171
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3172
|
+
className: FallbacksCard_module_css_default.field,
|
|
3173
|
+
role: "group",
|
|
3174
|
+
"aria-labelledby": `fallbacks-root-chain-${activeDraft.id}`,
|
|
3175
|
+
children: [
|
|
3176
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3177
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3178
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3179
|
+
id: `fallbacks-root-chain-${activeDraft.id}`,
|
|
3180
|
+
children: t("rootChain.label")
|
|
3181
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3182
|
+
label: t("rootChain.tooltip"),
|
|
3183
|
+
disabled: !writable
|
|
3184
|
+
})]
|
|
3185
|
+
}),
|
|
3186
|
+
state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3187
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3188
|
+
children: t("catalog.error", { message: state.catalogError })
|
|
3189
|
+
}),
|
|
3190
|
+
state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3191
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3192
|
+
children: t("catalog.partial", { message: state.catalogError })
|
|
3193
|
+
}),
|
|
3194
|
+
state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3195
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3196
|
+
children: t("catalog.empty")
|
|
3197
|
+
}),
|
|
3198
|
+
slotErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3199
|
+
className: FallbacksCard_module_css_default.error,
|
|
3200
|
+
role: "alert",
|
|
3201
|
+
"data-chain-error": "",
|
|
3202
|
+
children: `${t("validation.blocked")}${slotErrors.join("; ")}`
|
|
3203
|
+
}),
|
|
3204
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3205
|
+
className: FallbacksCard_module_css_default.list,
|
|
3206
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3207
|
+
className: FallbacksCard_module_css_default.editorCard,
|
|
3208
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3419
3209
|
className: FallbacksCard_module_css_default.chainBlock,
|
|
3420
3210
|
"data-chain-block": "",
|
|
3421
3211
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3422
|
-
selectors:
|
|
3212
|
+
selectors: activeDraft.selectors,
|
|
3423
3213
|
catalog: catalogOf(state),
|
|
3424
3214
|
configuredProviders: state.configuredProviders,
|
|
3425
3215
|
disabled: !writable,
|
|
3426
3216
|
t,
|
|
3427
3217
|
onChange: (selectorIndex, patch) => {
|
|
3428
|
-
|
|
3218
|
+
updateChainSelector(selectorIndex, patch);
|
|
3429
3219
|
},
|
|
3430
3220
|
onRemove: (selectorIndex) => {
|
|
3431
|
-
|
|
3221
|
+
removeChainSelector(selectorIndex);
|
|
3432
3222
|
},
|
|
3433
|
-
onMove:
|
|
3434
|
-
reorderTimeSlotSelector(index, from, to);
|
|
3435
|
-
}
|
|
3223
|
+
onMove: reorderChainSelector
|
|
3436
3224
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3437
3225
|
type: "button",
|
|
3438
3226
|
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3439
3227
|
disabled: !writable,
|
|
3440
|
-
onClick:
|
|
3441
|
-
addTimeSlotSelector(index);
|
|
3442
|
-
},
|
|
3228
|
+
onClick: addChainSelector,
|
|
3443
3229
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.selector.add")]
|
|
3444
3230
|
})]
|
|
3445
|
-
}),
|
|
3446
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3447
|
-
className: FallbacksCard_module_css_default.cardFoot,
|
|
3448
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3449
|
-
className: FallbacksCard_module_css_default.rowActions,
|
|
3450
|
-
children: [
|
|
3451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3452
|
-
type: "button",
|
|
3453
|
-
className: FallbacksCard_module_css_default.iconButton,
|
|
3454
|
-
"data-tip": t("timeSlots.moveUp"),
|
|
3455
|
-
"aria-label": t("timeSlots.moveUp"),
|
|
3456
|
-
disabled: !writable || index === 0,
|
|
3457
|
-
onClick: () => {
|
|
3458
|
-
moveTimeSlotRow(index, -1);
|
|
3459
|
-
},
|
|
3460
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, { className: FallbacksCard_module_css_default.chevronFlip })
|
|
3461
|
-
}),
|
|
3462
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3463
|
-
type: "button",
|
|
3464
|
-
className: FallbacksCard_module_css_default.iconButton,
|
|
3465
|
-
"data-tip": t("timeSlots.moveDown"),
|
|
3466
|
-
"aria-label": t("timeSlots.moveDown"),
|
|
3467
|
-
disabled: !writable || index === timeSlotRows.length - 1,
|
|
3468
|
-
onClick: () => {
|
|
3469
|
-
moveTimeSlotRow(index, 1);
|
|
3470
|
-
},
|
|
3471
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, {})
|
|
3472
|
-
}),
|
|
3473
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3474
|
-
type: "button",
|
|
3475
|
-
className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
|
|
3476
|
-
"data-tip": t("timeSlots.remove"),
|
|
3477
|
-
"aria-label": t("timeSlots.remove"),
|
|
3478
|
-
onClick: () => {
|
|
3479
|
-
removeTimeSlotRow(index);
|
|
3480
|
-
},
|
|
3481
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
|
|
3482
|
-
})
|
|
3483
|
-
]
|
|
3484
|
-
})
|
|
3485
3231
|
})
|
|
3486
|
-
|
|
3487
|
-
}
|
|
3488
|
-
|
|
3232
|
+
})
|
|
3233
|
+
})
|
|
3234
|
+
]
|
|
3489
3235
|
}),
|
|
3490
3236
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3491
|
-
className: FallbacksCard_module_css_default.
|
|
3237
|
+
className: FallbacksCard_module_css_default.field,
|
|
3238
|
+
role: "group",
|
|
3239
|
+
"aria-labelledby": `fallbacks-default-model-${activeDraft.id}`,
|
|
3492
3240
|
children: [
|
|
3493
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3494
|
-
className:
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
setPresetToAdd(event.target.value);
|
|
3500
|
-
},
|
|
3501
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3502
|
-
value: "",
|
|
3503
|
-
children: t("timeSlots.presetPlaceholder")
|
|
3504
|
-
}), SLOT_PRESET_IDS.filter((id) => !timeSlotRows.some((row) => row.kind === "preset" && row.preset === id)).map((id) => {
|
|
3505
|
-
const glmUnconfigured = !glmConfigured && (id === "glm-peak" || id === "glm-valley");
|
|
3506
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
3507
|
-
value: id,
|
|
3508
|
-
disabled: glmUnconfigured,
|
|
3509
|
-
children: [t(`timeSlots.preset.${id}.label`), glmUnconfigured ? t("timeSlots.preset.glm.unconfigured") : null]
|
|
3510
|
-
}, id);
|
|
3511
|
-
})]
|
|
3241
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3242
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3243
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3244
|
+
id: `fallbacks-default-model-${activeDraft.id}`,
|
|
3245
|
+
children: t("defaultModel.label")
|
|
3246
|
+
})
|
|
3512
3247
|
}),
|
|
3513
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
disabled: !writable || presetToAdd === "",
|
|
3517
|
-
onClick: addPresetSlotRow,
|
|
3518
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.addPreset")]
|
|
3248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3249
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3250
|
+
children: t("allDay.hint")
|
|
3519
3251
|
}),
|
|
3520
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3252
|
+
tailError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3253
|
+
className: FallbacksCard_module_css_default.error,
|
|
3254
|
+
role: "alert",
|
|
3255
|
+
"data-tail-error": "",
|
|
3256
|
+
children: `${t("validation.blocked")}${tailError}`
|
|
3257
|
+
}),
|
|
3258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3259
|
+
className: FallbacksCard_module_css_default.list,
|
|
3260
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3261
|
+
className: FallbacksCard_module_css_default.editorCard,
|
|
3262
|
+
children: OFFICIAL_ALL_DAY_IDS.map((id) => {
|
|
3263
|
+
const chain = chains[activeChain];
|
|
3264
|
+
const tail = chain !== void 0 && chain.entries.length >= 1 ? chain.entries[chain.entries.length - 1] : void 0;
|
|
3265
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3266
|
+
className: FallbacksCard_module_css_default.optionRow,
|
|
3267
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3268
|
+
type: "radio",
|
|
3269
|
+
name: `fallbacks-chain-tail-${activeDraft.id}`,
|
|
3270
|
+
checked: tail === id,
|
|
3271
|
+
disabled: !writable,
|
|
3272
|
+
onChange: () => {
|
|
3273
|
+
patchActiveDraft((draft) => {
|
|
3274
|
+
const selector = entryToSelectorRow(id, catalogOf(state));
|
|
3275
|
+
if (draft.selectors.length === 0) return {
|
|
3276
|
+
...draft,
|
|
3277
|
+
selectors: [selector]
|
|
3278
|
+
};
|
|
3279
|
+
const selectors = draft.selectors.map((row) => ({ ...row }));
|
|
3280
|
+
selectors[selectors.length - 1] = selector;
|
|
3281
|
+
return {
|
|
3282
|
+
...draft,
|
|
3283
|
+
selectors
|
|
3284
|
+
};
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
3287
|
+
}), t(id === OFFICIAL_ALL_DAY_IDS[0] ? "allDay.flash" : "allDay.pro")]
|
|
3288
|
+
}, id);
|
|
3289
|
+
})
|
|
3290
|
+
})
|
|
3526
3291
|
})
|
|
3527
3292
|
]
|
|
3528
|
-
})
|
|
3529
|
-
]
|
|
3530
|
-
}),
|
|
3531
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3532
|
-
className: FallbacksCard_module_css_default.field,
|
|
3533
|
-
role: "group",
|
|
3534
|
-
"aria-labelledby": "fallbacks-root-chain",
|
|
3535
|
-
children: [
|
|
3536
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3537
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3538
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3539
|
-
id: "fallbacks-root-chain",
|
|
3540
|
-
children: t("rootChain.label")
|
|
3541
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3542
|
-
label: t("rootChain.tooltip"),
|
|
3543
|
-
disabled: !writable
|
|
3544
|
-
})]
|
|
3545
|
-
}),
|
|
3546
|
-
state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3547
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3548
|
-
children: t("catalog.error", { message: state.catalogError })
|
|
3549
|
-
}),
|
|
3550
|
-
state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3551
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3552
|
-
children: t("catalog.partial", { message: state.catalogError })
|
|
3553
3293
|
}),
|
|
3554
|
-
|
|
3555
|
-
className: FallbacksCard_module_css_default.
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
})]
|
|
3585
|
-
})
|
|
3586
|
-
})
|
|
3587
|
-
})
|
|
3588
|
-
]
|
|
3589
|
-
}),
|
|
3590
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3591
|
-
className: FallbacksCard_module_css_default.field,
|
|
3592
|
-
role: "group",
|
|
3593
|
-
"aria-labelledby": "fallbacks-default-model",
|
|
3594
|
-
children: [
|
|
3595
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3596
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3597
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3598
|
-
id: "fallbacks-default-model",
|
|
3599
|
-
children: t("defaultModel.label")
|
|
3600
|
-
})
|
|
3601
|
-
}),
|
|
3602
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3603
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3604
|
-
children: t("allDay.hint")
|
|
3605
|
-
}),
|
|
3606
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3607
|
-
className: FallbacksCard_module_css_default.list,
|
|
3608
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3609
|
-
className: FallbacksCard_module_css_default.editorCard,
|
|
3610
|
-
children: [OFFICIAL_ALL_DAY_IDS.map((id, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3611
|
-
className: FallbacksCard_module_css_default.optionRow,
|
|
3612
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3613
|
-
type: "radio",
|
|
3614
|
-
name: "fallbacks-all-day",
|
|
3615
|
-
checked: allDayModel === id,
|
|
3616
|
-
disabled: !writable,
|
|
3617
|
-
onChange: () => {
|
|
3618
|
-
setAllDayModel(id);
|
|
3619
|
-
}
|
|
3620
|
-
}), t(ALL_DAY_LABEL_KEYS[index])]
|
|
3621
|
-
}, id)), allDayModel === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3622
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3623
|
-
children: t("allDay.nonconforming")
|
|
3624
|
-
})]
|
|
3625
|
-
})
|
|
3294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3295
|
+
className: FallbacksCard_module_css_default.chainFoot,
|
|
3296
|
+
"data-chain-foot": "",
|
|
3297
|
+
children: [
|
|
3298
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3299
|
+
type: "button",
|
|
3300
|
+
className: FallbacksCard_module_css_default.discard,
|
|
3301
|
+
disabled: !activeDirty || saving,
|
|
3302
|
+
onClick: discardChain,
|
|
3303
|
+
children: t("discard")
|
|
3304
|
+
}),
|
|
3305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3306
|
+
type: "button",
|
|
3307
|
+
className: FallbacksCard_module_css_default.save,
|
|
3308
|
+
disabled: !writable || saving || !activeDirty || anyChainErrors,
|
|
3309
|
+
onClick: () => {
|
|
3310
|
+
saveSection("chains");
|
|
3311
|
+
},
|
|
3312
|
+
children: saving ? t("save.saving") : t("save")
|
|
3313
|
+
}),
|
|
3314
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3315
|
+
className: FallbacksCard_module_css_default.chainFootHint,
|
|
3316
|
+
children: t("chains.chainFoot.hint")
|
|
3317
|
+
}),
|
|
3318
|
+
lastSaveSection === "chains" && lastSaveChain === activeChain && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3319
|
+
className: FallbacksCard_module_css_default.error,
|
|
3320
|
+
role: "alert",
|
|
3321
|
+
children: t("error.generic", { message: state.error })
|
|
3322
|
+
})
|
|
3323
|
+
]
|
|
3626
3324
|
})
|
|
3627
3325
|
]
|
|
3628
3326
|
}),
|
|
@@ -3638,7 +3336,7 @@ window.__ModuleLoader__.load({
|
|
|
3638
3336
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3639
3337
|
type: "button",
|
|
3640
3338
|
className: FallbacksCard_module_css_default.discard,
|
|
3641
|
-
disabled: !
|
|
3339
|
+
disabled: !rolesDirty || saving,
|
|
3642
3340
|
onClick: () => {
|
|
3643
3341
|
discardSection("sub");
|
|
3644
3342
|
},
|
|
@@ -3646,7 +3344,7 @@ window.__ModuleLoader__.load({
|
|
|
3646
3344
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3647
3345
|
type: "button",
|
|
3648
3346
|
className: FallbacksCard_module_css_default.save,
|
|
3649
|
-
disabled: !writable || saving || !
|
|
3347
|
+
disabled: !writable || saving || !rolesDirty || liveErrors.roles.length > 0,
|
|
3650
3348
|
onClick: () => {
|
|
3651
3349
|
saveSection("sub");
|
|
3652
3350
|
},
|
|
@@ -3654,101 +3352,27 @@ window.__ModuleLoader__.load({
|
|
|
3654
3352
|
})]
|
|
3655
3353
|
})]
|
|
3656
3354
|
}),
|
|
3657
|
-
liveErrors.
|
|
3355
|
+
liveErrors.roles.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3658
3356
|
className: FallbacksCard_module_css_default.error,
|
|
3659
3357
|
role: "alert",
|
|
3660
|
-
children: `${t("validation.blocked")}${liveErrors.
|
|
3358
|
+
children: `${t("validation.blocked")}${liveErrors.roles.join("; ")}`
|
|
3661
3359
|
}),
|
|
3662
3360
|
lastSaveSection === "sub" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3663
3361
|
className: FallbacksCard_module_css_default.error,
|
|
3664
3362
|
role: "alert",
|
|
3665
3363
|
children: t("error.generic", { message: state.error })
|
|
3666
3364
|
}),
|
|
3667
|
-
state.subagentPolicy?.state === "enabled" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3668
|
-
className: FallbacksCard_module_css_default.policyStatus,
|
|
3669
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3670
|
-
className: FallbacksCard_module_css_default.policyStatusText,
|
|
3671
|
-
role: "status",
|
|
3672
|
-
children: [
|
|
3673
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3674
|
-
className: FallbacksCard_module_css_default.policyStatusLabel,
|
|
3675
|
-
children: t("subagents.policy.label")
|
|
3676
|
-
}),
|
|
3677
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3678
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3679
|
-
children: `${t("subagents.policy.allowlist")}: ${state.subagentPolicy.allowedModels.map((route) => `${route.provider}/${route.model}`).join(", ")}`
|
|
3680
|
-
}),
|
|
3681
|
-
state.subagentPolicy.head !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3682
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3683
|
-
children: `${t("subagents.policy.head")}: ${state.subagentPolicy.head.route.provider}/${state.subagentPolicy.head.route.model} (${t(state.subagentPolicy.head.source === "authorized" ? "subagents.policy.source.authorized" : "subagents.policy.source.injected")})`
|
|
3684
|
-
}),
|
|
3685
|
-
state.subagentPolicy.blockedAttempt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3686
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3687
|
-
role: "alert",
|
|
3688
|
-
children: t("subagents.policy.blocked")
|
|
3689
|
-
}),
|
|
3690
|
-
offAllowlistCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3691
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3692
|
-
children: t("subagents.policy.offAllowlist", { count: String(offAllowlistCount) })
|
|
3693
|
-
}),
|
|
3694
|
-
!virtualRouteAllowed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3695
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3696
|
-
children: t("subagents.policy.recommend")
|
|
3697
|
-
})
|
|
3698
|
-
]
|
|
3699
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3700
|
-
className: FallbacksCard_module_css_default.policyStatusSync,
|
|
3701
|
-
role: "group",
|
|
3702
|
-
"aria-label": t("subagents.policy.sync"),
|
|
3703
|
-
children: [
|
|
3704
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3705
|
-
type: "button",
|
|
3706
|
-
className: FallbacksCard_module_css_default.outlineButton,
|
|
3707
|
-
disabled: !writable || subDirty || mainDirty,
|
|
3708
|
-
"aria-busy": syncStatus === "pending" || void 0,
|
|
3709
|
-
"aria-describedby": subDirty || mainDirty ? syncDirtyHintId : void 0,
|
|
3710
|
-
onClick: syncAllowlist,
|
|
3711
|
-
children: t("subagents.policy.sync")
|
|
3712
|
-
}),
|
|
3713
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3714
|
-
id: syncDirtyHintId,
|
|
3715
|
-
role: "status",
|
|
3716
|
-
"data-sync-hint": "dirty",
|
|
3717
|
-
className: `${FallbacksCard_module_css_default.policyStatusWarn} ${FallbacksCard_module_css_default.policyStatusLive}`,
|
|
3718
|
-
children: subDirty || mainDirty ? t("subagents.policy.sync.dirty") : ""
|
|
3719
|
-
}),
|
|
3720
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3721
|
-
role: "status",
|
|
3722
|
-
"data-sync-hint": "outcome",
|
|
3723
|
-
className: `${syncStatus === "error" ? FallbacksCard_module_css_default.policyStatusWarn : FallbacksCard_module_css_default.policyStatusLine} ${FallbacksCard_module_css_default.policyStatusLive}`,
|
|
3724
|
-
children: syncStatus === "ok" ? t("subagents.policy.sync.done") : syncStatus === "error" ? t("subagents.policy.sync.failed") : ""
|
|
3725
|
-
})
|
|
3726
|
-
]
|
|
3727
|
-
})]
|
|
3728
|
-
}),
|
|
3729
|
-
state.subagentPolicy?.state === "unprovable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3730
|
-
className: FallbacksCard_module_css_default.policyStatus,
|
|
3731
|
-
role: "status",
|
|
3732
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3733
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3734
|
-
role: "alert",
|
|
3735
|
-
children: t("subagents.policy.unprovable")
|
|
3736
|
-
})
|
|
3737
|
-
}),
|
|
3738
3365
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3739
3366
|
className: FallbacksCard_module_css_default.field,
|
|
3740
3367
|
role: "group",
|
|
3741
3368
|
"aria-labelledby": "fallbacks-roles-list",
|
|
3742
3369
|
children: [
|
|
3743
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3370
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3744
3371
|
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3745
|
-
children:
|
|
3372
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3746
3373
|
id: "fallbacks-roles-list",
|
|
3747
3374
|
children: t("roles.list.label")
|
|
3748
|
-
})
|
|
3749
|
-
label: t("roles.list.tooltip"),
|
|
3750
|
-
disabled: !writable
|
|
3751
|
-
})]
|
|
3375
|
+
})
|
|
3752
3376
|
}),
|
|
3753
3377
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3754
3378
|
className: FallbacksCard_module_css_default.hint,
|
|
@@ -3761,11 +3385,12 @@ window.__ModuleLoader__.load({
|
|
|
3761
3385
|
const seed = seedInfo.get(row.id.trim());
|
|
3762
3386
|
const seedBadge = seedBadgeLabel(seed, t);
|
|
3763
3387
|
const personaBlank = row.persona.trim() === "";
|
|
3764
|
-
const
|
|
3765
|
-
const roleSummary = roleChainSummary ?? row.fallback;
|
|
3388
|
+
const chainRoute = row.chain === "" ? t("roles.chainMeta.unspecified") : `FallbacksChain/${row.chain}`;
|
|
3766
3389
|
const roleExpanded = !row.collapsed || !writable;
|
|
3390
|
+
const chainOutside = row.chain !== "" && !chains.some((chain) => chain.id === row.chain);
|
|
3767
3391
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3768
3392
|
className: FallbacksCard_module_css_default.editorCard,
|
|
3393
|
+
"data-role-card": row.id,
|
|
3769
3394
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3770
3395
|
className: FallbacksCard_module_css_default.collapseRow,
|
|
3771
3396
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -3790,8 +3415,8 @@ window.__ModuleLoader__.load({
|
|
|
3790
3415
|
}),
|
|
3791
3416
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3792
3417
|
className: FallbacksCard_module_css_default.collapseMeta,
|
|
3793
|
-
title:
|
|
3794
|
-
children:
|
|
3418
|
+
title: chainRoute,
|
|
3419
|
+
children: chainRoute
|
|
3795
3420
|
})
|
|
3796
3421
|
]
|
|
3797
3422
|
})
|
|
@@ -3823,6 +3448,49 @@ window.__ModuleLoader__.load({
|
|
|
3823
3448
|
]
|
|
3824
3449
|
})
|
|
3825
3450
|
}),
|
|
3451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3452
|
+
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3453
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3454
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
3455
|
+
children: [
|
|
3456
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3457
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3458
|
+
htmlFor: `fallbacks-role-chain-${index}`,
|
|
3459
|
+
children: t("roles.chain.label")
|
|
3460
|
+
}),
|
|
3461
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3462
|
+
id: `fallbacks-role-chain-${index}`,
|
|
3463
|
+
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
3464
|
+
value: row.chain,
|
|
3465
|
+
"aria-describedby": `fallbacks-role-chain-hint-${index}`,
|
|
3466
|
+
disabled: !writable,
|
|
3467
|
+
onChange: (event) => {
|
|
3468
|
+
updateRoleRow(index, { chain: event.target.value });
|
|
3469
|
+
},
|
|
3470
|
+
"data-role-chain-select": "",
|
|
3471
|
+
children: [
|
|
3472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3473
|
+
value: "",
|
|
3474
|
+
children: t("roles.chain.unspecified")
|
|
3475
|
+
}),
|
|
3476
|
+
chains.map((chain) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3477
|
+
value: chain.id,
|
|
3478
|
+
children: `${chain.label === "" ? chain.id : chain.label}(FallbacksChain/${chain.id})`
|
|
3479
|
+
}, chain.id)),
|
|
3480
|
+
chainOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3481
|
+
value: row.chain,
|
|
3482
|
+
children: `${row.chain}${t("roles.chainMeta.unspecified")}`
|
|
3483
|
+
})
|
|
3484
|
+
]
|
|
3485
|
+
}),
|
|
3486
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3487
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3488
|
+
id: `fallbacks-role-chain-hint-${index}`,
|
|
3489
|
+
children: t("roles.chain.hint")
|
|
3490
|
+
})
|
|
3491
|
+
]
|
|
3492
|
+
})
|
|
3493
|
+
}),
|
|
3826
3494
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3827
3495
|
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3828
3496
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3869,66 +3537,6 @@ window.__ModuleLoader__.load({
|
|
|
3869
3537
|
})] })]
|
|
3870
3538
|
})
|
|
3871
3539
|
}),
|
|
3872
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3873
|
-
className: FallbacksCard_module_css_default.chainBlock,
|
|
3874
|
-
"data-chain-block": "",
|
|
3875
|
-
children: [
|
|
3876
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3877
|
-
selectors: row.selectors,
|
|
3878
|
-
catalog: catalogOf(state),
|
|
3879
|
-
configuredProviders: state.configuredProviders,
|
|
3880
|
-
disabled: !writable,
|
|
3881
|
-
t,
|
|
3882
|
-
onChange: (selectorIndex, patch) => {
|
|
3883
|
-
updateRoleSelector(index, selectorIndex, patch);
|
|
3884
|
-
},
|
|
3885
|
-
onRemove: (selectorIndex) => {
|
|
3886
|
-
removeRoleSelector(index, selectorIndex);
|
|
3887
|
-
},
|
|
3888
|
-
onMove: (from, to) => {
|
|
3889
|
-
reorderRoleSelector(index, from, to);
|
|
3890
|
-
}
|
|
3891
|
-
}),
|
|
3892
|
-
row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3893
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3894
|
-
children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
|
|
3895
|
-
}),
|
|
3896
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3897
|
-
type: "button",
|
|
3898
|
-
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3899
|
-
disabled: !writable,
|
|
3900
|
-
onClick: () => {
|
|
3901
|
-
addRoleSelector(index);
|
|
3902
|
-
},
|
|
3903
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("roles.selector.add")]
|
|
3904
|
-
})
|
|
3905
|
-
]
|
|
3906
|
-
}),
|
|
3907
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3908
|
-
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3909
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3910
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3911
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3912
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3913
|
-
children: t("roles.fallback")
|
|
3914
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3915
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
3916
|
-
value: row.fallback,
|
|
3917
|
-
"aria-label": t("roles.fallback"),
|
|
3918
|
-
disabled: !writable,
|
|
3919
|
-
onChange: (event) => {
|
|
3920
|
-
updateRoleRow(index, { fallback: event.target.value });
|
|
3921
|
-
},
|
|
3922
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3923
|
-
value: "inherit-root",
|
|
3924
|
-
children: t("roles.fallback.inherit-root")
|
|
3925
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3926
|
-
value: "none",
|
|
3927
|
-
children: t("roles.fallback.none")
|
|
3928
|
-
})]
|
|
3929
|
-
})]
|
|
3930
|
-
})
|
|
3931
|
-
}),
|
|
3932
3540
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3933
3541
|
className: FallbacksCard_module_css_default.cardFoot,
|
|
3934
3542
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -3955,188 +3563,6 @@ window.__ModuleLoader__.load({
|
|
|
3955
3563
|
})
|
|
3956
3564
|
]
|
|
3957
3565
|
}),
|
|
3958
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3959
|
-
className: FallbacksCard_module_css_default.field,
|
|
3960
|
-
role: "group",
|
|
3961
|
-
"aria-labelledby": "fallbacks-roles-rules",
|
|
3962
|
-
children: [
|
|
3963
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3964
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3965
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3966
|
-
id: "fallbacks-roles-rules",
|
|
3967
|
-
children: t("roles.rules")
|
|
3968
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3969
|
-
label: t("roles.rules.tooltip"),
|
|
3970
|
-
disabled: !writable
|
|
3971
|
-
})]
|
|
3972
|
-
}),
|
|
3973
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3974
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3975
|
-
children: t("roles.rules.hint")
|
|
3976
|
-
}),
|
|
3977
|
-
state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3978
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3979
|
-
children: t("catalog.error", { message: state.catalogError })
|
|
3980
|
-
}),
|
|
3981
|
-
state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3982
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3983
|
-
children: t("catalog.partial", { message: state.catalogError })
|
|
3984
|
-
}),
|
|
3985
|
-
state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3986
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3987
|
-
children: t("catalog.empty")
|
|
3988
|
-
}),
|
|
3989
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3990
|
-
className: FallbacksCard_module_css_default.list,
|
|
3991
|
-
children: ruleRows.map((row, index) => {
|
|
3992
|
-
const catalog = catalogOf(state);
|
|
3993
|
-
const providerRaw = selectionToRaw(row.provider);
|
|
3994
|
-
const group = catalog?.groups.find((entry) => entry.id === providerRaw);
|
|
3995
|
-
const providerOutside = row.provider?.kind === "outside";
|
|
3996
|
-
const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !state.configuredProviders.some((entry) => entry.provider === providerRaw);
|
|
3997
|
-
const modelOutside = row.model?.kind === "outside";
|
|
3998
|
-
const roleOutside = row.role !== "" && !roleOptions.includes(row.role);
|
|
3999
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4000
|
-
className: FallbacksCard_module_css_default.editorCard,
|
|
4001
|
-
children: [
|
|
4002
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4003
|
-
className: FallbacksCard_module_css_default.ruleGrid,
|
|
4004
|
-
children: [
|
|
4005
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4006
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
4007
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4008
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
4009
|
-
children: t("roles.rule.provider")
|
|
4010
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4011
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
4012
|
-
value: providerRaw,
|
|
4013
|
-
onChange: (event) => {
|
|
4014
|
-
if (event.target.value === providerRaw) return;
|
|
4015
|
-
updateRuleRow(index, {
|
|
4016
|
-
provider: classifyProvider(event.target.value, catalog),
|
|
4017
|
-
model: null
|
|
4018
|
-
});
|
|
4019
|
-
},
|
|
4020
|
-
children: [
|
|
4021
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4022
|
-
value: "",
|
|
4023
|
-
children: t("roles.rule.provider.any")
|
|
4024
|
-
}),
|
|
4025
|
-
state.configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4026
|
-
value: entry.provider,
|
|
4027
|
-
children: entry.displayName
|
|
4028
|
-
}, entry.provider)),
|
|
4029
|
-
providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4030
|
-
value: providerRaw,
|
|
4031
|
-
children: `${providerRaw}${t("catalog.unconfigured.short")}`
|
|
4032
|
-
}),
|
|
4033
|
-
providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4034
|
-
value: providerRaw,
|
|
4035
|
-
children: `${providerRaw}${t("catalog.outside.short")}`
|
|
4036
|
-
})
|
|
4037
|
-
]
|
|
4038
|
-
})]
|
|
4039
|
-
}),
|
|
4040
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4041
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
4042
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4043
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
4044
|
-
children: t("roles.rule.model")
|
|
4045
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4046
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
4047
|
-
value: selectionToRaw(row.model),
|
|
4048
|
-
onChange: (event) => {
|
|
4049
|
-
updateRuleRow(index, { model: classifyModel(providerRaw, event.target.value, catalog) });
|
|
4050
|
-
},
|
|
4051
|
-
children: [
|
|
4052
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4053
|
-
value: "",
|
|
4054
|
-
children: t("roles.rule.model.any")
|
|
4055
|
-
}),
|
|
4056
|
-
(group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4057
|
-
value: model.id,
|
|
4058
|
-
children: model.name
|
|
4059
|
-
}, model.id)),
|
|
4060
|
-
modelOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4061
|
-
value: selectionToRaw(row.model),
|
|
4062
|
-
children: `${selectionToRaw(row.model)}${t("catalog.outside.short")}`
|
|
4063
|
-
})
|
|
4064
|
-
]
|
|
4065
|
-
})]
|
|
4066
|
-
}),
|
|
4067
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4068
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
4069
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4070
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
4071
|
-
children: t("roles.rule.role")
|
|
4072
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4073
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
4074
|
-
value: row.role,
|
|
4075
|
-
disabled: !writable,
|
|
4076
|
-
onChange: (event) => {
|
|
4077
|
-
updateRuleRow(index, { role: event.target.value });
|
|
4078
|
-
},
|
|
4079
|
-
children: [
|
|
4080
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4081
|
-
value: "",
|
|
4082
|
-
children: t("roles.rule.roleSelectPlaceholder")
|
|
4083
|
-
}),
|
|
4084
|
-
roleOptions.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4085
|
-
value: id,
|
|
4086
|
-
children: id === "inherit" ? t("roles.rule.role.inherit") : id
|
|
4087
|
-
}, id)),
|
|
4088
|
-
roleOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4089
|
-
value: row.role,
|
|
4090
|
-
children: `${row.role}${t("roles.rule.roleUndeclared.short")}`
|
|
4091
|
-
})
|
|
4092
|
-
]
|
|
4093
|
-
})]
|
|
4094
|
-
})
|
|
4095
|
-
]
|
|
4096
|
-
}),
|
|
4097
|
-
(providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4098
|
-
className: FallbacksCard_module_css_default.hint,
|
|
4099
|
-
children: [t("catalog.outside.hint"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
4100
|
-
label: t("catalog.outside.tooltip"),
|
|
4101
|
-
disabled: !writable
|
|
4102
|
-
})]
|
|
4103
|
-
}),
|
|
4104
|
-
row.role === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4105
|
-
className: FallbacksCard_module_css_default.hint,
|
|
4106
|
-
children: t("validation.ruleRoleRequired")
|
|
4107
|
-
}),
|
|
4108
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4109
|
-
className: FallbacksCard_module_css_default.cardFoot,
|
|
4110
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4111
|
-
type: "button",
|
|
4112
|
-
className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
|
|
4113
|
-
"data-tip": t("roles.removeRule"),
|
|
4114
|
-
"aria-label": t("roles.removeRule"),
|
|
4115
|
-
onClick: () => {
|
|
4116
|
-
setRuleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
|
|
4117
|
-
},
|
|
4118
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
|
|
4119
|
-
})
|
|
4120
|
-
})
|
|
4121
|
-
]
|
|
4122
|
-
}, index);
|
|
4123
|
-
})
|
|
4124
|
-
}),
|
|
4125
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4126
|
-
type: "button",
|
|
4127
|
-
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
4128
|
-
disabled: !writable,
|
|
4129
|
-
onClick: () => {
|
|
4130
|
-
setRuleRows((rows) => [...rows, {
|
|
4131
|
-
provider: null,
|
|
4132
|
-
model: null,
|
|
4133
|
-
role: ""
|
|
4134
|
-
}]);
|
|
4135
|
-
},
|
|
4136
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("roles.addRule")]
|
|
4137
|
-
})
|
|
4138
|
-
]
|
|
4139
|
-
}),
|
|
4140
3566
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
4141
3567
|
className: FallbacksCard_module_css_default.sectionHeading,
|
|
4142
3568
|
id: "fallbacks-advanced",
|
|
@@ -4161,47 +3587,6 @@ window.__ModuleLoader__.load({
|
|
|
4161
3587
|
id: "fallbacks-advanced-body",
|
|
4162
3588
|
className: FallbacksCard_module_css_default.advancedBody,
|
|
4163
3589
|
children: [
|
|
4164
|
-
liveErrors.advanced.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4165
|
-
className: FallbacksCard_module_css_default.error,
|
|
4166
|
-
role: "alert",
|
|
4167
|
-
children: `${t("validation.blocked")}${liveErrors.advanced.join("; ")}`
|
|
4168
|
-
}),
|
|
4169
|
-
lastSaveSection === "advanced" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4170
|
-
className: FallbacksCard_module_css_default.error,
|
|
4171
|
-
role: "alert",
|
|
4172
|
-
children: t("error.generic", { message: state.error })
|
|
4173
|
-
}),
|
|
4174
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4175
|
-
className: FallbacksCard_module_css_default.checkboxRow,
|
|
4176
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4177
|
-
id: "fallbacks-role-automatch",
|
|
4178
|
-
type: "checkbox",
|
|
4179
|
-
className: FallbacksCard_module_css_default.checkbox,
|
|
4180
|
-
checked: scalars.roleAutoMatch,
|
|
4181
|
-
disabled: !writable,
|
|
4182
|
-
onChange: (event) => {
|
|
4183
|
-
updateScalars((draft) => {
|
|
4184
|
-
draft.roleAutoMatch = event.target.checked;
|
|
4185
|
-
});
|
|
4186
|
-
}
|
|
4187
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4188
|
-
className: FallbacksCard_module_css_default.checkLabel,
|
|
4189
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4190
|
-
className: FallbacksCard_module_css_default.checkLabelTitle,
|
|
4191
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
4192
|
-
htmlFor: "fallbacks-role-automatch",
|
|
4193
|
-
children: t("roleAutoMatch.label")
|
|
4194
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
4195
|
-
label: t("roleAutoMatch.tooltip"),
|
|
4196
|
-
disabled: !writable
|
|
4197
|
-
})]
|
|
4198
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
4199
|
-
className: FallbacksCard_module_css_default.checkLabelDesc,
|
|
4200
|
-
htmlFor: "fallbacks-role-automatch",
|
|
4201
|
-
children: t("roleAutoMatch.hint")
|
|
4202
|
-
})]
|
|
4203
|
-
})]
|
|
4204
|
-
}),
|
|
4205
3590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4206
3591
|
className: FallbacksCard_module_css_default.field,
|
|
4207
3592
|
role: "group",
|
|
@@ -4275,104 +3660,69 @@ window.__ModuleLoader__.load({
|
|
|
4275
3660
|
}),
|
|
4276
3661
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4277
3662
|
className: FallbacksCard_module_css_default.numberFields,
|
|
4278
|
-
children: [
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4315
|
-
|
|
4316
|
-
children:
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
]
|
|
4342
|
-
}),
|
|
4343
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4344
|
-
className: FallbacksCard_module_css_default.field,
|
|
4345
|
-
children: [
|
|
4346
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4347
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
4348
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
4349
|
-
htmlFor: "fallbacks-always-cap",
|
|
4350
|
-
children: t("alwaysModeRetryCap.label")
|
|
4351
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
4352
|
-
label: alwaysModeRetryCapTip,
|
|
4353
|
-
disabled: !writable
|
|
4354
|
-
})]
|
|
4355
|
-
}),
|
|
4356
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4357
|
-
id: "fallbacks-always-cap",
|
|
4358
|
-
className: FallbacksCard_module_css_default.input,
|
|
4359
|
-
type: "number",
|
|
4360
|
-
min: 0,
|
|
4361
|
-
value: String(scalars.alwaysModeRetryCap),
|
|
4362
|
-
disabled: !writable,
|
|
4363
|
-
onChange: (event) => {
|
|
4364
|
-
updateScalars((draft) => {
|
|
4365
|
-
draft.alwaysModeRetryCap = parseCount(event.target.value);
|
|
4366
|
-
});
|
|
4367
|
-
}
|
|
4368
|
-
}),
|
|
4369
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4370
|
-
className: FallbacksCard_module_css_default.hint,
|
|
4371
|
-
children: t("alwaysModeRetryCap.hint")
|
|
4372
|
-
})
|
|
4373
|
-
]
|
|
4374
|
-
})
|
|
4375
|
-
]
|
|
3663
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3664
|
+
className: FallbacksCard_module_css_default.field,
|
|
3665
|
+
children: [
|
|
3666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3667
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3668
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3669
|
+
htmlFor: "fallbacks-cooldown-ms",
|
|
3670
|
+
children: t("cooldownMs.label")
|
|
3671
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3672
|
+
label: cooldownMsTip,
|
|
3673
|
+
disabled: !writable
|
|
3674
|
+
})]
|
|
3675
|
+
}),
|
|
3676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3677
|
+
id: "fallbacks-cooldown-ms",
|
|
3678
|
+
className: FallbacksCard_module_css_default.input,
|
|
3679
|
+
type: "number",
|
|
3680
|
+
min: 0,
|
|
3681
|
+
value: String(scalars.cooldownMs),
|
|
3682
|
+
disabled: !writable,
|
|
3683
|
+
onChange: (event) => {
|
|
3684
|
+
updateScalars((draft) => {
|
|
3685
|
+
draft.cooldownMs = parseCount(event.target.value);
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
}),
|
|
3689
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3690
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3691
|
+
children: t("cooldownMs.hint")
|
|
3692
|
+
})
|
|
3693
|
+
]
|
|
3694
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3695
|
+
className: FallbacksCard_module_css_default.field,
|
|
3696
|
+
children: [
|
|
3697
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3698
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3699
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
3700
|
+
htmlFor: "fallbacks-max-switches",
|
|
3701
|
+
children: t("maxSwitchesPerStep.label")
|
|
3702
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3703
|
+
label: maxSwitchesPerStepTip,
|
|
3704
|
+
disabled: !writable
|
|
3705
|
+
})]
|
|
3706
|
+
}),
|
|
3707
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3708
|
+
id: "fallbacks-max-switches",
|
|
3709
|
+
className: FallbacksCard_module_css_default.input,
|
|
3710
|
+
type: "number",
|
|
3711
|
+
min: 0,
|
|
3712
|
+
value: String(scalars.maxSwitchesPerStep),
|
|
3713
|
+
disabled: !writable,
|
|
3714
|
+
onChange: (event) => {
|
|
3715
|
+
updateScalars((draft) => {
|
|
3716
|
+
draft.maxSwitchesPerStep = parseCount(event.target.value);
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
3719
|
+
}),
|
|
3720
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3721
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3722
|
+
children: t("maxSwitchesPerStep.hint")
|
|
3723
|
+
})
|
|
3724
|
+
]
|
|
3725
|
+
})]
|
|
4376
3726
|
}),
|
|
4377
3727
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4378
3728
|
className: FallbacksCard_module_css_default.sectionActions,
|
|
@@ -4397,126 +3747,11 @@ window.__ModuleLoader__.load({
|
|
|
4397
3747
|
]
|
|
4398
3748
|
})
|
|
4399
3749
|
]
|
|
4400
|
-
}),
|
|
4401
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4402
|
-
className: FallbacksCard_module_css_default.statusBlock,
|
|
4403
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4404
|
-
className: FallbacksCard_module_css_default.statusTitle,
|
|
4405
|
-
children: t("status.title")
|
|
4406
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4407
|
-
className: FallbacksCard_module_css_default.statusLine,
|
|
4408
|
-
role: state.switchesStatus === "error" ? "alert" : void 0,
|
|
4409
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4410
|
-
className: FallbacksCard_module_css_default.statusLineLabel,
|
|
4411
|
-
children: t("status.switches.label")
|
|
4412
|
-
}), switchesLine]
|
|
4413
|
-
})]
|
|
4414
3750
|
})
|
|
4415
3751
|
]
|
|
4416
3752
|
});
|
|
4417
3753
|
}
|
|
4418
3754
|
//#endregion
|
|
4419
|
-
//#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/GeneralFallbacksRow.module.css.mjs
|
|
4420
|
-
const css$2 = "\n\n._440e1d7b_row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 0;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n\n._a4f2ceec_rowText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-right: 48px;\n}\n\n._9865b509_title {\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._10a44713_summary {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n._35648278_badge {\n flex: none;\n padding: 2px 10px;\n border-radius: 9px;\n font-size: 12px;\n line-height: 18px;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-tertiary);\n}\n\n._3b9e8609_badgeEnabled {\n color: var(--dsw-alias-state-success-primary);\n}\n";
|
|
4421
|
-
const tagId$2 = "dsh-llm-fallbacks/GeneralFallbacksRow.module.css";
|
|
4422
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
4423
|
-
const tag = document.createElement("style");
|
|
4424
|
-
tag.dataset.plugin = "dsh-llm-fallbacks";
|
|
4425
|
-
tag.dataset.pluginCss = tagId$2;
|
|
4426
|
-
tag.textContent = css$2;
|
|
4427
|
-
document.head.appendChild(tag);
|
|
4428
|
-
}
|
|
4429
|
-
var GeneralFallbacksRow_module_css_default = {
|
|
4430
|
-
"row": "_440e1d7b_row",
|
|
4431
|
-
"rowText": "_a4f2ceec_rowText",
|
|
4432
|
-
"title": "_9865b509_title",
|
|
4433
|
-
"summary": "_10a44713_summary",
|
|
4434
|
-
"badge": "_35648278_badge",
|
|
4435
|
-
"badgeEnabled": "_3b9e8609_badgeEnabled"
|
|
4436
|
-
};
|
|
4437
|
-
//#endregion
|
|
4438
|
-
//#region src/client/GeneralFallbacksRow.tsx
|
|
4439
|
-
/**
|
|
4440
|
-
* Fallbacks status row — the `fallbacks` read-only row on the dsh General
|
|
4441
|
-
* settings page (plan fallbacks-aux-seams, task 1). Registered into the
|
|
4442
|
-
* `settings.general.item` slot (id `fallbacks`, order 100 — after every
|
|
4443
|
-
* upstream preference row: agent-preset -25 / permission -20 / language 0 /
|
|
4444
|
-
* appearance 10 / composer-enter 20, so the informational row renders at the
|
|
4445
|
-
* column end). Owner props are intentionally empty (`children?: never`,
|
|
4446
|
-
* dsh-private ui-settings slots.ts:81-84 — the section column only stacks),
|
|
4447
|
-
* so all data flows through the shared {@link FallbacksSettingsController}
|
|
4448
|
-
* (the same instance the plugin-config card consumes): the row triggers the
|
|
4449
|
-
* first read on mount when the store is still idle, and the pushed
|
|
4450
|
-
* invalidations wired in `apply` (`settings/document-updated` fallbacks-ns +
|
|
4451
|
-
* `connection/reset`, which refresh only already-read stores) keep it fresh
|
|
4452
|
-
* afterwards — no new data path, no store API change.
|
|
4453
|
-
*
|
|
4454
|
-
* The row is read-only by design (偏好位语义: a General preference row is not
|
|
4455
|
-
* a control surface): an active/inactive badge (content presence — the
|
|
4456
|
-
* removed config-level `enabled` switch is re-keyed to `isFallbackActive`,
|
|
4457
|
-
* plan fallbacks-web-ux-alignment T2) + a compact last-switch summary.
|
|
4458
|
-
* Honest degraded states: a hard load error or an unreachable gateway
|
|
4459
|
-
* channel (`ready && !present`) render the neutral 'unknown' badge — a
|
|
4460
|
-
* channel-down read must never masquerade as 'disabled' (KD-G5); the
|
|
4461
|
-
* switches face keeps its own error/empty states (D-5 semantics unchanged).
|
|
4462
|
-
*
|
|
4463
|
-
* Geometry follows the upstream Setting-Cell (figma 501:30011 — gap 8,
|
|
4464
|
-
* pad 16/0, hairline separator, title over subtitle in the text column, a
|
|
4465
|
-
* small non-interactive pill on the right); every color resolves through a
|
|
4466
|
-
* `--dsw-alias-*` token (light/dark adaptive).
|
|
4467
|
-
*/
|
|
4468
|
-
/**
|
|
4469
|
-
* Render the Fallbacks status row.
|
|
4470
|
-
* @param props - composed slot props.
|
|
4471
|
-
* @returns the row element tree.
|
|
4472
|
-
*/
|
|
4473
|
-
function GeneralFallbacksRow({ controller, useSnapshot, t }) {
|
|
4474
|
-
const state = useSnapshot((snapshot) => snapshot);
|
|
4475
|
-
(0, react.useEffect)(() => {
|
|
4476
|
-
const snapshot = controller.store.getSnapshot();
|
|
4477
|
-
if (snapshot.status === "idle") controller.load();
|
|
4478
|
-
if (snapshot.switchesStatus === "idle") controller.loadSwitches();
|
|
4479
|
-
}, [controller]);
|
|
4480
|
-
const settled = state.status === "ready";
|
|
4481
|
-
const badgeKey = settled && state.present ? isFallbackActive(state.config) ? "general.enabled" : "general.disabled" : "general.unknown";
|
|
4482
|
-
const latestSwitch = state.switches[0];
|
|
4483
|
-
let summary;
|
|
4484
|
-
if (state.status === "error") summary = t("general.error", { message: state.error ?? "" });
|
|
4485
|
-
else if (!settled) summary = t("loading");
|
|
4486
|
-
else if (!state.present) summary = t("general.unavailable");
|
|
4487
|
-
else if (state.switchesStatus === "error") summary = t("status.switches.error", { message: state.switchesError ?? "" });
|
|
4488
|
-
else if (state.switchesStatus === "loading") summary = t("loading");
|
|
4489
|
-
else if (latestSwitch === void 0) summary = t("general.switch.empty");
|
|
4490
|
-
else {
|
|
4491
|
-
const reasonKey = SWITCH_REASON_KEYS[latestSwitch.reason];
|
|
4492
|
-
const params = {
|
|
4493
|
-
from: `${latestSwitch.from.provider}/${latestSwitch.from.model}`,
|
|
4494
|
-
to: `${latestSwitch.to.provider}/${latestSwitch.to.model}`,
|
|
4495
|
-
role: latestSwitch.role,
|
|
4496
|
-
reason: reasonKey === void 0 ? latestSwitch.reason : t(reasonKey)
|
|
4497
|
-
};
|
|
4498
|
-
summary = latestSwitch.reason === "role-inject" ? t("general.switch.roleInject", params) : t("general.switch", params);
|
|
4499
|
-
}
|
|
4500
|
-
const alert = state.status === "error" || state.switchesStatus === "error";
|
|
4501
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4502
|
-
className: GeneralFallbacksRow_module_css_default.row,
|
|
4503
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4504
|
-
className: GeneralFallbacksRow_module_css_default.rowText,
|
|
4505
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4506
|
-
className: GeneralFallbacksRow_module_css_default.title,
|
|
4507
|
-
children: t("general.title")
|
|
4508
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4509
|
-
className: GeneralFallbacksRow_module_css_default.summary,
|
|
4510
|
-
role: alert ? "alert" : void 0,
|
|
4511
|
-
children: summary
|
|
4512
|
-
})]
|
|
4513
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4514
|
-
className: `${GeneralFallbacksRow_module_css_default.badge} ${badgeKey === "general.enabled" ? GeneralFallbacksRow_module_css_default.badgeEnabled : ""}`,
|
|
4515
|
-
children: t(badgeKey)
|
|
4516
|
-
})]
|
|
4517
|
-
});
|
|
4518
|
-
}
|
|
4519
|
-
//#endregion
|
|
4520
3755
|
//#region src/client/switch-guard.ts
|
|
4521
3756
|
/**
|
|
4522
3757
|
* True when `value` is a well-formed `fallbacks/switch` payload — the ONE
|
|
@@ -4870,7 +4105,7 @@ window.__ModuleLoader__.load({
|
|
|
4870
4105
|
const connection = ctx.get("connection");
|
|
4871
4106
|
const sessions = ctx.get("sessions");
|
|
4872
4107
|
const controller = new FallbacksSettingsController(ctx.remote, connection.rpc);
|
|
4873
|
-
|
|
4108
|
+
controller.store;
|
|
4874
4109
|
ctx.effect(() => {
|
|
4875
4110
|
const syncSession = () => {
|
|
4876
4111
|
controller.setCurrentSession(sessions === void 0 ? void 0 : viewedSessionId(sessions.list.getSnapshot()));
|
|
@@ -4879,7 +4114,6 @@ window.__ModuleLoader__.load({
|
|
|
4879
4114
|
const refresh = (ns) => {
|
|
4880
4115
|
if (ns !== void 0 && ns !== "fallbacks") return;
|
|
4881
4116
|
refreshFallbacksIfLoaded(controller);
|
|
4882
|
-
refreshSwitchesIfLoaded(controller);
|
|
4883
4117
|
};
|
|
4884
4118
|
const refreshCatalog = () => {
|
|
4885
4119
|
refreshCatalogIfLoaded(controller);
|
|
@@ -4920,18 +4154,6 @@ window.__ModuleLoader__.load({
|
|
|
4920
4154
|
})
|
|
4921
4155
|
}, FallbacksCard);
|
|
4922
4156
|
});
|
|
4923
|
-
ctx.slots.inject("settings.general.item", function* () {
|
|
4924
|
-
yield ctx.slots.register({
|
|
4925
|
-
name: "settings.general.item",
|
|
4926
|
-
id: "fallbacks",
|
|
4927
|
-
order: 100,
|
|
4928
|
-
locale: NS,
|
|
4929
|
-
inject: () => ({
|
|
4930
|
-
controller,
|
|
4931
|
-
useSnapshot
|
|
4932
|
-
})
|
|
4933
|
-
}, GeneralFallbacksRow);
|
|
4934
|
-
});
|
|
4935
4157
|
ctx.effect(() => ctx.uiConversation.events.register(fallbackSwitchDefinition), "llm-fallbacks: conversation node definition");
|
|
4936
4158
|
ctx.slots.inject("conversation.chat.node", function* () {
|
|
4937
4159
|
yield ctx.slots.register({
|