dsh-llm-fallbacks 0.7.0 → 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 +27 -27
- package/README.zh-CN.md +25 -25
- package/dist/chain-walk.d.ts +63 -77
- package/dist/chains.d.ts +32 -101
- 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 +1256 -1926
- package/dist/client/locales.d.ts +59 -105
- package/dist/commands.d.ts +68 -103
- package/dist/config.d.ts +152 -105
- package/dist/gateway.d.ts +63 -109
- package/dist/index.d.ts +3 -25
- package/dist/index.js +552 -775
- package/dist/role-resolution.d.ts +14 -43
- package/dist/schema.d.ts +6 -5
- package/dist/seeds.d.ts +5 -7
- package/dist/state.d.ts +0 -42
- package/dist/subagents-seam.d.ts +23 -45
- package/dist/time-slots.d.ts +21 -24
- package/dist/tui-settings.d.ts +5 -8
- package/dist/virtual-adapter.d.ts +63 -108
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +1 -1
- package/dist/client/GeneralFallbacksRow.d.ts +0 -19
- package/dist/roles.d.ts +0 -89
- package/dist/route-allowlist.d.ts +0 -43
- package/dist/subagent-policy.d.ts +0 -137
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,50 +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
476
|
presets: "bundled",
|
|
493
|
-
timeSlots: [],
|
|
494
477
|
tz: "Asia/Shanghai",
|
|
495
478
|
recovery: "timer"
|
|
496
479
|
};
|
|
497
|
-
/**
|
|
498
|
-
* Reserved role id: legal as a rule target (`roles.rules[].role`) and as
|
|
499
|
-
* the no-rule-match fallback, but FORBIDDEN in `roles.list[].id`.
|
|
500
|
-
*/
|
|
501
|
-
const INHERIT_ROLE_ID = "inherit";
|
|
502
|
-
/**
|
|
503
|
-
* The no-op gate (AC-8), re-keyed from the removed `enabled` switch to
|
|
504
|
-
* CONTENT PRESENCE (plan fallbacks-web-ux-alignment T2): the plugin
|
|
505
|
-
* intervenes exactly when something is configured — a non-empty `rootChain`
|
|
506
|
-
* (the all-day chain), at least one time-slot row, or at least one declared
|
|
507
|
-
* role. An inactive config is a pass-through exactly like an uninstalled
|
|
508
|
-
* plugin; the Plugins-page row toggle remains the master switch (row
|
|
509
|
-
* disable = fiber destroy). ONE helper for every runtime gate — the former
|
|
510
|
-
* `config.enabled` sites (request waterfalls, picker-row registration, TUI
|
|
511
|
-
* summary, General-row badge) all read through this.
|
|
512
|
-
*/
|
|
513
|
-
function isFallbackActive(config) {
|
|
514
|
-
return config.rootChain.length > 0 || (config.timeSlots?.length ?? 0) > 0 || config.roles.list.length > 0;
|
|
515
|
-
}
|
|
516
480
|
/** Role id format (aligned with yet-another-subagent `isValidProfileId`). */
|
|
517
481
|
const ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
518
482
|
//#endregion
|
|
519
|
-
//#region src/route-allowlist.ts
|
|
520
|
-
/**
|
|
521
|
-
* Whether the route is on the allowlist under exact route identity: both the
|
|
522
|
-
* provider and the model strings must equal an allowed route exactly.
|
|
523
|
-
*/
|
|
524
|
-
function routeOnAllowlist(route, allowedModels) {
|
|
525
|
-
return allowedModels.some((allowed) => allowed.provider === route.provider && allowed.model === route.model);
|
|
526
|
-
}
|
|
527
|
-
//#endregion
|
|
528
483
|
//#region src/client/fallbacks-store.ts
|
|
529
484
|
/** The plugin's settings namespace on the host wire (settings/document-updated ns filter). */
|
|
530
485
|
const FALLBACKS_SETTINGS_NS = "fallbacks";
|
|
@@ -580,56 +535,6 @@ window.__ModuleLoader__.load({
|
|
|
580
535
|
}
|
|
581
536
|
return entries;
|
|
582
537
|
}
|
|
583
|
-
function parsePolicyRoute(value) {
|
|
584
|
-
if (!isRecord(value)) return void 0;
|
|
585
|
-
if (typeof value.provider !== "string" || value.provider.length === 0) return void 0;
|
|
586
|
-
if (typeof value.model !== "string" || value.model.length === 0) return void 0;
|
|
587
|
-
return {
|
|
588
|
-
provider: value.provider,
|
|
589
|
-
model: value.model
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Shape-guard the additive `subagentPolicy` wire field (T5). Malformed
|
|
594
|
-
* payloads resolve to `undefined` (hide the status area) — the store never
|
|
595
|
-
* throws on an old or misshapen field (old-payload tolerance).
|
|
596
|
-
*/
|
|
597
|
-
function parseSubagentPolicyWire(value) {
|
|
598
|
-
if (!isRecord(value) || typeof value.state !== "string") return void 0;
|
|
599
|
-
if (value.state === "disabled" || value.state === "unprovable") return { state: value.state };
|
|
600
|
-
if (value.state !== "enabled") return void 0;
|
|
601
|
-
if (!Array.isArray(value.allowedModels)) return void 0;
|
|
602
|
-
const allowedModels = [];
|
|
603
|
-
for (const entry of value.allowedModels) {
|
|
604
|
-
const route = parsePolicyRoute(entry);
|
|
605
|
-
if (route === void 0) return void 0;
|
|
606
|
-
allowedModels.push(route);
|
|
607
|
-
}
|
|
608
|
-
const view = {
|
|
609
|
-
state: "enabled",
|
|
610
|
-
allowedModels
|
|
611
|
-
};
|
|
612
|
-
if (isRecord(value.head) && (value.head.source === "authorized" || value.head.source === "injected")) {
|
|
613
|
-
const route = parsePolicyRoute(value.head.route);
|
|
614
|
-
if (route !== void 0) view.head = {
|
|
615
|
-
route,
|
|
616
|
-
source: value.head.source
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
if (isRecord(value.blockedAttempt) && typeof value.blockedAttempt.at === "number") {
|
|
620
|
-
const route = parsePolicyRoute(value.blockedAttempt.route);
|
|
621
|
-
if (route !== void 0) view.blockedAttempt = {
|
|
622
|
-
at: value.blockedAttempt.at,
|
|
623
|
-
route,
|
|
624
|
-
reason: typeof value.blockedAttempt.reason === "string" ? value.blockedAttempt.reason : ""
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
return view;
|
|
628
|
-
}
|
|
629
|
-
function subagentPolicyFromWire(value, fallback) {
|
|
630
|
-
if (!isRecord(value) || !("subagentPolicy" in value)) return fallback;
|
|
631
|
-
return parseSubagentPolicyWire(value.subagentPolicy);
|
|
632
|
-
}
|
|
633
538
|
/**
|
|
634
539
|
* The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
|
|
635
540
|
* settings profile resolves in the describe namespaces — the Models page's
|
|
@@ -655,8 +560,29 @@ window.__ModuleLoader__.load({
|
|
|
655
560
|
if (!isRecord(value)) throw new TypeError(`fallbacks descriptor value is not an object: ${String(value)}`);
|
|
656
561
|
const triggerCodes = value.triggerCodes;
|
|
657
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");
|
|
658
|
-
const
|
|
659
|
-
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 })");
|
|
660
586
|
const roles = isRecord(value.roles) ? value.roles : {};
|
|
661
587
|
const parsedList = (Array.isArray(roles.list) ? roles.list : []).map((role, index) => {
|
|
662
588
|
if (!isRecord(role) || typeof role.id !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}] must have a string id`);
|
|
@@ -666,32 +592,13 @@ window.__ModuleLoader__.load({
|
|
|
666
592
|
if (prompt !== void 0 && typeof prompt !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].prompt must be a string`);
|
|
667
593
|
const permissions = role.permissions;
|
|
668
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`);
|
|
669
|
-
const chain = role.chain;
|
|
670
|
-
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`);
|
|
671
|
-
const fallback = role.fallback;
|
|
672
|
-
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;
|
|
673
596
|
return {
|
|
674
597
|
id: role.id,
|
|
675
598
|
persona: persona ?? "",
|
|
676
599
|
...prompt === void 0 ? {} : { prompt },
|
|
677
600
|
...permissions === void 0 ? {} : { permissions },
|
|
678
|
-
chain: chain
|
|
679
|
-
fallback: fallback ?? "inherit-root"
|
|
680
|
-
};
|
|
681
|
-
});
|
|
682
|
-
const parsedRules = (Array.isArray(roles.rules) ? roles.rules : []).map((rule, index) => {
|
|
683
|
-
if (!isRecord(rule) || typeof rule.role !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}] must have a string role`);
|
|
684
|
-
const origin = rule.origin;
|
|
685
|
-
if (origin !== void 0 && origin !== "root" && origin !== "subagent") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].origin must be root|subagent`);
|
|
686
|
-
const provider = rule.provider;
|
|
687
|
-
const model = rule.model;
|
|
688
|
-
if (provider !== void 0 && typeof provider !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].provider must be a string`);
|
|
689
|
-
if (model !== void 0 && typeof model !== "string") throw new TypeError(`fallbacks descriptor roles.rules[${String(index)}].model must be a string`);
|
|
690
|
-
return {
|
|
691
|
-
...origin === void 0 ? {} : { origin },
|
|
692
|
-
...provider === void 0 ? {} : { provider },
|
|
693
|
-
...model === void 0 ? {} : { model },
|
|
694
|
-
role: rule.role
|
|
601
|
+
...chain === void 0 || chain === "" ? {} : { chain }
|
|
695
602
|
};
|
|
696
603
|
});
|
|
697
604
|
const cooldownMs = value.cooldownMs;
|
|
@@ -701,37 +608,21 @@ window.__ModuleLoader__.load({
|
|
|
701
608
|
if (revertPolicy !== void 0 && revertPolicy !== "cooldown-expiry" && revertPolicy !== "never") throw new TypeError("fallbacks descriptor revertPolicy must be cooldown-expiry|never");
|
|
702
609
|
const presets = value.presets;
|
|
703
610
|
if (presets !== void 0 && presets !== "bundled" && presets !== "none") throw new TypeError("fallbacks descriptor presets must be bundled|none");
|
|
704
|
-
const timeSlots = value.timeSlots;
|
|
705
|
-
if (timeSlots !== void 0 && (!Array.isArray(timeSlots) || timeSlots.some((row) => {
|
|
706
|
-
if (!isRecord(row)) return true;
|
|
707
|
-
for (const field of [
|
|
708
|
-
"kind",
|
|
709
|
-
"preset",
|
|
710
|
-
"start",
|
|
711
|
-
"end"
|
|
712
|
-
]) if (row[field] !== void 0 && typeof row[field] !== "string") return true;
|
|
713
|
-
const chain = row.chain;
|
|
714
|
-
if (chain !== void 0 && (!Array.isArray(chain) || chain.some((entry) => typeof entry !== "string"))) return true;
|
|
715
|
-
const days = row.days;
|
|
716
|
-
if (days !== void 0 && (!Array.isArray(days) || days.some((day) => typeof day !== "number"))) return true;
|
|
717
|
-
return false;
|
|
718
|
-
}))) throw new TypeError("fallbacks descriptor timeSlots must be an array of slot rows (kind/preset/start/end strings, chain string array, days number array)");
|
|
719
611
|
const tz = value.tz;
|
|
720
612
|
if (tz !== void 0 && typeof tz !== "string") throw new TypeError("fallbacks descriptor tz must be a string");
|
|
721
613
|
const recovery = value.recovery;
|
|
722
614
|
if (recovery !== void 0 && recovery !== "timer" && recovery !== "half-open") throw new TypeError("fallbacks descriptor recovery must be timer|half-open");
|
|
723
615
|
return {
|
|
724
616
|
triggerCodes: triggerCodes ?? [...defaultFallbacksConfig.triggerCodes],
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
},
|
|
617
|
+
chains: chains ?? defaultFallbacksConfig.chains.map((chain) => ({
|
|
618
|
+
...chain,
|
|
619
|
+
timeSlots: [...chain.timeSlots ?? []]
|
|
620
|
+
})),
|
|
621
|
+
roles: { list: parsedList },
|
|
730
622
|
cooldownMs: cooldownMs ?? defaultFallbacksConfig.cooldownMs,
|
|
731
623
|
revertPolicy: revertPolicy ?? defaultFallbacksConfig.revertPolicy,
|
|
732
624
|
maxSwitchesPerStep: maxSwitchesPerStep ?? defaultFallbacksConfig.maxSwitchesPerStep,
|
|
733
625
|
presets: presets ?? defaultFallbacksConfig.presets,
|
|
734
|
-
timeSlots: timeSlots ?? [...defaultFallbacksConfig.timeSlots ?? []],
|
|
735
626
|
tz: tz ?? defaultFallbacksConfig.tz,
|
|
736
627
|
recovery: recovery ?? defaultFallbacksConfig.recovery
|
|
737
628
|
};
|
|
@@ -771,27 +662,6 @@ window.__ModuleLoader__.load({
|
|
|
771
662
|
raw
|
|
772
663
|
};
|
|
773
664
|
}
|
|
774
|
-
/**
|
|
775
|
-
* Extract the most recent `fallbacks/switch` events from one history page
|
|
776
|
-
* (spec §2.5 D-5): drop packed chunk runs, filter by event type, order by
|
|
777
|
-
* `seq` descending, take at most `limit`. Single-page read — fewer than
|
|
778
|
-
* `limit` events show as-is; no multi-page backfill (Non-Goal).
|
|
779
|
-
*/
|
|
780
|
-
function extractRecentSwitches(entries, limit = 5) {
|
|
781
|
-
const switches = [];
|
|
782
|
-
for (const entry of entries) {
|
|
783
|
-
if (entry.type !== "event") continue;
|
|
784
|
-
const event = entry.event;
|
|
785
|
-
if (event.type !== "fallbacks/switch") continue;
|
|
786
|
-
switches.push({
|
|
787
|
-
...event.data,
|
|
788
|
-
seq: event.seq,
|
|
789
|
-
time: event.time
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
switches.sort((a, b) => b.seq - a.seq);
|
|
793
|
-
return switches.slice(0, limit);
|
|
794
|
-
}
|
|
795
665
|
/** Serialize one selector row to its wire string (`provider/model` | `provider/*`). */
|
|
796
666
|
function selectorRowToRaw(row) {
|
|
797
667
|
const provider = selectionToRaw(row.provider);
|
|
@@ -820,22 +690,6 @@ window.__ModuleLoader__.load({
|
|
|
820
690
|
};
|
|
821
691
|
}
|
|
822
692
|
}
|
|
823
|
-
/** Project the rootChain entries into editable rows (one flat chain row). */
|
|
824
|
-
function rootChainToRows(rootChain, catalog) {
|
|
825
|
-
return [{ selectors: rootChain.map((entry) => entryToSelectorRow(entry, catalog)) }];
|
|
826
|
-
}
|
|
827
|
-
/** Rebuild the rootChain from edited rows; rows with no usable selector drop out. */
|
|
828
|
-
function rowsToRootChain(rows) {
|
|
829
|
-
const entries = [];
|
|
830
|
-
for (const row of rows) {
|
|
831
|
-
if (row.selectors.length === 0) continue;
|
|
832
|
-
for (const selector of row.selectors) {
|
|
833
|
-
const raw = selectorRowToRaw(selector);
|
|
834
|
-
if (raw !== "") entries.push(raw);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
return entries;
|
|
838
|
-
}
|
|
839
693
|
/** Project the time-slot rows into editable rows (chain selectors classified). */
|
|
840
694
|
function timeSlotsToRows(timeSlots, catalog) {
|
|
841
695
|
return timeSlots.map((row) => ({
|
|
@@ -851,11 +705,10 @@ window.__ModuleLoader__.load({
|
|
|
851
705
|
}
|
|
852
706
|
/** Rebuild the time-slot rows from edited rows; blank selectors drop out.
|
|
853
707
|
* `kind` rides verbatim (a hand-written unknown kind reads back unchanged;
|
|
854
|
-
* save validation rejects it)
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
* 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. */
|
|
859
712
|
function rowsToTimeSlots(rows) {
|
|
860
713
|
return rows.map((row) => {
|
|
861
714
|
const chain = row.selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
|
|
@@ -876,35 +729,63 @@ window.__ModuleLoader__.load({
|
|
|
876
729
|
};
|
|
877
730
|
});
|
|
878
731
|
}
|
|
879
|
-
/** Project the
|
|
880
|
-
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) {
|
|
881
766
|
return roles.map((role) => ({
|
|
882
767
|
id: role.id,
|
|
883
768
|
persona: role.persona,
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
collapsed: true,
|
|
769
|
+
chain: role.chain ?? "",
|
|
770
|
+
collapsed: false,
|
|
887
771
|
personaOpen: false
|
|
888
772
|
}));
|
|
889
773
|
}
|
|
890
|
-
/** Rebuild the declared roles from edited rows; empty
|
|
774
|
+
/** Rebuild the declared roles from edited rows; an empty pointer drops out. */
|
|
891
775
|
function rowsToRoles(rows) {
|
|
892
776
|
return rows.map((row) => ({
|
|
893
777
|
id: row.id.trim(),
|
|
894
778
|
persona: row.persona,
|
|
895
|
-
chain: row.
|
|
896
|
-
fallback: row.fallback
|
|
779
|
+
...row.chain === "" ? {} : { chain: row.chain }
|
|
897
780
|
}));
|
|
898
781
|
}
|
|
899
782
|
/**
|
|
900
783
|
* Rebuild the declared roles from edited rows, re-attaching the
|
|
901
784
|
* schema-reserved `prompt`/`permissions` fields from the last accepted
|
|
902
785
|
* config by role id — they never round-trip through rows this round, so
|
|
903
|
-
* without the merge a save would silently drop them
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
* `parseFallbacksConfig` so a clean draft's JSON dirty comparison never
|
|
907
|
-
* 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.
|
|
908
789
|
*/
|
|
909
790
|
function mergeRoleExtras(rows, originalRoles) {
|
|
910
791
|
const originalById = new Map(originalRoles.map((role) => [role.id, role]));
|
|
@@ -916,36 +797,10 @@ window.__ModuleLoader__.load({
|
|
|
916
797
|
persona: role.persona,
|
|
917
798
|
...original.prompt === void 0 ? {} : { prompt: original.prompt },
|
|
918
799
|
...original.permissions === void 0 ? {} : { permissions: original.permissions },
|
|
919
|
-
chain: role.chain
|
|
920
|
-
fallback: role.fallback
|
|
800
|
+
...role.chain === void 0 ? {} : { chain: role.chain }
|
|
921
801
|
};
|
|
922
802
|
});
|
|
923
803
|
}
|
|
924
|
-
/**
|
|
925
|
-
* The `roles.rules` role dropdown's offer set — the ONLY data source for the
|
|
926
|
-
* rule rows' role selector: the built-in `'inherit'` target plus every
|
|
927
|
-
* declared `roles.list` id, in declaration order (a role added/removed on
|
|
928
|
-
* the same page is reflected immediately).
|
|
929
|
-
*/
|
|
930
|
-
function ruleRoleOptions(roles) {
|
|
931
|
-
return [INHERIT_ROLE_ID, ...new Set(roles.list.map((role) => role.id.trim()))];
|
|
932
|
-
}
|
|
933
|
-
/** Project the role rules into editable rows (provider/model classified). */
|
|
934
|
-
function rulesToRows(rules, catalog) {
|
|
935
|
-
return rules.map((rule) => ({
|
|
936
|
-
provider: classifyProvider(rule.provider ?? "", catalog),
|
|
937
|
-
model: classifyModel(rule.provider ?? "", rule.model ?? "", catalog),
|
|
938
|
-
role: rule.role
|
|
939
|
-
}));
|
|
940
|
-
}
|
|
941
|
-
/** Rebuild the role rules from edited rows; empty provider/model drop out. */
|
|
942
|
-
function rowsToRules(rows) {
|
|
943
|
-
return rows.map((row) => ({
|
|
944
|
-
...row.provider === null ? {} : { provider: selectionToRaw(row.provider) },
|
|
945
|
-
...row.model === null ? {} : { model: selectionToRaw(row.model) },
|
|
946
|
-
role: row.role.trim()
|
|
947
|
-
})).filter((rule) => rule.role !== "");
|
|
948
|
-
}
|
|
949
804
|
/** Controller joining Settings reads, writes, and pushed invalidations. */
|
|
950
805
|
var FallbacksSettingsController = class {
|
|
951
806
|
api;
|
|
@@ -965,10 +820,7 @@ window.__ModuleLoader__.load({
|
|
|
965
820
|
configuredProviders: [],
|
|
966
821
|
groups: [],
|
|
967
822
|
catalogEpoch: 0,
|
|
968
|
-
|
|
969
|
-
switchesError: null,
|
|
970
|
-
switches: [],
|
|
971
|
-
subagentPolicy: void 0
|
|
823
|
+
currentSession: void 0
|
|
972
824
|
});
|
|
973
825
|
/** Read guard: a newer load() supersedes an older one's publish. */
|
|
974
826
|
readGeneration = 0;
|
|
@@ -979,22 +831,12 @@ window.__ModuleLoader__.load({
|
|
|
979
831
|
*/
|
|
980
832
|
writeGeneration = 0;
|
|
981
833
|
catalogGeneration = 0;
|
|
982
|
-
switchesGeneration = 0;
|
|
983
834
|
/** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
|
|
984
835
|
namespaces = /* @__PURE__ */ new Map();
|
|
985
|
-
currentSession;
|
|
986
|
-
/**
|
|
987
|
-
* The store's dispose signal (qc3 F-003): created alongside the store
|
|
988
|
-
* lifecycle, threaded into every `session.follow` call, aborted once in
|
|
989
|
-
* {@link dispose} — a follow opening still mid-await at plugin unload
|
|
990
|
-
* (HMR / fiber dispose, or a wedged gateway stream) is cancelled promptly
|
|
991
|
-
* instead of staying open until the mount abort or socket failure.
|
|
992
|
-
*/
|
|
993
|
-
followAbort = new AbortController();
|
|
994
836
|
/**
|
|
995
837
|
* @param api - the Remote namespace faces the store reads (settings
|
|
996
838
|
* describe `writable` + namespace directory, llm provider directory,
|
|
997
|
-
* session model catalog
|
|
839
|
+
* session model catalog) — `ctx.remote` cast once at
|
|
998
840
|
* the mount boundary.
|
|
999
841
|
* @param rpc - the connection's generic RPC caller for the host gateway
|
|
1000
842
|
* channel (`/api`), injected from the connection handle.
|
|
@@ -1042,8 +884,7 @@ window.__ModuleLoader__.load({
|
|
|
1042
884
|
}
|
|
1043
885
|
if ("seeds" in getResult.value) seeds = parseSeedsWire(getResult.value.seeds);
|
|
1044
886
|
}
|
|
1045
|
-
|
|
1046
|
-
this.accept(config, writable, legacyKeys, seeds, subagentPolicy);
|
|
887
|
+
this.accept(config, writable, legacyKeys, seeds);
|
|
1047
888
|
} catch (error) {
|
|
1048
889
|
if (generation !== this.readGeneration) return;
|
|
1049
890
|
if (writeGenerationAtStart !== this.writeGeneration) return;
|
|
@@ -1092,75 +933,13 @@ window.__ModuleLoader__.load({
|
|
|
1092
933
|
}
|
|
1093
934
|
}
|
|
1094
935
|
/**
|
|
1095
|
-
* Record the current session the
|
|
1096
|
-
*
|
|
1097
|
-
* immediately; an idle block only records the id — the section's mount
|
|
1098
|
-
* effect performs the first read.
|
|
1099
|
-
* @param sessionId - the session whose history is summarized; undefined
|
|
1100
|
-
* (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).
|
|
1101
938
|
*/
|
|
1102
939
|
setCurrentSession(sessionId) {
|
|
1103
|
-
if (sessionId === this.currentSession) return;
|
|
1104
|
-
this.currentSession = sessionId;
|
|
1105
|
-
if (this.store.getSnapshot().switchesStatus !== "idle") this.loadSwitches();
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Read the recent-switch summary for the current session (spec §2.5 D-5):
|
|
1109
|
-
* one tail page (`maxMessages` = {@link SWITCHES_HISTORY_PAGE}) read from
|
|
1110
|
-
* the `session/follow` opening snapshot — the host has no unary tail-page
|
|
1111
|
-
* read (`session/page` needs the follow opening frame's cursor), so the
|
|
1112
|
-
* stream is cancelled right after its first frame —
|
|
1113
|
-
* `fallbacks/switch` events extracted newest-first capped at
|
|
1114
|
-
* {@link RECENT_SWITCH_LIMIT}. No current session → honest empty ready
|
|
1115
|
-
* state (no RPC); a read failure lands `switchesStatus: 'error'` and never
|
|
1116
|
-
* touches the settings state (the form keeps editing/saving normally).
|
|
1117
|
-
* @returns nothing; {@link store} carries success or failure.
|
|
1118
|
-
*/
|
|
1119
|
-
async loadSwitches() {
|
|
1120
|
-
const generation = ++this.switchesGeneration;
|
|
1121
|
-
const sessionId = this.currentSession;
|
|
1122
|
-
if (sessionId === void 0) {
|
|
1123
|
-
this.store.update((state) => {
|
|
1124
|
-
state.switchesStatus = "ready";
|
|
1125
|
-
state.switchesError = null;
|
|
1126
|
-
state.switches = [];
|
|
1127
|
-
});
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
940
|
this.store.update((state) => {
|
|
1131
|
-
state.
|
|
1132
|
-
state.switchesError = null;
|
|
941
|
+
state.currentSession = sessionId;
|
|
1133
942
|
});
|
|
1134
|
-
try {
|
|
1135
|
-
const iterator = this.api.session.follow({
|
|
1136
|
-
address: {
|
|
1137
|
-
kind: "session",
|
|
1138
|
-
sessionId
|
|
1139
|
-
},
|
|
1140
|
-
maxMessages: 50
|
|
1141
|
-
}, this.followAbort.signal)[Symbol.asyncIterator]();
|
|
1142
|
-
let first;
|
|
1143
|
-
try {
|
|
1144
|
-
first = await iterator.next();
|
|
1145
|
-
} finally {
|
|
1146
|
-
await iterator.return?.();
|
|
1147
|
-
}
|
|
1148
|
-
if (generation !== this.switchesGeneration) return;
|
|
1149
|
-
if (first.done || first.value.type !== "snapshot") throw new Error("session follow closed before its opening snapshot");
|
|
1150
|
-
const switches = extractRecentSwitches(first.value.records);
|
|
1151
|
-
this.store.update((state) => {
|
|
1152
|
-
state.switchesStatus = "ready";
|
|
1153
|
-
state.switchesError = null;
|
|
1154
|
-
state.switches = switches;
|
|
1155
|
-
});
|
|
1156
|
-
} catch (error) {
|
|
1157
|
-
if (generation !== this.switchesGeneration) return;
|
|
1158
|
-
const wire = error;
|
|
1159
|
-
this.store.update((state) => {
|
|
1160
|
-
state.switchesStatus = "error";
|
|
1161
|
-
state.switchesError = typeof wire?.message === "string" ? wire.message : messageOf(error);
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
943
|
}
|
|
1165
944
|
/**
|
|
1166
945
|
* Persist the full edited configuration through the gateway channel
|
|
@@ -1192,30 +971,6 @@ window.__ModuleLoader__.load({
|
|
|
1192
971
|
}
|
|
1193
972
|
}
|
|
1194
973
|
/**
|
|
1195
|
-
* Explicit "sync to host allowlist" write (plan
|
|
1196
|
-
* role-chain-allowlist-visibility T2): the gateway endpoint merges the
|
|
1197
|
-
* CURRENT allowlist with this plugin's exact chain routes plus the virtual
|
|
1198
|
-
* `FallbacksChain/Auto` route and writes only `allowedModels`. The button
|
|
1199
|
-
* click is the ONLY trigger — the store never syncs on its own.
|
|
1200
|
-
* @returns true when the write landed (the card then reads the fresh
|
|
1201
|
-
* snapshot from the store); false on a refused/failed call, leaving the
|
|
1202
|
-
* last accepted state untouched. A response discarded by the write
|
|
1203
|
-
* generation guard is still a landed write — dispose() is the only
|
|
1204
|
-
* invalidator, so it must not report failure.
|
|
1205
|
-
*/
|
|
1206
|
-
async syncAllowlist() {
|
|
1207
|
-
if (!this.store.getSnapshot().writable) return false;
|
|
1208
|
-
const generation = ++this.writeGeneration;
|
|
1209
|
-
try {
|
|
1210
|
-
const result = await this.rpc.call("/api", "fallbacks/sync-allowlist", { args: {} });
|
|
1211
|
-
if (!result.ok) return false;
|
|
1212
|
-
if (generation === this.writeGeneration) this.acceptWire(result.value);
|
|
1213
|
-
return true;
|
|
1214
|
-
} catch {
|
|
1215
|
-
return false;
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
/**
|
|
1219
974
|
* Reset to composition defaults through the gateway channel
|
|
1220
975
|
* (`/api/fallbacks/reset` — the fallbacks-specific third method; the host
|
|
1221
976
|
* clears the user layer via `settings.replace(ns, {})`, the removal path a
|
|
@@ -1244,15 +999,13 @@ window.__ModuleLoader__.load({
|
|
|
1244
999
|
this.readGeneration += 1;
|
|
1245
1000
|
this.writeGeneration += 1;
|
|
1246
1001
|
this.catalogGeneration += 1;
|
|
1247
|
-
this.switchesGeneration += 1;
|
|
1248
1002
|
this.namespaces = /* @__PURE__ */ new Map();
|
|
1249
|
-
this.followAbort.abort();
|
|
1250
1003
|
}
|
|
1251
1004
|
/**
|
|
1252
|
-
* Unpack one write response (set / reset
|
|
1253
|
-
*
|
|
1254
|
-
*
|
|
1255
|
-
*
|
|
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.
|
|
1256
1009
|
*/
|
|
1257
1010
|
acceptWire(value) {
|
|
1258
1011
|
const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
|
|
@@ -1266,8 +1019,7 @@ window.__ModuleLoader__.load({
|
|
|
1266
1019
|
const wireSeeds = value.seeds;
|
|
1267
1020
|
if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
|
|
1268
1021
|
}
|
|
1269
|
-
|
|
1270
|
-
this.accept(config, true, legacyKeys, seeds, subagentPolicy);
|
|
1022
|
+
this.accept(config, true, legacyKeys, seeds);
|
|
1271
1023
|
}
|
|
1272
1024
|
/**
|
|
1273
1025
|
* Publish a settled load: `status` ready, `writable` from describe, and —
|
|
@@ -1285,7 +1037,7 @@ window.__ModuleLoader__.load({
|
|
|
1285
1037
|
* only when a real config resolved — a transient channel-down keeps the
|
|
1286
1038
|
* last accepted badge state.
|
|
1287
1039
|
*/
|
|
1288
|
-
accept(config, writable, legacyKeys, seeds
|
|
1040
|
+
accept(config, writable, legacyKeys, seeds) {
|
|
1289
1041
|
const parsed = config === void 0 ? void 0 : parseFallbacksConfig(config);
|
|
1290
1042
|
this.store.update((state) => {
|
|
1291
1043
|
state.status = "ready";
|
|
@@ -1294,7 +1046,6 @@ window.__ModuleLoader__.load({
|
|
|
1294
1046
|
state.present = parsed !== void 0;
|
|
1295
1047
|
state.legacyKeys = parsed === void 0 ? state.legacyKeys : legacyKeys;
|
|
1296
1048
|
state.seeds = parsed === void 0 ? state.seeds : seeds;
|
|
1297
|
-
state.subagentPolicy = parsed === void 0 ? state.subagentPolicy : subagentPolicy;
|
|
1298
1049
|
if (parsed !== void 0) state.config = parsed;
|
|
1299
1050
|
state.configuredProviders = configuredProvidersOf(state.providers, this.namespaces);
|
|
1300
1051
|
});
|
|
@@ -1325,17 +1076,6 @@ window.__ModuleLoader__.load({
|
|
|
1325
1076
|
if (controller.store.getSnapshot().catalogStatus === "idle") return;
|
|
1326
1077
|
controller.loadCatalog();
|
|
1327
1078
|
}
|
|
1328
|
-
/**
|
|
1329
|
-
* Refetch the recent-switch summary after `settings/document-updated`
|
|
1330
|
-
* (fallbacks ns) / `connection/reset` only when the status block has already
|
|
1331
|
-
* been read once
|
|
1332
|
-
* (the switches twin of {@link refreshFallbacksIfLoaded}).
|
|
1333
|
-
* @param controller - the fallbacks settings controller.
|
|
1334
|
-
*/
|
|
1335
|
-
function refreshSwitchesIfLoaded(controller) {
|
|
1336
|
-
if (controller.store.getSnapshot().switchesStatus === "idle") return;
|
|
1337
|
-
controller.loadSwitches();
|
|
1338
|
-
}
|
|
1339
1079
|
//#endregion
|
|
1340
1080
|
//#region src/client/locales.ts
|
|
1341
1081
|
/**
|
|
@@ -1370,7 +1110,7 @@ window.__ModuleLoader__.load({
|
|
|
1370
1110
|
"maxSwitchesPerStep.label": "单次请求行走最大切换",
|
|
1371
1111
|
"maxSwitchesPerStep.hint": "超过后停止切换",
|
|
1372
1112
|
"maxSwitchesPerStep.tooltip": "超过后不再切换,以原始错误语义结束本次请求,防止链循环放大延迟。",
|
|
1373
|
-
"virtualOnly.rule": "只有模型选择为 FallbacksChain
|
|
1113
|
+
"virtualOnly.rule": "只有模型选择为 FallbacksChain/<链 id> 的会话(含继承该路由的子代理)会执行降级链;选择真实模型的会话没有插件降级,仅由 llm-retry 处理重试。",
|
|
1374
1114
|
"advanced.label": "高级选项",
|
|
1375
1115
|
"advanced.expand": "展开高级选项",
|
|
1376
1116
|
"advanced.collapse": "收起高级选项",
|
|
@@ -1385,21 +1125,26 @@ window.__ModuleLoader__.load({
|
|
|
1385
1125
|
"chains.selector.modelPlaceholder": "选择 model",
|
|
1386
1126
|
"chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
|
|
1387
1127
|
"chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
|
|
1388
|
-
"mainAgent.label": "主代理",
|
|
1389
1128
|
"subagents.label": "子代理",
|
|
1390
|
-
"
|
|
1391
|
-
"
|
|
1392
|
-
"
|
|
1393
|
-
"
|
|
1394
|
-
"
|
|
1395
|
-
"
|
|
1396
|
-
"
|
|
1397
|
-
"
|
|
1398
|
-
"
|
|
1399
|
-
"
|
|
1400
|
-
"
|
|
1401
|
-
"
|
|
1402
|
-
"
|
|
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": "保存",
|
|
1403
1148
|
"timeSlots.label": "分时槽设置",
|
|
1404
1149
|
"timeSlots.hint": "自上而下第一条命中生效;全时段行固定最后",
|
|
1405
1150
|
"timeSlots.tz.label": "时区(UTC±)",
|
|
@@ -1447,8 +1192,6 @@ window.__ModuleLoader__.load({
|
|
|
1447
1192
|
"allDay.pro": "官方 Pro(deepseek-official/deepseek-v4-pro)",
|
|
1448
1193
|
"allDay.nonconforming": "当前默认模型不合法:请选择官方 Flash 或 Pro 后保存",
|
|
1449
1194
|
"roles.list.label": "角色实体",
|
|
1450
|
-
"roles.list.hint": "先声明角色,规则才能引用",
|
|
1451
|
-
"roles.list.tooltip": "角色 id 须匹配 /^[a-z0-9-]{1,32}$/ 且唯一;\"inherit\" 为保留字,不能用作角色 id。",
|
|
1452
1195
|
"roles.id": "id",
|
|
1453
1196
|
"roles.id.hint": "小写字母/数字/连字符,1–32 字符",
|
|
1454
1197
|
"roles.idPlaceholder": "例如 reviewer",
|
|
@@ -1460,35 +1203,24 @@ window.__ModuleLoader__.load({
|
|
|
1460
1203
|
"roles.seedSource.bundled": "内置",
|
|
1461
1204
|
"roles.seedSource.external": "外部",
|
|
1462
1205
|
"roles.seedSource.user": "用户",
|
|
1463
|
-
"roles.
|
|
1464
|
-
"roles.
|
|
1465
|
-
"roles.
|
|
1466
|
-
"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": "未指定降级链",
|
|
1467
1212
|
"roles.add": "添加角色",
|
|
1468
1213
|
"roles.remove": "删除该角色",
|
|
1469
1214
|
"roles.expand": "展开该角色",
|
|
1470
1215
|
"roles.collapse": "收起该角色",
|
|
1471
|
-
"roles.selector.add": "添加选择器",
|
|
1472
|
-
"roles.rules": "角色规则",
|
|
1473
|
-
"roles.rules.hint": "仅对子代理生效:顺序匹配 provider/model,未命中 → inherit(root 链)",
|
|
1474
|
-
"roles.rules.tooltip": "规则仅对子代理生效(root 请求不匹配规则):命中后走对应角色的链;未命中走内置 inherit(rootChain)。",
|
|
1475
|
-
"roles.rule.provider": "provider",
|
|
1476
|
-
"roles.rule.provider.any": "任意",
|
|
1477
|
-
"roles.rule.model": "model",
|
|
1478
|
-
"roles.rule.model.any": "任意",
|
|
1479
|
-
"roles.rule.role": "角色",
|
|
1480
|
-
"roles.rule.role.inherit": "inherit(内置:root 链)",
|
|
1481
|
-
"roles.rule.roleSelectPlaceholder": "选择角色",
|
|
1482
|
-
"roles.rule.roleUndeclared.short": "(未声明)",
|
|
1483
|
-
"roles.addRule": "添加规则",
|
|
1484
|
-
"roles.removeRule": "删除该规则",
|
|
1485
1216
|
"validation.blocked": "配置校验未通过,保存被拦截:",
|
|
1486
1217
|
"validation.roleIdFormat": "角色 id \"{id}\" 不符合格式 /^[a-z0-9-]{1,32}$/",
|
|
1487
1218
|
"validation.roleIdReserved": "\"inherit\" 为保留角色 id,不能用于角色实体",
|
|
1488
1219
|
"validation.roleIdDuplicate": "角色 id \"{id}\" 重复",
|
|
1489
|
-
"validation.
|
|
1490
|
-
"validation.
|
|
1491
|
-
"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": "默认降级链不能为空:请至少添加一条选择器。",
|
|
1492
1224
|
"validation.allDayRequired": "默认模型必须二选一:官方 Flash 或 Pro",
|
|
1493
1225
|
"validation.slotChainRequired": "分时槽未配置模型:请至少添加一条链选择器",
|
|
1494
1226
|
"validation.slotWindow": "分时槽开始/结束时间须为 HH:mm 格式",
|
|
@@ -1498,7 +1230,7 @@ window.__ModuleLoader__.load({
|
|
|
1498
1230
|
"validation.slotPresetDuplicate": "预设 \"{preset}\" 已存在:每个预设只能添加一行",
|
|
1499
1231
|
"validation.slotPresetFrozen": "预设窗口是冻结代码常量:预设行不能携带 start/end/days",
|
|
1500
1232
|
"validation.selector": "选择器 \"{entry}\" 非法:{message}",
|
|
1501
|
-
"legacy.banner": "
|
|
1233
|
+
"legacy.banner": "检测到旧格式字段({keys}):已自动升级为命名链——原默认降级链成为第 0 条链「默认链」,角色与人格保持不变,旧的角色数组链已置为「未指定」(可在下方重新指定)。保存一次即可固化;插件不会主动改写你的配置文件。",
|
|
1502
1234
|
"catalog.empty": "暂无可用模型:请先在模型页添加模型,添加后此处将自动可选。",
|
|
1503
1235
|
"catalog.error": "模型目录读取失败:{message}",
|
|
1504
1236
|
"catalog.partial": "部分 provider 模型查询失败:{message}",
|
|
@@ -1506,24 +1238,9 @@ window.__ModuleLoader__.load({
|
|
|
1506
1238
|
"catalog.outside.tooltip": "不在当前模型目录,可保留原值并保存;新增条目仅可从目录选择。",
|
|
1507
1239
|
"catalog.outside.short": " (目录外)",
|
|
1508
1240
|
"catalog.unconfigured.short": " (未配置)",
|
|
1509
|
-
"status.title": "运行状态(只读)",
|
|
1510
|
-
"status.switches.label": "最近切换:",
|
|
1511
|
-
"status.switches.empty": "本会话暂无 fallback 切换。",
|
|
1512
|
-
"status.switches.error": "切换历史读取失败:{message}",
|
|
1513
|
-
"status.switches.compact": "最近 {count} 次 · {from} → {to}({role} · {reason})",
|
|
1514
|
-
"status.switches.compact.roleInject": "最近 {count} 次 · {role} → {to}({reason})",
|
|
1515
1241
|
"status.switches.reason.trigger-code": "触发失败码",
|
|
1516
1242
|
"status.switches.reason.always-cap": "always 模式上限",
|
|
1517
1243
|
"status.switches.reason.role-inject": "角色注入",
|
|
1518
|
-
"general.title": "模型故障降级",
|
|
1519
|
-
"general.enabled": "已启用",
|
|
1520
|
-
"general.disabled": "未启用",
|
|
1521
|
-
"general.unknown": "未知",
|
|
1522
|
-
"general.unavailable": "状态通道暂不可达",
|
|
1523
|
-
"general.switch": "最近切换:{from} → {to}({role} · {reason})",
|
|
1524
|
-
"general.switch.roleInject": "最近切换:{role} → {to}({reason})",
|
|
1525
|
-
"general.switch.empty": "本会话暂无切换",
|
|
1526
|
-
"general.error": "状态读取失败:{message}",
|
|
1527
1244
|
"chat.switch.title": "模型已降级",
|
|
1528
1245
|
"chat.switch.summary": "{from} → {to}({reason})",
|
|
1529
1246
|
"chat.switch.summary.roleInject": "({reason})",
|
|
@@ -1561,7 +1278,7 @@ window.__ModuleLoader__.load({
|
|
|
1561
1278
|
"maxSwitchesPerStep.label": "Max switches per request walk",
|
|
1562
1279
|
"maxSwitchesPerStep.hint": "Stops switching beyond the cap",
|
|
1563
1280
|
"maxSwitchesPerStep.tooltip": "Beyond this no further switch happens and the request ends with the original error semantics, preventing chain loops from amplifying latency.",
|
|
1564
|
-
"virtualOnly.rule": "Only sessions whose model selection is FallbacksChain
|
|
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.",
|
|
1565
1282
|
"advanced.label": "Advanced options",
|
|
1566
1283
|
"advanced.expand": "Expand advanced options",
|
|
1567
1284
|
"advanced.collapse": "Collapse advanced options",
|
|
@@ -1576,21 +1293,26 @@ window.__ModuleLoader__.load({
|
|
|
1576
1293
|
"chains.selector.modelPlaceholder": "Select model",
|
|
1577
1294
|
"chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
|
|
1578
1295
|
"chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
|
|
1579
|
-
"mainAgent.label": "Main agent",
|
|
1580
1296
|
"subagents.label": "Subagents",
|
|
1581
|
-
"
|
|
1582
|
-
"
|
|
1583
|
-
"
|
|
1584
|
-
"
|
|
1585
|
-
"
|
|
1586
|
-
"
|
|
1587
|
-
"
|
|
1588
|
-
"
|
|
1589
|
-
"
|
|
1590
|
-
"
|
|
1591
|
-
"
|
|
1592
|
-
"
|
|
1593
|
-
"
|
|
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",
|
|
1594
1316
|
"timeSlots.label": "Time slots",
|
|
1595
1317
|
"timeSlots.hint": "First match from top to bottom wins; the all-day row is always last",
|
|
1596
1318
|
"timeSlots.tz.label": "Timezone (UTC±)",
|
|
@@ -1638,8 +1360,7 @@ window.__ModuleLoader__.load({
|
|
|
1638
1360
|
"allDay.pro": "Official Pro (deepseek-official/deepseek-v4-pro)",
|
|
1639
1361
|
"allDay.nonconforming": "The current default model is not valid: pick official Flash or Pro before saving",
|
|
1640
1362
|
"roles.list.label": "Declared roles",
|
|
1641
|
-
"roles.list.hint": "
|
|
1642
|
-
"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 指定降级链",
|
|
1643
1364
|
"roles.id": "ID",
|
|
1644
1365
|
"roles.id.hint": "lowercase letters, digits, hyphens; 1–32 chars",
|
|
1645
1366
|
"roles.idPlaceholder": "e.g. reviewer",
|
|
@@ -1651,35 +1372,23 @@ window.__ModuleLoader__.load({
|
|
|
1651
1372
|
"roles.seedSource.bundled": "bundled",
|
|
1652
1373
|
"roles.seedSource.external": "external",
|
|
1653
1374
|
"roles.seedSource.user": "User",
|
|
1654
|
-
"roles.
|
|
1655
|
-
"roles.
|
|
1656
|
-
"roles.
|
|
1657
|
-
"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",
|
|
1658
1380
|
"roles.add": "Add role",
|
|
1659
1381
|
"roles.remove": "Remove this role",
|
|
1660
1382
|
"roles.expand": "Expand this role",
|
|
1661
1383
|
"roles.collapse": "Collapse this role",
|
|
1662
|
-
"roles.selector.add": "Add selector",
|
|
1663
|
-
"roles.rules": "Role rules",
|
|
1664
|
-
"roles.rules.hint": "Subagents only: matches provider/model in order; no match → inherit (root chain)",
|
|
1665
|
-
"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).",
|
|
1666
|
-
"roles.rule.provider": "provider",
|
|
1667
|
-
"roles.rule.provider.any": "Any",
|
|
1668
|
-
"roles.rule.model": "model",
|
|
1669
|
-
"roles.rule.model.any": "Any",
|
|
1670
|
-
"roles.rule.role": "role",
|
|
1671
|
-
"roles.rule.role.inherit": "inherit (built-in: root chain)",
|
|
1672
|
-
"roles.rule.roleSelectPlaceholder": "Select role",
|
|
1673
|
-
"roles.rule.roleUndeclared.short": " (undeclared)",
|
|
1674
|
-
"roles.addRule": "Add rule",
|
|
1675
|
-
"roles.removeRule": "Remove this rule",
|
|
1676
1384
|
"validation.blocked": "Configuration validation failed; save was blocked: ",
|
|
1677
1385
|
"validation.roleIdFormat": "Role id \"{id}\" does not match /^[a-z0-9-]{1,32}$/",
|
|
1678
1386
|
"validation.roleIdReserved": "\"inherit\" is a reserved role id and cannot be declared",
|
|
1679
1387
|
"validation.roleIdDuplicate": "Duplicate role id \"{id}\"",
|
|
1680
|
-
"validation.
|
|
1681
|
-
"validation.
|
|
1682
|
-
"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.",
|
|
1683
1392
|
"validation.allDayRequired": "The default model must be exactly one official model (Flash or Pro)",
|
|
1684
1393
|
"validation.slotChainRequired": "Time-slot row has no models: add at least one chain entry",
|
|
1685
1394
|
"validation.slotWindow": "Time-slot start/end must use HH:mm format",
|
|
@@ -1689,7 +1398,7 @@ window.__ModuleLoader__.load({
|
|
|
1689
1398
|
"validation.slotPresetDuplicate": "Preset \"{preset}\" already exists: at most one row per preset",
|
|
1690
1399
|
"validation.slotPresetFrozen": "Preset windows are frozen code constants: a preset row cannot carry start/end/days",
|
|
1691
1400
|
"validation.selector": "Invalid selector \"{entry}\": {message}",
|
|
1692
|
-
"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.",
|
|
1693
1402
|
"catalog.empty": "No models yet: add a model on the Models page first; options will appear here automatically.",
|
|
1694
1403
|
"catalog.error": "Model catalog read failed: {message}",
|
|
1695
1404
|
"catalog.partial": "Some provider model lookups failed: {message}",
|
|
@@ -1697,24 +1406,9 @@ window.__ModuleLoader__.load({
|
|
|
1697
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).",
|
|
1698
1407
|
"catalog.outside.short": " (outside catalog)",
|
|
1699
1408
|
"catalog.unconfigured.short": " (not configured)",
|
|
1700
|
-
"status.title": "Runtime status (read-only)",
|
|
1701
|
-
"status.switches.label": "Recent switches: ",
|
|
1702
|
-
"status.switches.empty": "No fallback switches in this session yet.",
|
|
1703
|
-
"status.switches.error": "Switch history read failed: {message}",
|
|
1704
|
-
"status.switches.compact": "last {count} · {from} → {to} ({role} · {reason})",
|
|
1705
|
-
"status.switches.compact.roleInject": "last {count} · {role} → {to} ({reason})",
|
|
1706
1409
|
"status.switches.reason.trigger-code": "trigger code",
|
|
1707
1410
|
"status.switches.reason.always-cap": "always-mode cap",
|
|
1708
1411
|
"status.switches.reason.role-inject": "role inject",
|
|
1709
|
-
"general.title": "Model failover",
|
|
1710
|
-
"general.enabled": "Enabled",
|
|
1711
|
-
"general.disabled": "Disabled",
|
|
1712
|
-
"general.unknown": "Unknown",
|
|
1713
|
-
"general.unavailable": "Status channel unavailable",
|
|
1714
|
-
"general.switch": "Last switch: {from} → {to} ({role} · {reason})",
|
|
1715
|
-
"general.switch.roleInject": "Last switch: {role} → {to} ({reason})",
|
|
1716
|
-
"general.switch.empty": "No switches this session",
|
|
1717
|
-
"general.error": "Status read failed: {message}",
|
|
1718
1412
|
"chat.switch.title": "Model downgraded",
|
|
1719
1413
|
"chat.switch.summary": "{from} → {to} ({reason})",
|
|
1720
1414
|
"chat.switch.summary.roleInject": "({reason})",
|
|
@@ -1732,8 +1426,8 @@ window.__ModuleLoader__.load({
|
|
|
1732
1426
|
/** The settings section's dictionary namespace. */
|
|
1733
1427
|
const NS = "fallbacks";
|
|
1734
1428
|
/**
|
|
1735
|
-
* Reason → locale key map for switch summaries (
|
|
1736
|
-
*
|
|
1429
|
+
* Reason → locale key map for switch summaries (shared by the conversation
|
|
1430
|
+
* node). All
|
|
1737
1431
|
* reasons resolve from ONE key family (`status.switches.reason.*`) — the
|
|
1738
1432
|
* shared reason vocabulary must not mix dictionary families (qc1 F-004). The
|
|
1739
1433
|
* session log is durable and forward-compatible: a reason value outside the
|
|
@@ -1767,13 +1461,13 @@ window.__ModuleLoader__.load({
|
|
|
1767
1461
|
}
|
|
1768
1462
|
//#endregion
|
|
1769
1463
|
//#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/FallbacksCard.module.css.mjs
|
|
1770
|
-
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 > ._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";
|
|
1771
|
-
const tagId$
|
|
1772
|
-
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) {
|
|
1773
1467
|
const tag = document.createElement("style");
|
|
1774
1468
|
tag.dataset.plugin = "dsh-llm-fallbacks";
|
|
1775
|
-
tag.dataset.pluginCss = tagId$
|
|
1776
|
-
tag.textContent = css$
|
|
1469
|
+
tag.dataset.pluginCss = tagId$2;
|
|
1470
|
+
tag.textContent = css$2;
|
|
1777
1471
|
document.head.appendChild(tag);
|
|
1778
1472
|
}
|
|
1779
1473
|
var FallbacksCard_module_css_default = {
|
|
@@ -1860,92 +1554,71 @@ window.__ModuleLoader__.load({
|
|
|
1860
1554
|
"policyStatusLive": "_579f87fb_policyStatusLive",
|
|
1861
1555
|
"policyStatusLabel": "_d4b67a81_policyStatusLabel",
|
|
1862
1556
|
"policyStatusLine": "_278bb4b3_policyStatusLine",
|
|
1863
|
-
"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"
|
|
1864
1576
|
};
|
|
1865
1577
|
//#endregion
|
|
1866
1578
|
//#region src/client/FallbacksCard.tsx
|
|
1867
1579
|
/**
|
|
1868
|
-
* Fallbacks settings card — the `fallbacks` plugin card on the web
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
* the upstream bash/agent-loop/web-search cards and the advisor card, in
|
|
1872
|
-
* registration order); owner props are empty and all data flows
|
|
1873
|
-
* through {@link FallbacksSettingsController}.
|
|
1874
|
-
*
|
|
1875
|
-
* FLAT rebuild (plan fallbacks-web-ux-alignment T3, the advisor PR #98
|
|
1876
|
-
* pattern): the collapsible card chrome is gone — no header button, no
|
|
1877
|
-
* rotating chevron, no dirty "unsaved" pill, no card-local open state, no
|
|
1878
|
-
* bordered `<li>` box. The page already renders the plugin title/description
|
|
1879
|
-
* above the card (the `locale/*.json` meta files the host resolves per UI
|
|
1880
|
-
* language), so the notices, the form, and the section actions directly
|
|
1881
|
-
* beneath it in the official flat settings-form language (reference §3):
|
|
1882
|
-
* fields at
|
|
1883
|
-
* 12px vertical padding with 0.5px hairline separators, 34px inputs on the
|
|
1884
|
-
* 0.5px l4 border, per-section Discard + Save pairs on the section
|
|
1885
|
-
* headings. The card is ALWAYS open —
|
|
1886
|
-
* nothing needs card-level disclosure state; the degraded /
|
|
1887
|
-
* error / read-only / migration notices render flat atop the form (the
|
|
1888
|
-
* unavailable notice stays always-visible per KD-G5 — the documented
|
|
1889
|
-
* divergence from the advisor, whose unavailable card renders nothing: the
|
|
1890
|
-
* fallbacks skeleton stays the USABLE last-known form and saves are
|
|
1891
|
-
* attempted).
|
|
1892
|
-
*
|
|
1893
|
-
* The config-level `enabled` switch is REMOVED (T2, breaking): there is no
|
|
1894
|
-
* checkbox row and no `enabled.off` hiding — all fields render
|
|
1895
|
-
* unconditionally; the Plugins-page row toggle is the master switch and the
|
|
1896
|
-
* runtime gates read content presence (`isFallbackActive`).
|
|
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`).
|
|
1897
1583
|
*
|
|
1898
|
-
*
|
|
1899
|
-
*
|
|
1900
|
-
*
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1907
|
-
*
|
|
1908
|
-
* store). Violations render live under their OWNING section heading, and a
|
|
1909
|
-
* store write failure renders under the section whose Save was last
|
|
1910
|
-
* clicked (`lastSaveSection`). Discard is kept — our staged edits survive
|
|
1911
|
-
* refresh, the advisor's documented divergence from the official
|
|
1912
|
-
* no-discard form — a pure client-side revert of THAT section's editors to
|
|
1913
|
-
* the last accepted config.
|
|
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).
|
|
1914
1594
|
*
|
|
1915
|
-
* The
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
*
|
|
1919
|
-
*
|
|
1920
|
-
* dropdown bound to the declared ids + the built-in `inherit`, same-page
|
|
1921
|
-
* live). Row-level disclosures that are CONTENT keep their expand/collapse:
|
|
1922
|
-
* the time-slot row cards and the role entity cards (with the seeded
|
|
1923
|
-
* persona briefs) stay collapsible — a read-only view forces them open so
|
|
1924
|
-
* the configs stay reachable. The 主代理 / 子代理 / 高级选项 group labels
|
|
1925
|
-
* are semantic `<h2>` headings (T1); 高级选项 is a content-level disclosure
|
|
1926
|
-
* collapsed by default (T3 — same expand/collapse contract as the rows, a
|
|
1927
|
-
* read-only view forces it open).
|
|
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.
|
|
1928
1600
|
*
|
|
1929
|
-
* Validation: id format/
|
|
1930
|
-
*
|
|
1931
|
-
*
|
|
1932
|
-
* + inline red borders / hints (never touching the store error path); a
|
|
1933
|
-
* non-empty `state.legacyKeys` renders the migration banner at the top.
|
|
1934
|
-
* The reset-to-defaults affordance is GONE from the card (PR #62 UX
|
|
1935
|
-
* round 3) — the gateway RPC `fallbacks/reset` and the store
|
|
1936
|
-
* `resetToDefaults()` stay as host APIs, only the card UI was removed.
|
|
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.
|
|
1937
1604
|
*
|
|
1938
|
-
*
|
|
1939
|
-
*
|
|
1940
|
-
*
|
|
1941
|
-
* Presentation is ui-primitives-free (T4, advisor parity + reference §4
|
|
1942
|
-
* risk): Button/Tooltip and the five icon imports are replaced by
|
|
1943
|
-
* self-drawn styled elements — inline SVG icons (plus / trash / chevron /
|
|
1944
|
-
* grip) and the CSS `data-tip` tooltip bubble (the card's existing
|
|
1945
|
-
* icon-button pattern); every color resolves through a `--dsw-alias-*`
|
|
1946
|
-
* 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).
|
|
1947
1608
|
*/
|
|
1948
|
-
|
|
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
|
+
}
|
|
1949
1622
|
const SLOT_PRESET_IDS = [
|
|
1950
1623
|
"liang-peak",
|
|
1951
1624
|
"liang-valley",
|
|
@@ -2089,33 +1762,8 @@ window.__ModuleLoader__.load({
|
|
|
2089
1762
|
const offset = tzUtcOffset(tz);
|
|
2090
1763
|
return offset === "" ? tz : `${tz} (${offset})`;
|
|
2091
1764
|
}
|
|
2092
|
-
/** Persist tz: presets lock UTC+8; custom-only uses the host zone; else keep the accepted value. */
|
|
2093
|
-
function resolvedSlotTz(rows, fallback) {
|
|
2094
|
-
if (rows.some((row) => row.kind === "preset")) return "Asia/Shanghai";
|
|
2095
|
-
if (rows.some((row) => row.kind === "custom")) return hostTimeZone();
|
|
2096
|
-
return fallback === "" ? "Asia/Shanghai" : fallback;
|
|
2097
|
-
}
|
|
2098
1765
|
/** Strict 24h `HH:mm` — the resolver's HHMM_RE twin (drift-guarded by the gateway reject-on-save). */
|
|
2099
1766
|
const HHMM_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
2100
|
-
/**
|
|
2101
|
-
* The 默认模型 value for a chain: the official id when the chain TAIL
|
|
2102
|
-
* is that model (Flash or Pro XOR — leading 默认降级链 entries allowed);
|
|
2103
|
-
* `''` for an empty chain or a chain whose last entry is not official
|
|
2104
|
-
* (the panel reads back unselected and save validation blocks the value).
|
|
2105
|
-
*/
|
|
2106
|
-
function allDayModelOf(chain) {
|
|
2107
|
-
const tail = chain.length >= 1 ? chain[chain.length - 1] : void 0;
|
|
2108
|
-
return tail !== void 0 && isOfficialAllDayId(tail) ? tail : "";
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* The 默认降级链 editor row: the leading entries BEFORE the official tail
|
|
2112
|
-
* (Flash or Pro), or the whole chain while the tail is not official (the
|
|
2113
|
-
* draft rides the accepted value until a 默认模型 pick).
|
|
2114
|
-
*/
|
|
2115
|
-
function allDayChainRowOf(chain, catalog) {
|
|
2116
|
-
return rootChainToRows(allDayModelOf(chain) === "" ? chain : chain.slice(0, -1), catalog)[0];
|
|
2117
|
-
}
|
|
2118
|
-
/** Split scalars from the row editors (rootChain / role entities / role rules). */
|
|
2119
1767
|
function scalarsOf(config) {
|
|
2120
1768
|
return {
|
|
2121
1769
|
triggerCodes: [...config.triggerCodes],
|
|
@@ -2125,52 +1773,7 @@ window.__ModuleLoader__.load({
|
|
|
2125
1773
|
tz: config.tz ?? "Asia/Shanghai"
|
|
2126
1774
|
};
|
|
2127
1775
|
}
|
|
2128
|
-
/**
|
|
2129
|
-
* Assemble the full config the row editors + scalars describe. The rebuilt
|
|
2130
|
-
* `roles.list` comes from the rows, with the schema-reserved
|
|
2131
|
-
* `prompt`/`permissions` merged back from the last accepted config by role
|
|
2132
|
-
* id (see {@link mergeRoleExtras}) so a save never silently drops them
|
|
2133
|
-
* (T2 reviewer minor #2). `presets` (spec §9.4) follows the same rule at
|
|
2134
|
-
* the top level: no presets UI this iteration (R-001 re-defer), so the
|
|
2135
|
-
* draft carries the accepted value through untouched — a clean draft stays
|
|
2136
|
-
* equal to the accepted config and a save never drops the key.
|
|
2137
|
-
*
|
|
2138
|
-
* All-day: rootChain is composed from the 默认降级链 editor's leading
|
|
2139
|
-
* selectors plus the 默认模型 tail (exactly one official model — Flash or
|
|
2140
|
-
* Pro XOR). While no tail is selected the ACCEPTED chain rides through
|
|
2141
|
-
* untouched. `timeSlots` is rebuilt from the slot rows every render. `tz`
|
|
2142
|
-
* is a card scalar: preset rows lock it to Asia/Shanghai; custom rows
|
|
2143
|
-
* follow the selected timezone.
|
|
2144
|
-
*/
|
|
2145
|
-
function assembleConfig(scalars, allDayModel, acceptedRootChain, allDayChainRow, roleRows, ruleRows, originalRoles, presets, timeSlotRows) {
|
|
2146
|
-
const list = mergeRoleExtras(roleRows, originalRoles);
|
|
2147
|
-
const trailingChain = rowsToRootChain([allDayChainRow]);
|
|
2148
|
-
const tz = resolvedSlotTz(timeSlotRows, scalars.tz);
|
|
2149
|
-
return {
|
|
2150
|
-
triggerCodes: [...scalars.triggerCodes],
|
|
2151
|
-
rootChain: allDayModel === "" ? [...acceptedRootChain] : [...trailingChain, allDayModel],
|
|
2152
|
-
roles: {
|
|
2153
|
-
list,
|
|
2154
|
-
rules: rowsToRules(ruleRows)
|
|
2155
|
-
},
|
|
2156
|
-
cooldownMs: scalars.cooldownMs,
|
|
2157
|
-
revertPolicy: scalars.revertPolicy,
|
|
2158
|
-
maxSwitchesPerStep: scalars.maxSwitchesPerStep,
|
|
2159
|
-
...presets === void 0 ? {} : { presets },
|
|
2160
|
-
timeSlots: rowsToTimeSlots(timeSlotRows),
|
|
2161
|
-
tz
|
|
2162
|
-
};
|
|
2163
|
-
}
|
|
2164
|
-
/**
|
|
2165
|
-
* Badge label for one row's provenance (plan role-card-seeded-ux): every
|
|
2166
|
-
* row states where its seed state comes from. The three reserved labels
|
|
2167
|
-
* localize — `bundled`, `external` (the shared unnamed-producer slice;
|
|
2168
|
-
* plan seeds-source-and-persona-width), and the operator's own rows —
|
|
2169
|
-
* while a live seed declared under a registered set name renders that
|
|
2170
|
-
* name VERBATIM (case-preserved — no case-folding). `null` = no badge: a
|
|
2171
|
-
* currently-seeded row whose wire entry lacks `source` (gateway version
|
|
2172
|
-
* skew) — the degradation path, never an error.
|
|
2173
|
-
*/
|
|
1776
|
+
/** Badge label for one row's provenance; `null` = no badge (version skew). */
|
|
2174
1777
|
function seedBadgeLabel(seed, t) {
|
|
2175
1778
|
if (seed === void 0) return t("roles.seedSource.user");
|
|
2176
1779
|
if (seed.source === void 0) return null;
|
|
@@ -2180,105 +1783,66 @@ window.__ModuleLoader__.load({
|
|
|
2180
1783
|
return seed.source;
|
|
2181
1784
|
}
|
|
2182
1785
|
/**
|
|
2183
|
-
*
|
|
2184
|
-
*
|
|
2185
|
-
*
|
|
2186
|
-
*
|
|
2187
|
-
* rootChain and role chains. Returns one localized message per violation,
|
|
2188
|
-
* bucketed by the section that owns the offending field (主代理: allDay /
|
|
2189
|
-
* timeSlots / slot* / tz / default model / default chain; 子代理: role* /
|
|
2190
|
-
* rule*; 高级选项: trigger / cooldown / revert —
|
|
2191
|
-
* the scalars are never validated, so the advanced bucket stays empty
|
|
2192
|
-
* today). A non-empty bucket blocks THAT section's Save write only
|
|
2193
|
-
* (per-section saves, plan fallbacks-card-section-ux) — a sibling section
|
|
2194
|
-
* with a clean bucket still saves. `persona` is free text and never
|
|
2195
|
-
* validated.
|
|
2196
|
-
*
|
|
2197
|
-
* `seedInfo` is the live trimmed-id → seed-entry map derived from
|
|
2198
|
-
* `state.seeds` (spec §9.4): the empty-chain block relaxes for seeded ids
|
|
2199
|
-
* only (spec §9.6 / AC-3 — a seeded role's chain is legitimately empty by
|
|
2200
|
-
* design, R4, and the row must stay persistable); non-seeded behavior is
|
|
2201
|
-
* 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.
|
|
2202
1790
|
*/
|
|
2203
|
-
function validateDraft(
|
|
2204
|
-
const
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
if (
|
|
2212
|
-
|
|
2213
|
-
if (
|
|
2214
|
-
|
|
2215
|
-
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 {
|
|
2216
1803
|
parseSelector(entry);
|
|
2217
1804
|
} catch (error) {
|
|
2218
|
-
|
|
1805
|
+
bucket.push(t("validation.selector", {
|
|
2219
1806
|
entry,
|
|
2220
1807
|
message: error.message
|
|
2221
1808
|
}));
|
|
2222
1809
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
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"));
|
|
2245
|
-
if (row.days !== void 0 && row.days.some((day) => !Number.isInteger(day) || day < 0 || day > 6)) errors.main.push(t("validation.slotDays"));
|
|
2246
|
-
}
|
|
2247
|
-
for (const entry of row.chain) try {
|
|
2248
|
-
parseSelector(entry);
|
|
2249
|
-
} catch (error) {
|
|
2250
|
-
errors.main.push(t("validation.selector", {
|
|
2251
|
-
entry,
|
|
2252
|
-
message: error.message
|
|
2253
|
-
}));
|
|
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"));
|
|
2254
1831
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
const
|
|
2258
|
-
for (const
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
* / duplicate) — drives the inline red border on the offending id input
|
|
2264
|
-
* (live: the border tracks the draft, matching the live Save disabled
|
|
2265
|
-
* gate). Derived once per render into a Set (qc3 F-3): a duplicate scan
|
|
2266
|
-
* inside the render loop would be O(N²) per row; here the whole derivation
|
|
2267
|
-
* is O(N) and each row's check is a single Set lookup. Selector errors stay
|
|
2268
|
-
* on the banner only (plan Task 3 inline-scope rule).
|
|
2269
|
-
*/
|
|
2270
|
-
function collectInvalidRoleIds(rows) {
|
|
2271
|
-
const counts = /* @__PURE__ */ new Map();
|
|
2272
|
-
for (const row of rows) {
|
|
2273
|
-
const id = row.id.trim();
|
|
2274
|
-
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
2275
|
-
}
|
|
2276
|
-
const invalid = /* @__PURE__ */ new Set();
|
|
2277
|
-
for (const row of rows) {
|
|
2278
|
-
const id = row.id.trim();
|
|
2279
|
-
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);
|
|
2280
1840
|
}
|
|
2281
|
-
return
|
|
1841
|
+
return {
|
|
1842
|
+
chains: chainBuckets,
|
|
1843
|
+
roles: roleErrors,
|
|
1844
|
+
advanced: []
|
|
1845
|
+
};
|
|
2282
1846
|
}
|
|
2283
1847
|
/** Parse a number input, clamped to a non-negative integer. */
|
|
2284
1848
|
function parseCount(raw) {
|
|
@@ -2335,60 +1899,6 @@ window.__ModuleLoader__.load({
|
|
|
2335
1899
|
});
|
|
2336
1900
|
}
|
|
2337
1901
|
/**
|
|
2338
|
-
* The virtual all-fallbacks route the host carve-out recognizes (features.md):
|
|
2339
|
-
* a subagent spawned on it is not allowlist-filtered, so it is the
|
|
2340
|
-
* recommendation when the allowlist does not already carry it.
|
|
2341
|
-
*/
|
|
2342
|
-
const VIRTUAL_ALL_ROUTE = {
|
|
2343
|
-
provider: "FallbacksChain",
|
|
2344
|
-
model: "Auto"
|
|
2345
|
-
};
|
|
2346
|
-
/**
|
|
2347
|
-
* Parse one raw selector into an exact route; wildcard and unparseable
|
|
2348
|
-
* entries resolve to `undefined` (they are not exact routes — never expanded,
|
|
2349
|
-
* never counted). The selector parser is the single grammar authority.
|
|
2350
|
-
*/
|
|
2351
|
-
function exactRouteOf(raw) {
|
|
2352
|
-
let parsed;
|
|
2353
|
-
try {
|
|
2354
|
-
parsed = parseSelector(raw);
|
|
2355
|
-
} catch {
|
|
2356
|
-
return;
|
|
2357
|
-
}
|
|
2358
|
-
return parsed.model === void 0 ? void 0 : {
|
|
2359
|
-
provider: parsed.provider,
|
|
2360
|
-
model: parsed.model
|
|
2361
|
-
};
|
|
2362
|
-
}
|
|
2363
|
-
/**
|
|
2364
|
-
* The whole chain as one line (plan fallbacks-chain-reorder T2, user request):
|
|
2365
|
-
* every non-blank entry in walk order joined with `>`, or `undefined` for an
|
|
2366
|
-
* empty/blank chain so the caller keeps its own summary (a role's raw strategy
|
|
2367
|
-
* token). The card ellipsizes long summaries; the full string rides `title`.
|
|
2368
|
-
*
|
|
2369
|
-
* With `allowedModels` (host policy enabled) exact entries the policy cannot
|
|
2370
|
-
* use are dropped, so the title tells the truth about what will run; wildcard
|
|
2371
|
-
* entries stay (their resolved routes are unknown client-side) and an
|
|
2372
|
-
* unparseable entry is kept rather than hidden.
|
|
2373
|
-
*/
|
|
2374
|
-
function chainSummary(selectors, allowedModels) {
|
|
2375
|
-
const entries = selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
|
|
2376
|
-
const visible = allowedModels === void 0 ? entries : entries.filter((entry) => {
|
|
2377
|
-
const route = exactRouteOf(entry);
|
|
2378
|
-
return route === void 0 || routeOnAllowlist(route, allowedModels);
|
|
2379
|
-
});
|
|
2380
|
-
return visible.length === 0 ? void 0 : visible.join(" > ");
|
|
2381
|
-
}
|
|
2382
|
-
/** How many selected role-chain exact entries the host allowlist does not carry. */
|
|
2383
|
-
function offAllowlistEntryCount(roles, allowedModels) {
|
|
2384
|
-
let count = 0;
|
|
2385
|
-
for (const role of roles) for (const entry of role.chain ?? []) {
|
|
2386
|
-
const route = exactRouteOf(entry);
|
|
2387
|
-
if (route !== void 0 && !routeOnAllowlist(route, allowedModels)) count += 1;
|
|
2388
|
-
}
|
|
2389
|
-
return count;
|
|
2390
|
-
}
|
|
2391
|
-
/**
|
|
2392
1902
|
* Pure chain reorder (plan fallbacks-chain-reorder T1): a copy of `list` with
|
|
2393
1903
|
* the entry at `from` re-inserted at `to`; an out-of-range or same-position
|
|
2394
1904
|
* move returns `list` untouched (the end-clamping the keyboard and the drop
|
|
@@ -2409,9 +1919,8 @@ window.__ModuleLoader__.load({
|
|
|
2409
1919
|
* icon. The visible provider/model field labels are gone — the two selects
|
|
2410
1920
|
* carry their accessible name on `aria-label` (the visible labels repeated on
|
|
2411
1921
|
* every row and cost a whole line).
|
|
2412
|
-
* The GUI never offers a `provider/*` wildcard
|
|
2413
|
-
*
|
|
2414
|
-
* 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
|
|
2415
1924
|
* renders with the legacy-conversion hint and an enabled model select:
|
|
2416
1925
|
* picking a model converts the row to an exact entry (the patch carries
|
|
2417
1926
|
* `wildcard: false`). The provider options are the catalog providers
|
|
@@ -2653,11 +2162,9 @@ window.__ModuleLoader__.load({
|
|
|
2653
2162
|
});
|
|
2654
2163
|
}
|
|
2655
2164
|
/**
|
|
2656
|
-
* Render the Fallbacks settings card inside the plugin-config section
|
|
2657
|
-
*
|
|
2658
|
-
* the page's plugin title/description
|
|
2659
|
-
* @param props - slot-delivered injected dependencies and the synthesized t seat.
|
|
2660
|
-
* @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.
|
|
2661
2168
|
*/
|
|
2662
2169
|
function FallbacksCard({ controller, useSnapshot, t }) {
|
|
2663
2170
|
const state = useSnapshot((snapshot) => snapshot);
|
|
@@ -2665,47 +2172,39 @@ window.__ModuleLoader__.load({
|
|
|
2665
2172
|
const snapshot = controller.store.getSnapshot();
|
|
2666
2173
|
if (snapshot.status === "idle") controller.load();
|
|
2667
2174
|
if (snapshot.catalogStatus === "idle") controller.loadCatalog();
|
|
2668
|
-
if (snapshot.switchesStatus === "idle") controller.loadSwitches();
|
|
2669
2175
|
}, [controller]);
|
|
2670
2176
|
const [scalars, setScalars] = (0, react.useState)(() => scalarsOf(defaultFallbacksConfig));
|
|
2671
|
-
const [
|
|
2672
|
-
const [
|
|
2673
|
-
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);
|
|
2674
2179
|
const [roleRows, setRoleRows] = (0, react.useState)(() => rolesToRows(defaultFallbacksConfig.roles.list));
|
|
2675
|
-
const [ruleRows, setRuleRows] = (0, react.useState)(() => rulesToRows(defaultFallbacksConfig.roles.rules));
|
|
2676
2180
|
const [presetToAdd, setPresetToAdd] = (0, react.useState)("");
|
|
2677
2181
|
const [lastSaveSection, setLastSaveSection] = (0, react.useState)(null);
|
|
2182
|
+
const [lastSaveChain, setLastSaveChain] = (0, react.useState)(null);
|
|
2678
2183
|
const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
|
|
2184
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
2679
2185
|
const seededConfigKey = (0, react.useRef)(null);
|
|
2680
|
-
const
|
|
2681
|
-
const
|
|
2682
|
-
const
|
|
2683
|
-
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);
|
|
2684
2197
|
const advancedDirty = JSON.stringify([
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2198
|
+
scalars.triggerCodes,
|
|
2199
|
+
scalars.cooldownMs,
|
|
2200
|
+
scalars.revertPolicy,
|
|
2201
|
+
scalars.maxSwitchesPerStep
|
|
2689
2202
|
]) !== JSON.stringify([
|
|
2690
2203
|
state.config.triggerCodes,
|
|
2691
2204
|
state.config.cooldownMs,
|
|
2692
2205
|
state.config.revertPolicy,
|
|
2693
2206
|
state.config.maxSwitchesPerStep
|
|
2694
2207
|
]);
|
|
2695
|
-
const enabledPolicy = state.subagentPolicy?.state === "enabled" ? state.subagentPolicy : void 0;
|
|
2696
|
-
const allowedModels = enabledPolicy?.allowedModels;
|
|
2697
|
-
const offAllowlistCount = enabledPolicy === void 0 ? 0 : offAllowlistEntryCount(draft.roles.list, enabledPolicy.allowedModels);
|
|
2698
|
-
const virtualRouteAllowed = enabledPolicy !== void 0 && routeOnAllowlist(VIRTUAL_ALL_ROUTE, enabledPolicy.allowedModels);
|
|
2699
|
-
const [syncStatus, setSyncStatus] = (0, react.useState)("idle");
|
|
2700
|
-
const syncDirtyHintId = `fallbacks-policy-sync-dirty-${(0, react.useId)().replace(/:/g, "")}`;
|
|
2701
|
-
const syncAllowlist = () => {
|
|
2702
|
-
if (syncStatus === "pending") return;
|
|
2703
|
-
setSyncStatus("pending");
|
|
2704
|
-
controller.syncAllowlist().then((ok) => {
|
|
2705
|
-
setSyncStatus(ok ? "ok" : "error");
|
|
2706
|
-
});
|
|
2707
|
-
};
|
|
2708
|
-
const firstSeedDone = (0, react.useRef)(false);
|
|
2709
2208
|
(0, react.useEffect)(() => {
|
|
2710
2209
|
if (state.status !== "ready") return;
|
|
2711
2210
|
const key = JSON.stringify(state.config);
|
|
@@ -2714,33 +2213,21 @@ window.__ModuleLoader__.load({
|
|
|
2714
2213
|
const firstSeed = !firstSeedDone.current;
|
|
2715
2214
|
firstSeedDone.current = true;
|
|
2716
2215
|
const catalog = catalogOf(state);
|
|
2717
|
-
if (firstSeed || !
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
setScalars((prev) => ({
|
|
2722
|
-
...prev,
|
|
2723
|
-
tz: state.config.tz ?? "Asia/Shanghai"
|
|
2724
|
-
}));
|
|
2725
|
-
}
|
|
2726
|
-
if (firstSeed || !subDirty) {
|
|
2727
|
-
setRoleRows(rolesToRows(state.config.roles.list, catalog));
|
|
2728
|
-
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)));
|
|
2729
2220
|
}
|
|
2221
|
+
if (firstSeed || !rolesDirty) setRoleRows(rolesToRows(state.config.roles.list));
|
|
2730
2222
|
if (firstSeed || !advancedDirty) setScalars((prev) => ({
|
|
2731
2223
|
...prev,
|
|
2732
2224
|
triggerCodes: [...state.config.triggerCodes],
|
|
2733
2225
|
cooldownMs: state.config.cooldownMs,
|
|
2734
2226
|
revertPolicy: state.config.revertPolicy,
|
|
2735
|
-
maxSwitchesPerStep: state.config.maxSwitchesPerStep
|
|
2227
|
+
maxSwitchesPerStep: state.config.maxSwitchesPerStep,
|
|
2228
|
+
tz: state.config.tz ?? "Asia/Shanghai"
|
|
2736
2229
|
}));
|
|
2737
|
-
}, [
|
|
2738
|
-
state.status,
|
|
2739
|
-
state.config,
|
|
2740
|
-
mainDirty,
|
|
2741
|
-
subDirty,
|
|
2742
|
-
advancedDirty
|
|
2743
|
-
]);
|
|
2230
|
+
}, [state.status, state.config]);
|
|
2744
2231
|
const updateScalars = (mutator) => {
|
|
2745
2232
|
setScalars((prev) => {
|
|
2746
2233
|
const next = {
|
|
@@ -2751,139 +2238,195 @@ window.__ModuleLoader__.load({
|
|
|
2751
2238
|
return next;
|
|
2752
2239
|
});
|
|
2753
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
|
+
};
|
|
2754
2251
|
const updateTimeSlotRow = (index, patch) => {
|
|
2755
|
-
|
|
2756
|
-
const
|
|
2757
|
-
|
|
2758
|
-
...
|
|
2252
|
+
patchActiveDraft((draft) => {
|
|
2253
|
+
const slots = draft.slots.map((row) => ({ ...row }));
|
|
2254
|
+
slots[index] = {
|
|
2255
|
+
...slots[index],
|
|
2759
2256
|
...patch
|
|
2760
2257
|
};
|
|
2761
|
-
return
|
|
2258
|
+
return {
|
|
2259
|
+
...draft,
|
|
2260
|
+
slots
|
|
2261
|
+
};
|
|
2762
2262
|
});
|
|
2763
2263
|
};
|
|
2764
2264
|
const updateTimeSlotSelector = (rowIndex, selectorIndex, patch) => {
|
|
2765
|
-
|
|
2766
|
-
const
|
|
2265
|
+
patchActiveDraft((draft) => {
|
|
2266
|
+
const slots = draft.slots.map((row) => ({
|
|
2767
2267
|
...row,
|
|
2768
2268
|
selectors: row.selectors.map((selector) => ({ ...selector }))
|
|
2769
2269
|
}));
|
|
2770
|
-
const selectors =
|
|
2270
|
+
const selectors = slots[rowIndex].selectors;
|
|
2771
2271
|
selectors[selectorIndex] = {
|
|
2772
2272
|
...selectors[selectorIndex],
|
|
2773
2273
|
...patch
|
|
2774
2274
|
};
|
|
2775
|
-
return
|
|
2275
|
+
return {
|
|
2276
|
+
...draft,
|
|
2277
|
+
slots
|
|
2278
|
+
};
|
|
2776
2279
|
});
|
|
2777
2280
|
};
|
|
2778
2281
|
const addTimeSlotSelector = (rowIndex) => {
|
|
2779
|
-
|
|
2780
|
-
...
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
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
|
+
}));
|
|
2787
2293
|
};
|
|
2788
2294
|
const removeTimeSlotSelector = (rowIndex, selectorIndex) => {
|
|
2789
|
-
|
|
2790
|
-
...
|
|
2791
|
-
|
|
2792
|
-
|
|
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
|
+
}));
|
|
2793
2302
|
};
|
|
2794
2303
|
const addPresetSlotRow = () => {
|
|
2795
2304
|
if (presetToAdd === "") return;
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
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
|
+
}));
|
|
2807
2318
|
setPresetToAdd("");
|
|
2808
2319
|
};
|
|
2809
2320
|
const addCustomSlotRow = () => {
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
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
|
+
}));
|
|
2819
2333
|
};
|
|
2820
2334
|
const removeTimeSlotRow = (index) => {
|
|
2821
|
-
|
|
2335
|
+
patchActiveDraft((draft) => ({
|
|
2336
|
+
...draft,
|
|
2337
|
+
slots: draft.slots.filter((_, rowIndex) => rowIndex !== index)
|
|
2338
|
+
}));
|
|
2822
2339
|
};
|
|
2823
2340
|
const moveTimeSlotRow = (index, delta) => {
|
|
2824
|
-
|
|
2341
|
+
patchActiveDraft((draft) => {
|
|
2825
2342
|
const target = index + delta;
|
|
2826
|
-
if (target < 0 || target >=
|
|
2827
|
-
const
|
|
2828
|
-
const moved =
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
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
|
+
};
|
|
2832
2352
|
});
|
|
2833
2353
|
};
|
|
2834
2354
|
const [draggedSlotIndex, setDraggedSlotIndex] = (0, react.useState)(null);
|
|
2835
2355
|
const [overSlotIndex, setOverSlotIndex] = (0, react.useState)(null);
|
|
2836
2356
|
const reorderTimeSlotRow = (from, to) => {
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
const [moved] =
|
|
2841
|
-
|
|
2842
|
-
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
|
+
};
|
|
2843
2366
|
});
|
|
2844
2367
|
};
|
|
2845
|
-
const
|
|
2846
|
-
|
|
2847
|
-
...
|
|
2848
|
-
|
|
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 ? {
|
|
2849
2380
|
...selector,
|
|
2850
2381
|
...patch
|
|
2851
|
-
} : selector)
|
|
2852
|
-
|
|
2382
|
+
} : selector);
|
|
2383
|
+
return {
|
|
2384
|
+
...draft,
|
|
2385
|
+
selectors
|
|
2386
|
+
};
|
|
2387
|
+
});
|
|
2853
2388
|
};
|
|
2854
|
-
const
|
|
2855
|
-
|
|
2856
|
-
...
|
|
2857
|
-
selectors: [...
|
|
2389
|
+
const addChainSelector = () => {
|
|
2390
|
+
patchActiveDraft((draft) => ({
|
|
2391
|
+
...draft,
|
|
2392
|
+
selectors: [...draft.selectors, {
|
|
2858
2393
|
wildcard: false,
|
|
2859
2394
|
provider: null,
|
|
2860
2395
|
model: null
|
|
2861
2396
|
}]
|
|
2862
2397
|
}));
|
|
2863
2398
|
};
|
|
2864
|
-
const
|
|
2865
|
-
|
|
2866
|
-
...
|
|
2867
|
-
selectors:
|
|
2399
|
+
const removeChainSelector = (selectorIndex) => {
|
|
2400
|
+
patchActiveDraft((draft) => ({
|
|
2401
|
+
...draft,
|
|
2402
|
+
selectors: draft.selectors.filter((_, index) => index !== selectorIndex)
|
|
2868
2403
|
}));
|
|
2869
2404
|
};
|
|
2870
|
-
const
|
|
2871
|
-
|
|
2872
|
-
...
|
|
2873
|
-
selectors: moveItem(
|
|
2405
|
+
const reorderChainSelector = (from, to) => {
|
|
2406
|
+
patchActiveDraft((draft) => ({
|
|
2407
|
+
...draft,
|
|
2408
|
+
selectors: moveItem(draft.selectors, from, to)
|
|
2874
2409
|
}));
|
|
2875
2410
|
};
|
|
2876
|
-
const
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
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);
|
|
2881
2424
|
};
|
|
2882
|
-
const
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
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);
|
|
2887
2430
|
};
|
|
2888
2431
|
const updateRoleRow = (index, patch) => {
|
|
2889
2432
|
setRoleRows((rows) => {
|
|
@@ -2895,42 +2438,11 @@ window.__ModuleLoader__.load({
|
|
|
2895
2438
|
return next;
|
|
2896
2439
|
});
|
|
2897
2440
|
};
|
|
2898
|
-
const updateRoleSelector = (roleIndex, selectorIndex, patch) => {
|
|
2899
|
-
setRoleRows((rows) => {
|
|
2900
|
-
const next = rows.map((row) => ({
|
|
2901
|
-
...row,
|
|
2902
|
-
selectors: row.selectors.map((selector) => ({ ...selector }))
|
|
2903
|
-
}));
|
|
2904
|
-
const selectors = next[roleIndex].selectors;
|
|
2905
|
-
selectors[selectorIndex] = {
|
|
2906
|
-
...selectors[selectorIndex],
|
|
2907
|
-
...patch
|
|
2908
|
-
};
|
|
2909
|
-
return next;
|
|
2910
|
-
});
|
|
2911
|
-
};
|
|
2912
|
-
const addRoleSelector = (roleIndex) => {
|
|
2913
|
-
setRoleRows((rows) => rows.map((row, index) => index === roleIndex ? {
|
|
2914
|
-
...row,
|
|
2915
|
-
selectors: [...row.selectors, {
|
|
2916
|
-
wildcard: false,
|
|
2917
|
-
provider: null,
|
|
2918
|
-
model: null
|
|
2919
|
-
}]
|
|
2920
|
-
} : row));
|
|
2921
|
-
};
|
|
2922
|
-
const removeRoleSelector = (roleIndex, selectorIndex) => {
|
|
2923
|
-
setRoleRows((rows) => rows.map((row, index) => index === roleIndex ? {
|
|
2924
|
-
...row,
|
|
2925
|
-
selectors: row.selectors.filter((_, sIndex) => sIndex !== selectorIndex)
|
|
2926
|
-
} : row));
|
|
2927
|
-
};
|
|
2928
2441
|
const addRole = () => {
|
|
2929
2442
|
setRoleRows((rows) => [...rows, {
|
|
2930
2443
|
id: "",
|
|
2931
2444
|
persona: "",
|
|
2932
|
-
|
|
2933
|
-
fallback: "inherit-root",
|
|
2445
|
+
chain: "",
|
|
2934
2446
|
collapsed: true,
|
|
2935
2447
|
personaOpen: false
|
|
2936
2448
|
}]);
|
|
@@ -2938,141 +2450,145 @@ window.__ModuleLoader__.load({
|
|
|
2938
2450
|
const removeRole = (index) => {
|
|
2939
2451
|
setRoleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
|
|
2940
2452
|
};
|
|
2941
|
-
const updateRuleRow = (index, patch) => {
|
|
2942
|
-
setRuleRows((rows) => {
|
|
2943
|
-
const next = rows.map((row) => ({ ...row }));
|
|
2944
|
-
next[index] = {
|
|
2945
|
-
...next[index],
|
|
2946
|
-
...patch
|
|
2947
|
-
};
|
|
2948
|
-
return next;
|
|
2949
|
-
});
|
|
2950
|
-
};
|
|
2951
2453
|
const saving = state.status === "saving";
|
|
2952
2454
|
const writable = state.writable;
|
|
2953
2455
|
const advancedVisible = advancedOpen || !writable;
|
|
2954
2456
|
const cooldownMsTip = `${t("cooldownMs.tooltip")} ${t("defaults.prefix")}: ${defaultFallbacksConfig.cooldownMs}`;
|
|
2955
2457
|
const maxSwitchesPerStepTip = `${t("maxSwitchesPerStep.tooltip")} ${t("defaults.prefix")}: ${defaultFallbacksConfig.maxSwitchesPerStep}`;
|
|
2956
2458
|
const unknownCodes = scalars.triggerCodes.filter((code) => !KNOWN_TRIGGER_CODES.includes(code));
|
|
2957
|
-
const presetsPresent =
|
|
2459
|
+
const presetsPresent = (chainDrafts[activeChain]?.slots ?? []).some((row) => row.kind === "preset");
|
|
2958
2460
|
const glmConfigured = state.configuredProviders.some((entry) => entry.provider === "zai-coding-cn");
|
|
2959
|
-
const
|
|
2960
|
-
const activeSlotIndex =
|
|
2961
|
-
|
|
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 ?? []);
|
|
2962
2469
|
const invalidRoleIds = collectInvalidRoleIds(roleRows);
|
|
2963
|
-
const invalidSlotRows = collectInvalidSlotRows(timeSlotRows);
|
|
2964
2470
|
const seedInfo = /* @__PURE__ */ new Map();
|
|
2965
2471
|
for (const seed of state.seeds) seedInfo.set(seed.id.trim(), {
|
|
2966
2472
|
overridden: seed.overridden,
|
|
2967
2473
|
source: seed.source
|
|
2968
2474
|
});
|
|
2969
|
-
const liveErrors = validateDraft(
|
|
2970
|
-
const
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
else if (state.switchesStatus === "loading") switchesLine = t("loading");
|
|
2974
|
-
else if (latestSwitch === void 0) switchesLine = t("status.switches.empty");
|
|
2975
|
-
else {
|
|
2976
|
-
const reasonKey = SWITCH_REASON_KEYS[latestSwitch.reason];
|
|
2977
|
-
const params = {
|
|
2978
|
-
count: String(state.switches.length),
|
|
2979
|
-
from: `${latestSwitch.from.provider}/${latestSwitch.from.model}`,
|
|
2980
|
-
to: `${latestSwitch.to.provider}/${latestSwitch.to.model}`,
|
|
2981
|
-
role: latestSwitch.role,
|
|
2982
|
-
reason: reasonKey === void 0 ? latestSwitch.reason : t(reasonKey)
|
|
2983
|
-
};
|
|
2984
|
-
switchesLine = latestSwitch.reason === "role-inject" ? t("status.switches.compact.roleInject", params) : t("status.switches.compact", params);
|
|
2985
|
-
}
|
|
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;
|
|
2986
2479
|
const catalogSeededSections = (0, react.useRef)({
|
|
2987
2480
|
epoch: null,
|
|
2988
|
-
|
|
2989
|
-
|
|
2481
|
+
chains: false,
|
|
2482
|
+
roles: false
|
|
2990
2483
|
});
|
|
2991
2484
|
(0, react.useEffect)(() => {
|
|
2992
2485
|
if (state.catalogStatus !== "ready") return;
|
|
2993
2486
|
const seed = catalogSeededSections.current;
|
|
2994
2487
|
if (seed.epoch !== state.catalogEpoch) {
|
|
2995
2488
|
seed.epoch = state.catalogEpoch;
|
|
2996
|
-
seed.
|
|
2997
|
-
seed.
|
|
2489
|
+
seed.chains = false;
|
|
2490
|
+
seed.roles = false;
|
|
2998
2491
|
}
|
|
2999
2492
|
const catalog = catalogOf(state);
|
|
3000
|
-
if (!
|
|
3001
|
-
seed.
|
|
3002
|
-
|
|
3003
|
-
setAllDayChainRow(allDayChainRowOf(state.config.rootChain, catalog));
|
|
3004
|
-
setTimeSlotRows(timeSlotsToRows(state.config.timeSlots ?? [], catalog));
|
|
2493
|
+
if (!chainsDirty && !seed.chains) {
|
|
2494
|
+
seed.chains = true;
|
|
2495
|
+
setChainDrafts(chainsToDrafts(state.config.chains, catalog));
|
|
3005
2496
|
}
|
|
3006
|
-
if (!
|
|
3007
|
-
seed.
|
|
3008
|
-
setRoleRows(rolesToRows(state.config.roles.list
|
|
3009
|
-
setRuleRows(rulesToRows(state.config.roles.rules, catalog));
|
|
2497
|
+
if (!rolesDirty && !seed.roles) {
|
|
2498
|
+
seed.roles = true;
|
|
2499
|
+
setRoleRows(rolesToRows(state.config.roles.list));
|
|
3010
2500
|
}
|
|
3011
2501
|
}, [
|
|
3012
2502
|
state.catalogStatus,
|
|
3013
2503
|
state.catalogEpoch,
|
|
3014
|
-
state.config
|
|
3015
|
-
mainDirty,
|
|
3016
|
-
subDirty
|
|
2504
|
+
state.config
|
|
3017
2505
|
]);
|
|
3018
|
-
const
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
timeSlots: draft.timeSlots,
|
|
3025
|
-
tz: draft.tz
|
|
3026
|
-
};
|
|
3027
|
-
case "sub": return {
|
|
3028
|
-
...base,
|
|
3029
|
-
roles: draft.roles
|
|
3030
|
-
};
|
|
3031
|
-
case "advanced": return {
|
|
3032
|
-
...base,
|
|
3033
|
-
triggerCodes: draft.triggerCodes,
|
|
3034
|
-
cooldownMs: draft.cooldownMs,
|
|
3035
|
-
revertPolicy: draft.revertPolicy,
|
|
3036
|
-
maxSwitchesPerStep: draft.maxSwitchesPerStep
|
|
3037
|
-
};
|
|
3038
|
-
}
|
|
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
|
+
});
|
|
3039
2512
|
};
|
|
3040
2513
|
const saveSection = (section) => {
|
|
3041
|
-
if (
|
|
3042
|
-
|
|
3043
|
-
|
|
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
|
+
});
|
|
3044
2541
|
};
|
|
3045
|
-
const
|
|
3046
|
-
const
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
...prev,
|
|
3054
|
-
tz: state.config.tz ?? "Asia/Shanghai"
|
|
3055
|
-
}));
|
|
3056
|
-
break;
|
|
3057
|
-
case "sub":
|
|
3058
|
-
setRoleRows(rolesToRows(state.config.roles.list, catalog));
|
|
3059
|
-
setRuleRows(rulesToRows(state.config.roles.rules, catalog));
|
|
3060
|
-
break;
|
|
3061
|
-
case "advanced": setScalars((prev) => ({
|
|
3062
|
-
...prev,
|
|
3063
|
-
triggerCodes: [...state.config.triggerCodes],
|
|
3064
|
-
cooldownMs: state.config.cooldownMs,
|
|
3065
|
-
revertPolicy: state.config.revertPolicy,
|
|
3066
|
-
maxSwitchesPerStep: state.config.maxSwitchesPerStep
|
|
3067
|
-
}));
|
|
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;
|
|
3068
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
|
+
}));
|
|
3069
2563
|
};
|
|
3070
2564
|
(0, react.useEffect)(() => {
|
|
3071
|
-
if (state.status === "ready")
|
|
2565
|
+
if (state.status === "ready") {
|
|
2566
|
+
setLastSaveSection(null);
|
|
2567
|
+
setLastSaveChain(null);
|
|
2568
|
+
}
|
|
3072
2569
|
}, [state.status]);
|
|
3073
2570
|
const degradedLatch = (0, react.useRef)(false);
|
|
3074
2571
|
if (state.status === "ready") degradedLatch.current = !state.present;
|
|
3075
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);
|
|
3076
2592
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3077
2593
|
className: FallbacksCard_module_css_default.root,
|
|
3078
2594
|
children: [
|
|
@@ -3116,474 +2632,698 @@ window.__ModuleLoader__.load({
|
|
|
3116
2632
|
children: [
|
|
3117
2633
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
|
|
3118
2634
|
className: FallbacksCard_module_css_default.sectionHeading,
|
|
3119
|
-
id: "fallbacks-
|
|
3120
|
-
"aria-label": t("
|
|
2635
|
+
id: "fallbacks-virtual-chains",
|
|
2636
|
+
"aria-label": t("virtualChain.label"),
|
|
3121
2637
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3122
2638
|
className: FallbacksCard_module_css_default.sectionHeadingText,
|
|
3123
|
-
children: t("
|
|
3124
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3125
|
-
className: FallbacksCard_module_css_default.
|
|
3126
|
-
children:
|
|
3127
|
-
type: "button",
|
|
3128
|
-
className: FallbacksCard_module_css_default.discard,
|
|
3129
|
-
disabled: !mainDirty || saving,
|
|
3130
|
-
onClick: () => {
|
|
3131
|
-
discardSection("main");
|
|
3132
|
-
},
|
|
3133
|
-
children: t("discard")
|
|
3134
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3135
|
-
type: "button",
|
|
3136
|
-
className: FallbacksCard_module_css_default.save,
|
|
3137
|
-
disabled: !writable || saving || !mainDirty,
|
|
3138
|
-
onClick: () => {
|
|
3139
|
-
saveSection("main");
|
|
3140
|
-
},
|
|
3141
|
-
children: saving ? t("save.saving") : t("save")
|
|
3142
|
-
})]
|
|
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")
|
|
3143
2643
|
})]
|
|
3144
2644
|
}),
|
|
3145
|
-
liveErrors.main.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3146
|
-
className: FallbacksCard_module_css_default.error,
|
|
3147
|
-
role: "alert",
|
|
3148
|
-
children: `${t("validation.blocked")}${liveErrors.main.join("; ")}`
|
|
3149
|
-
}),
|
|
3150
|
-
lastSaveSection === "main" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3151
|
-
className: FallbacksCard_module_css_default.error,
|
|
3152
|
-
role: "alert",
|
|
3153
|
-
children: t("error.generic", { message: state.error })
|
|
3154
|
-
}),
|
|
3155
2645
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3156
|
-
className: FallbacksCard_module_css_default.
|
|
3157
|
-
|
|
3158
|
-
"aria-labelledby": "fallbacks-time-slots",
|
|
2646
|
+
className: FallbacksCard_module_css_default.tabsWrap,
|
|
2647
|
+
"data-tabs-wrap": "",
|
|
3159
2648
|
children: [
|
|
3160
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
3161
|
-
className: FallbacksCard_module_css_default.
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
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
|
+
]
|
|
3169
2763
|
}),
|
|
3170
2764
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3171
|
-
className: FallbacksCard_module_css_default.
|
|
3172
|
-
|
|
2765
|
+
className: FallbacksCard_module_css_default.fadeL,
|
|
2766
|
+
"aria-hidden": "true"
|
|
3173
2767
|
}),
|
|
3174
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3175
|
-
className: FallbacksCard_module_css_default.
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
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);
|
|
3239
2869
|
},
|
|
3240
|
-
|
|
2870
|
+
onDrop: (event) => {
|
|
2871
|
+
event.preventDefault();
|
|
2872
|
+
const from = draggedSlotIndex;
|
|
3241
2873
|
setDraggedSlotIndex(null);
|
|
3242
2874
|
setOverSlotIndex(null);
|
|
2875
|
+
if (from !== null && from !== index) reorderTimeSlotRow(from, index);
|
|
3243
2876
|
},
|
|
3244
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3252
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3253
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3254
|
-
children: t("timeSlots.preset.name")
|
|
3255
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3256
|
-
className: FallbacksCard_module_css_default.slotPresetName,
|
|
3257
|
-
children: t(`timeSlots.preset.${row.preset}.label`)
|
|
3258
|
-
})]
|
|
3259
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3260
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3261
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3262
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3263
|
-
children: t("timeSlots.preset.windowLabel")
|
|
3264
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3265
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3266
|
-
children: t(`timeSlots.preset.${row.preset}.window`)
|
|
3267
|
-
})]
|
|
3268
|
-
})]
|
|
3269
|
-
}),
|
|
3270
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3271
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3272
|
-
children: t("timeSlots.preset.chainsOnly")
|
|
3273
|
-
}),
|
|
3274
|
-
(row.preset === "glm-peak" || row.preset === "glm-valley") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3275
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3276
|
-
children: t("timeSlots.preset.glm.note")
|
|
3277
|
-
})
|
|
3278
|
-
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3279
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3280
|
-
className: FallbacksCard_module_css_default.field,
|
|
3281
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3282
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3283
|
-
children: t("timeSlots.name")
|
|
3284
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3285
|
-
className: FallbacksCard_module_css_default.input,
|
|
3286
|
-
value: row.name,
|
|
3287
|
-
placeholder: t("timeSlots.name"),
|
|
3288
|
-
"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"),
|
|
3289
2884
|
disabled: !writable,
|
|
3290
|
-
|
|
3291
|
-
updateTimeSlotRow(index, {
|
|
3292
|
-
}
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
"aria-label": t("timeSlots.end"),
|
|
3333
|
-
disabled: !writable,
|
|
3334
|
-
onChange: (event) => {
|
|
3335
|
-
updateTimeSlotRow(index, { end: event.target.value });
|
|
3336
|
-
}
|
|
3337
|
-
})]
|
|
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, {})
|
|
3338
2927
|
})]
|
|
3339
|
-
}),
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
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
|
+
}),
|
|
3343
2952
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3344
|
-
className: FallbacksCard_module_css_default.
|
|
3345
|
-
children: t("timeSlots.
|
|
2953
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2954
|
+
children: t("timeSlots.preset.chainsOnly")
|
|
3346
2955
|
}),
|
|
3347
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3348
|
-
className: FallbacksCard_module_css_default.
|
|
3349
|
-
children:
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
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
|
+
})]
|
|
2987
|
+
}),
|
|
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"),
|
|
3354
3000
|
disabled: !writable,
|
|
3355
|
-
onChange: () => {
|
|
3356
|
-
updateTimeSlotRow(index, {
|
|
3001
|
+
onChange: (event) => {
|
|
3002
|
+
updateTimeSlotRow(index, { start: event.target.value });
|
|
3357
3003
|
}
|
|
3358
|
-
})
|
|
3359
|
-
},
|
|
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"),
|
|
3015
|
+
disabled: !writable,
|
|
3016
|
+
onChange: (event) => {
|
|
3017
|
+
updateTimeSlotRow(index, { end: event.target.value });
|
|
3018
|
+
}
|
|
3019
|
+
})]
|
|
3020
|
+
})]
|
|
3360
3021
|
}),
|
|
3361
|
-
/* @__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", {
|
|
3362
3050
|
className: FallbacksCard_module_css_default.hint,
|
|
3363
|
-
children: t("
|
|
3051
|
+
children: t("validation.slotWindow")
|
|
3364
3052
|
})
|
|
3365
|
-
]
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
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
|
+
})]
|
|
3375
3152
|
}),
|
|
3376
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
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")]
|
|
3159
|
+
}),
|
|
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", {
|
|
3377
3209
|
className: FallbacksCard_module_css_default.chainBlock,
|
|
3378
3210
|
"data-chain-block": "",
|
|
3379
3211
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3380
|
-
selectors:
|
|
3212
|
+
selectors: activeDraft.selectors,
|
|
3381
3213
|
catalog: catalogOf(state),
|
|
3382
3214
|
configuredProviders: state.configuredProviders,
|
|
3383
3215
|
disabled: !writable,
|
|
3384
3216
|
t,
|
|
3385
3217
|
onChange: (selectorIndex, patch) => {
|
|
3386
|
-
|
|
3218
|
+
updateChainSelector(selectorIndex, patch);
|
|
3387
3219
|
},
|
|
3388
3220
|
onRemove: (selectorIndex) => {
|
|
3389
|
-
|
|
3221
|
+
removeChainSelector(selectorIndex);
|
|
3390
3222
|
},
|
|
3391
|
-
onMove:
|
|
3392
|
-
reorderTimeSlotSelector(index, from, to);
|
|
3393
|
-
}
|
|
3223
|
+
onMove: reorderChainSelector
|
|
3394
3224
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3395
3225
|
type: "button",
|
|
3396
3226
|
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3397
3227
|
disabled: !writable,
|
|
3398
|
-
onClick:
|
|
3399
|
-
addTimeSlotSelector(index);
|
|
3400
|
-
},
|
|
3228
|
+
onClick: addChainSelector,
|
|
3401
3229
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.selector.add")]
|
|
3402
3230
|
})]
|
|
3403
|
-
}),
|
|
3404
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3405
|
-
className: FallbacksCard_module_css_default.cardFoot,
|
|
3406
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3407
|
-
className: FallbacksCard_module_css_default.rowActions,
|
|
3408
|
-
children: [
|
|
3409
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3410
|
-
type: "button",
|
|
3411
|
-
className: FallbacksCard_module_css_default.iconButton,
|
|
3412
|
-
"data-tip": t("timeSlots.moveUp"),
|
|
3413
|
-
"aria-label": t("timeSlots.moveUp"),
|
|
3414
|
-
disabled: !writable || index === 0,
|
|
3415
|
-
onClick: () => {
|
|
3416
|
-
moveTimeSlotRow(index, -1);
|
|
3417
|
-
},
|
|
3418
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, { className: FallbacksCard_module_css_default.chevronFlip })
|
|
3419
|
-
}),
|
|
3420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3421
|
-
type: "button",
|
|
3422
|
-
className: FallbacksCard_module_css_default.iconButton,
|
|
3423
|
-
"data-tip": t("timeSlots.moveDown"),
|
|
3424
|
-
"aria-label": t("timeSlots.moveDown"),
|
|
3425
|
-
disabled: !writable || index === timeSlotRows.length - 1,
|
|
3426
|
-
onClick: () => {
|
|
3427
|
-
moveTimeSlotRow(index, 1);
|
|
3428
|
-
},
|
|
3429
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, {})
|
|
3430
|
-
}),
|
|
3431
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3432
|
-
type: "button",
|
|
3433
|
-
className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
|
|
3434
|
-
"data-tip": t("timeSlots.remove"),
|
|
3435
|
-
"aria-label": t("timeSlots.remove"),
|
|
3436
|
-
onClick: () => {
|
|
3437
|
-
removeTimeSlotRow(index);
|
|
3438
|
-
},
|
|
3439
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
|
|
3440
|
-
})
|
|
3441
|
-
]
|
|
3442
|
-
})
|
|
3443
3231
|
})
|
|
3444
|
-
|
|
3445
|
-
}
|
|
3446
|
-
|
|
3232
|
+
})
|
|
3233
|
+
})
|
|
3234
|
+
]
|
|
3447
3235
|
}),
|
|
3448
3236
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3449
|
-
className: FallbacksCard_module_css_default.
|
|
3237
|
+
className: FallbacksCard_module_css_default.field,
|
|
3238
|
+
role: "group",
|
|
3239
|
+
"aria-labelledby": `fallbacks-default-model-${activeDraft.id}`,
|
|
3450
3240
|
children: [
|
|
3451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3452
|
-
className:
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
children: [t(`timeSlots.preset.${id}.label`), glmUnconfigured ? t("timeSlots.preset.glm.unconfigured") : null]
|
|
3468
|
-
}, id);
|
|
3469
|
-
})]
|
|
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
|
+
})
|
|
3247
|
+
}),
|
|
3248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3249
|
+
className: FallbacksCard_module_css_default.hint,
|
|
3250
|
+
children: t("allDay.hint")
|
|
3251
|
+
}),
|
|
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}`
|
|
3470
3257
|
}),
|
|
3471
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
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
|
+
})
|
|
3291
|
+
})
|
|
3292
|
+
]
|
|
3293
|
+
}),
|
|
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", {
|
|
3472
3299
|
type: "button",
|
|
3473
|
-
className:
|
|
3474
|
-
disabled: !
|
|
3475
|
-
onClick:
|
|
3476
|
-
children:
|
|
3300
|
+
className: FallbacksCard_module_css_default.discard,
|
|
3301
|
+
disabled: !activeDirty || saving,
|
|
3302
|
+
onClick: discardChain,
|
|
3303
|
+
children: t("discard")
|
|
3477
3304
|
}),
|
|
3478
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3479
3306
|
type: "button",
|
|
3480
|
-
className:
|
|
3481
|
-
disabled: !writable,
|
|
3482
|
-
onClick:
|
|
3483
|
-
|
|
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 })
|
|
3484
3322
|
})
|
|
3485
3323
|
]
|
|
3486
3324
|
})
|
|
3487
3325
|
]
|
|
3488
3326
|
}),
|
|
3489
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3490
|
-
className: FallbacksCard_module_css_default.field,
|
|
3491
|
-
role: "group",
|
|
3492
|
-
"aria-labelledby": "fallbacks-root-chain",
|
|
3493
|
-
children: [
|
|
3494
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3495
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3496
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3497
|
-
id: "fallbacks-root-chain",
|
|
3498
|
-
children: t("rootChain.label")
|
|
3499
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3500
|
-
label: t("rootChain.tooltip"),
|
|
3501
|
-
disabled: !writable
|
|
3502
|
-
})]
|
|
3503
|
-
}),
|
|
3504
|
-
state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3505
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3506
|
-
children: t("catalog.error", { message: state.catalogError })
|
|
3507
|
-
}),
|
|
3508
|
-
state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3509
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3510
|
-
children: t("catalog.partial", { message: state.catalogError })
|
|
3511
|
-
}),
|
|
3512
|
-
state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3513
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3514
|
-
children: t("catalog.empty")
|
|
3515
|
-
}),
|
|
3516
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3517
|
-
className: FallbacksCard_module_css_default.list,
|
|
3518
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3519
|
-
className: FallbacksCard_module_css_default.editorCard,
|
|
3520
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3521
|
-
className: FallbacksCard_module_css_default.chainBlock,
|
|
3522
|
-
"data-chain-block": "",
|
|
3523
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3524
|
-
selectors: allDayChainRow.selectors,
|
|
3525
|
-
catalog: catalogOf(state),
|
|
3526
|
-
configuredProviders: state.configuredProviders,
|
|
3527
|
-
disabled: !writable,
|
|
3528
|
-
t,
|
|
3529
|
-
onChange: (selectorIndex, patch) => {
|
|
3530
|
-
updateAllDayChainSelector(selectorIndex, patch);
|
|
3531
|
-
},
|
|
3532
|
-
onRemove: (selectorIndex) => {
|
|
3533
|
-
removeAllDayChainSelector(selectorIndex);
|
|
3534
|
-
},
|
|
3535
|
-
onMove: reorderAllDayChainSelector
|
|
3536
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3537
|
-
type: "button",
|
|
3538
|
-
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3539
|
-
disabled: !writable,
|
|
3540
|
-
onClick: addAllDayChainSelector,
|
|
3541
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("timeSlots.selector.add")]
|
|
3542
|
-
})]
|
|
3543
|
-
})
|
|
3544
|
-
})
|
|
3545
|
-
})
|
|
3546
|
-
]
|
|
3547
|
-
}),
|
|
3548
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3549
|
-
className: FallbacksCard_module_css_default.field,
|
|
3550
|
-
role: "group",
|
|
3551
|
-
"aria-labelledby": "fallbacks-default-model",
|
|
3552
|
-
children: [
|
|
3553
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3554
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3555
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3556
|
-
id: "fallbacks-default-model",
|
|
3557
|
-
children: t("defaultModel.label")
|
|
3558
|
-
})
|
|
3559
|
-
}),
|
|
3560
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3561
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3562
|
-
children: t("allDay.hint")
|
|
3563
|
-
}),
|
|
3564
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3565
|
-
className: FallbacksCard_module_css_default.list,
|
|
3566
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3567
|
-
className: FallbacksCard_module_css_default.editorCard,
|
|
3568
|
-
children: [OFFICIAL_ALL_DAY_IDS.map((id, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3569
|
-
className: FallbacksCard_module_css_default.optionRow,
|
|
3570
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3571
|
-
type: "radio",
|
|
3572
|
-
name: "fallbacks-all-day",
|
|
3573
|
-
checked: allDayModel === id,
|
|
3574
|
-
disabled: !writable,
|
|
3575
|
-
onChange: () => {
|
|
3576
|
-
setAllDayModel(id);
|
|
3577
|
-
}
|
|
3578
|
-
}), t(ALL_DAY_LABEL_KEYS[index])]
|
|
3579
|
-
}, id)), allDayModel === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3580
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3581
|
-
children: t("allDay.nonconforming")
|
|
3582
|
-
})]
|
|
3583
|
-
})
|
|
3584
|
-
})
|
|
3585
|
-
]
|
|
3586
|
-
}),
|
|
3587
3327
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
|
|
3588
3328
|
className: FallbacksCard_module_css_default.sectionHeading,
|
|
3589
3329
|
id: "fallbacks-subagents",
|
|
@@ -3596,7 +3336,7 @@ window.__ModuleLoader__.load({
|
|
|
3596
3336
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3597
3337
|
type: "button",
|
|
3598
3338
|
className: FallbacksCard_module_css_default.discard,
|
|
3599
|
-
disabled: !
|
|
3339
|
+
disabled: !rolesDirty || saving,
|
|
3600
3340
|
onClick: () => {
|
|
3601
3341
|
discardSection("sub");
|
|
3602
3342
|
},
|
|
@@ -3604,7 +3344,7 @@ window.__ModuleLoader__.load({
|
|
|
3604
3344
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3605
3345
|
type: "button",
|
|
3606
3346
|
className: FallbacksCard_module_css_default.save,
|
|
3607
|
-
disabled: !writable || saving || !
|
|
3347
|
+
disabled: !writable || saving || !rolesDirty || liveErrors.roles.length > 0,
|
|
3608
3348
|
onClick: () => {
|
|
3609
3349
|
saveSection("sub");
|
|
3610
3350
|
},
|
|
@@ -3612,101 +3352,27 @@ window.__ModuleLoader__.load({
|
|
|
3612
3352
|
})]
|
|
3613
3353
|
})]
|
|
3614
3354
|
}),
|
|
3615
|
-
liveErrors.
|
|
3355
|
+
liveErrors.roles.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3616
3356
|
className: FallbacksCard_module_css_default.error,
|
|
3617
3357
|
role: "alert",
|
|
3618
|
-
children: `${t("validation.blocked")}${liveErrors.
|
|
3358
|
+
children: `${t("validation.blocked")}${liveErrors.roles.join("; ")}`
|
|
3619
3359
|
}),
|
|
3620
3360
|
lastSaveSection === "sub" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3621
3361
|
className: FallbacksCard_module_css_default.error,
|
|
3622
3362
|
role: "alert",
|
|
3623
3363
|
children: t("error.generic", { message: state.error })
|
|
3624
3364
|
}),
|
|
3625
|
-
state.subagentPolicy?.state === "enabled" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3626
|
-
className: FallbacksCard_module_css_default.policyStatus,
|
|
3627
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3628
|
-
className: FallbacksCard_module_css_default.policyStatusText,
|
|
3629
|
-
role: "status",
|
|
3630
|
-
children: [
|
|
3631
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3632
|
-
className: FallbacksCard_module_css_default.policyStatusLabel,
|
|
3633
|
-
children: t("subagents.policy.label")
|
|
3634
|
-
}),
|
|
3635
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3636
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3637
|
-
children: `${t("subagents.policy.allowlist")}: ${state.subagentPolicy.allowedModels.map((route) => `${route.provider}/${route.model}`).join(", ")}`
|
|
3638
|
-
}),
|
|
3639
|
-
state.subagentPolicy.head !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3640
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3641
|
-
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")})`
|
|
3642
|
-
}),
|
|
3643
|
-
state.subagentPolicy.blockedAttempt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3644
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3645
|
-
role: "alert",
|
|
3646
|
-
children: t("subagents.policy.blocked")
|
|
3647
|
-
}),
|
|
3648
|
-
offAllowlistCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3649
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3650
|
-
children: t("subagents.policy.offAllowlist", { count: String(offAllowlistCount) })
|
|
3651
|
-
}),
|
|
3652
|
-
!virtualRouteAllowed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3653
|
-
className: FallbacksCard_module_css_default.policyStatusLine,
|
|
3654
|
-
children: t("subagents.policy.recommend")
|
|
3655
|
-
})
|
|
3656
|
-
]
|
|
3657
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3658
|
-
className: FallbacksCard_module_css_default.policyStatusSync,
|
|
3659
|
-
role: "group",
|
|
3660
|
-
"aria-label": t("subagents.policy.sync"),
|
|
3661
|
-
children: [
|
|
3662
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3663
|
-
type: "button",
|
|
3664
|
-
className: FallbacksCard_module_css_default.outlineButton,
|
|
3665
|
-
disabled: !writable || subDirty || mainDirty,
|
|
3666
|
-
"aria-busy": syncStatus === "pending" || void 0,
|
|
3667
|
-
"aria-describedby": subDirty || mainDirty ? syncDirtyHintId : void 0,
|
|
3668
|
-
onClick: syncAllowlist,
|
|
3669
|
-
children: t("subagents.policy.sync")
|
|
3670
|
-
}),
|
|
3671
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3672
|
-
id: syncDirtyHintId,
|
|
3673
|
-
role: "status",
|
|
3674
|
-
"data-sync-hint": "dirty",
|
|
3675
|
-
className: `${FallbacksCard_module_css_default.policyStatusWarn} ${FallbacksCard_module_css_default.policyStatusLive}`,
|
|
3676
|
-
children: subDirty || mainDirty ? t("subagents.policy.sync.dirty") : ""
|
|
3677
|
-
}),
|
|
3678
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3679
|
-
role: "status",
|
|
3680
|
-
"data-sync-hint": "outcome",
|
|
3681
|
-
className: `${syncStatus === "error" ? FallbacksCard_module_css_default.policyStatusWarn : FallbacksCard_module_css_default.policyStatusLine} ${FallbacksCard_module_css_default.policyStatusLive}`,
|
|
3682
|
-
children: syncStatus === "ok" ? t("subagents.policy.sync.done") : syncStatus === "error" ? t("subagents.policy.sync.failed") : ""
|
|
3683
|
-
})
|
|
3684
|
-
]
|
|
3685
|
-
})]
|
|
3686
|
-
}),
|
|
3687
|
-
state.subagentPolicy?.state === "unprovable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3688
|
-
className: FallbacksCard_module_css_default.policyStatus,
|
|
3689
|
-
role: "status",
|
|
3690
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3691
|
-
className: FallbacksCard_module_css_default.policyStatusWarn,
|
|
3692
|
-
role: "alert",
|
|
3693
|
-
children: t("subagents.policy.unprovable")
|
|
3694
|
-
})
|
|
3695
|
-
}),
|
|
3696
3365
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3697
3366
|
className: FallbacksCard_module_css_default.field,
|
|
3698
3367
|
role: "group",
|
|
3699
3368
|
"aria-labelledby": "fallbacks-roles-list",
|
|
3700
3369
|
children: [
|
|
3701
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3370
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3702
3371
|
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3703
|
-
children:
|
|
3372
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3704
3373
|
id: "fallbacks-roles-list",
|
|
3705
3374
|
children: t("roles.list.label")
|
|
3706
|
-
})
|
|
3707
|
-
label: t("roles.list.tooltip"),
|
|
3708
|
-
disabled: !writable
|
|
3709
|
-
})]
|
|
3375
|
+
})
|
|
3710
3376
|
}),
|
|
3711
3377
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3712
3378
|
className: FallbacksCard_module_css_default.hint,
|
|
@@ -3719,11 +3385,12 @@ window.__ModuleLoader__.load({
|
|
|
3719
3385
|
const seed = seedInfo.get(row.id.trim());
|
|
3720
3386
|
const seedBadge = seedBadgeLabel(seed, t);
|
|
3721
3387
|
const personaBlank = row.persona.trim() === "";
|
|
3722
|
-
const
|
|
3723
|
-
const roleSummary = roleChainSummary ?? row.fallback;
|
|
3388
|
+
const chainRoute = row.chain === "" ? t("roles.chainMeta.unspecified") : `FallbacksChain/${row.chain}`;
|
|
3724
3389
|
const roleExpanded = !row.collapsed || !writable;
|
|
3390
|
+
const chainOutside = row.chain !== "" && !chains.some((chain) => chain.id === row.chain);
|
|
3725
3391
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3726
3392
|
className: FallbacksCard_module_css_default.editorCard,
|
|
3393
|
+
"data-role-card": row.id,
|
|
3727
3394
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3728
3395
|
className: FallbacksCard_module_css_default.collapseRow,
|
|
3729
3396
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -3748,8 +3415,8 @@ window.__ModuleLoader__.load({
|
|
|
3748
3415
|
}),
|
|
3749
3416
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3750
3417
|
className: FallbacksCard_module_css_default.collapseMeta,
|
|
3751
|
-
title:
|
|
3752
|
-
children:
|
|
3418
|
+
title: chainRoute,
|
|
3419
|
+
children: chainRoute
|
|
3753
3420
|
})
|
|
3754
3421
|
]
|
|
3755
3422
|
})
|
|
@@ -3781,6 +3448,49 @@ window.__ModuleLoader__.load({
|
|
|
3781
3448
|
]
|
|
3782
3449
|
})
|
|
3783
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
|
+
}),
|
|
3784
3494
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3785
3495
|
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3786
3496
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3827,66 +3537,6 @@ window.__ModuleLoader__.load({
|
|
|
3827
3537
|
})] })]
|
|
3828
3538
|
})
|
|
3829
3539
|
}),
|
|
3830
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3831
|
-
className: FallbacksCard_module_css_default.chainBlock,
|
|
3832
|
-
"data-chain-block": "",
|
|
3833
|
-
children: [
|
|
3834
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChainSelectorList, {
|
|
3835
|
-
selectors: row.selectors,
|
|
3836
|
-
catalog: catalogOf(state),
|
|
3837
|
-
configuredProviders: state.configuredProviders,
|
|
3838
|
-
disabled: !writable,
|
|
3839
|
-
t,
|
|
3840
|
-
onChange: (selectorIndex, patch) => {
|
|
3841
|
-
updateRoleSelector(index, selectorIndex, patch);
|
|
3842
|
-
},
|
|
3843
|
-
onRemove: (selectorIndex) => {
|
|
3844
|
-
removeRoleSelector(index, selectorIndex);
|
|
3845
|
-
},
|
|
3846
|
-
onMove: (from, to) => {
|
|
3847
|
-
reorderRoleSelector(index, from, to);
|
|
3848
|
-
}
|
|
3849
|
-
}),
|
|
3850
|
-
row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3851
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3852
|
-
children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
|
|
3853
|
-
}),
|
|
3854
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3855
|
-
type: "button",
|
|
3856
|
-
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
3857
|
-
disabled: !writable,
|
|
3858
|
-
onClick: () => {
|
|
3859
|
-
addRoleSelector(index);
|
|
3860
|
-
},
|
|
3861
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("roles.selector.add")]
|
|
3862
|
-
})
|
|
3863
|
-
]
|
|
3864
|
-
}),
|
|
3865
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3866
|
-
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3867
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3868
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3869
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3870
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3871
|
-
children: t("roles.fallback")
|
|
3872
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3873
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
3874
|
-
value: row.fallback,
|
|
3875
|
-
"aria-label": t("roles.fallback"),
|
|
3876
|
-
disabled: !writable,
|
|
3877
|
-
onChange: (event) => {
|
|
3878
|
-
updateRoleRow(index, { fallback: event.target.value });
|
|
3879
|
-
},
|
|
3880
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3881
|
-
value: "inherit-root",
|
|
3882
|
-
children: t("roles.fallback.inherit-root")
|
|
3883
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3884
|
-
value: "none",
|
|
3885
|
-
children: t("roles.fallback.none")
|
|
3886
|
-
})]
|
|
3887
|
-
})]
|
|
3888
|
-
})
|
|
3889
|
-
}),
|
|
3890
3540
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3891
3541
|
className: FallbacksCard_module_css_default.cardFoot,
|
|
3892
3542
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -3913,188 +3563,6 @@ window.__ModuleLoader__.load({
|
|
|
3913
3563
|
})
|
|
3914
3564
|
]
|
|
3915
3565
|
}),
|
|
3916
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3917
|
-
className: FallbacksCard_module_css_default.field,
|
|
3918
|
-
role: "group",
|
|
3919
|
-
"aria-labelledby": "fallbacks-roles-rules",
|
|
3920
|
-
children: [
|
|
3921
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3922
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
3923
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3924
|
-
id: "fallbacks-roles-rules",
|
|
3925
|
-
children: t("roles.rules")
|
|
3926
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
3927
|
-
label: t("roles.rules.tooltip"),
|
|
3928
|
-
disabled: !writable
|
|
3929
|
-
})]
|
|
3930
|
-
}),
|
|
3931
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3932
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3933
|
-
children: t("roles.rules.hint")
|
|
3934
|
-
}),
|
|
3935
|
-
state.catalogStatus === "error" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3936
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3937
|
-
children: t("catalog.error", { message: state.catalogError })
|
|
3938
|
-
}),
|
|
3939
|
-
state.catalogStatus === "ready" && state.catalogError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3940
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3941
|
-
children: t("catalog.partial", { message: state.catalogError })
|
|
3942
|
-
}),
|
|
3943
|
-
state.catalogStatus === "ready" && (state.groups.length === 0 || state.configuredProviders.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3944
|
-
className: FallbacksCard_module_css_default.hint,
|
|
3945
|
-
children: t("catalog.empty")
|
|
3946
|
-
}),
|
|
3947
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3948
|
-
className: FallbacksCard_module_css_default.list,
|
|
3949
|
-
children: ruleRows.map((row, index) => {
|
|
3950
|
-
const catalog = catalogOf(state);
|
|
3951
|
-
const providerRaw = selectionToRaw(row.provider);
|
|
3952
|
-
const group = catalog?.groups.find((entry) => entry.id === providerRaw);
|
|
3953
|
-
const providerOutside = row.provider?.kind === "outside";
|
|
3954
|
-
const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !state.configuredProviders.some((entry) => entry.provider === providerRaw);
|
|
3955
|
-
const modelOutside = row.model?.kind === "outside";
|
|
3956
|
-
const roleOutside = row.role !== "" && !roleOptions.includes(row.role);
|
|
3957
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3958
|
-
className: FallbacksCard_module_css_default.editorCard,
|
|
3959
|
-
children: [
|
|
3960
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3961
|
-
className: FallbacksCard_module_css_default.ruleGrid,
|
|
3962
|
-
children: [
|
|
3963
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3964
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
3965
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3966
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
3967
|
-
children: t("roles.rule.provider")
|
|
3968
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3969
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
3970
|
-
value: providerRaw,
|
|
3971
|
-
onChange: (event) => {
|
|
3972
|
-
if (event.target.value === providerRaw) return;
|
|
3973
|
-
updateRuleRow(index, {
|
|
3974
|
-
provider: classifyProvider(event.target.value, catalog),
|
|
3975
|
-
model: null
|
|
3976
|
-
});
|
|
3977
|
-
},
|
|
3978
|
-
children: [
|
|
3979
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3980
|
-
value: "",
|
|
3981
|
-
children: t("roles.rule.provider.any")
|
|
3982
|
-
}),
|
|
3983
|
-
state.configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3984
|
-
value: entry.provider,
|
|
3985
|
-
children: entry.displayName
|
|
3986
|
-
}, entry.provider)),
|
|
3987
|
-
providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3988
|
-
value: providerRaw,
|
|
3989
|
-
children: `${providerRaw}${t("catalog.unconfigured.short")}`
|
|
3990
|
-
}),
|
|
3991
|
-
providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3992
|
-
value: providerRaw,
|
|
3993
|
-
children: `${providerRaw}${t("catalog.outside.short")}`
|
|
3994
|
-
})
|
|
3995
|
-
]
|
|
3996
|
-
})]
|
|
3997
|
-
}),
|
|
3998
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3999
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
4000
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4001
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
4002
|
-
children: t("roles.rule.model")
|
|
4003
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4004
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
4005
|
-
value: selectionToRaw(row.model),
|
|
4006
|
-
onChange: (event) => {
|
|
4007
|
-
updateRuleRow(index, { model: classifyModel(providerRaw, event.target.value, catalog) });
|
|
4008
|
-
},
|
|
4009
|
-
children: [
|
|
4010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4011
|
-
value: "",
|
|
4012
|
-
children: t("roles.rule.model.any")
|
|
4013
|
-
}),
|
|
4014
|
-
(group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4015
|
-
value: model.id,
|
|
4016
|
-
children: model.name
|
|
4017
|
-
}, model.id)),
|
|
4018
|
-
modelOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4019
|
-
value: selectionToRaw(row.model),
|
|
4020
|
-
children: `${selectionToRaw(row.model)}${t("catalog.outside.short")}`
|
|
4021
|
-
})
|
|
4022
|
-
]
|
|
4023
|
-
})]
|
|
4024
|
-
}),
|
|
4025
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4026
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
4027
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4028
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
4029
|
-
children: t("roles.rule.role")
|
|
4030
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4031
|
-
className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
|
|
4032
|
-
value: row.role,
|
|
4033
|
-
disabled: !writable,
|
|
4034
|
-
onChange: (event) => {
|
|
4035
|
-
updateRuleRow(index, { role: event.target.value });
|
|
4036
|
-
},
|
|
4037
|
-
children: [
|
|
4038
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4039
|
-
value: "",
|
|
4040
|
-
children: t("roles.rule.roleSelectPlaceholder")
|
|
4041
|
-
}),
|
|
4042
|
-
roleOptions.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4043
|
-
value: id,
|
|
4044
|
-
children: id === "inherit" ? t("roles.rule.role.inherit") : id
|
|
4045
|
-
}, id)),
|
|
4046
|
-
roleOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4047
|
-
value: row.role,
|
|
4048
|
-
children: `${row.role}${t("roles.rule.roleUndeclared.short")}`
|
|
4049
|
-
})
|
|
4050
|
-
]
|
|
4051
|
-
})]
|
|
4052
|
-
})
|
|
4053
|
-
]
|
|
4054
|
-
}),
|
|
4055
|
-
(providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4056
|
-
className: FallbacksCard_module_css_default.hint,
|
|
4057
|
-
children: [t("catalog.outside.hint"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
4058
|
-
label: t("catalog.outside.tooltip"),
|
|
4059
|
-
disabled: !writable
|
|
4060
|
-
})]
|
|
4061
|
-
}),
|
|
4062
|
-
row.role === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4063
|
-
className: FallbacksCard_module_css_default.hint,
|
|
4064
|
-
children: t("validation.ruleRoleRequired")
|
|
4065
|
-
}),
|
|
4066
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4067
|
-
className: FallbacksCard_module_css_default.cardFoot,
|
|
4068
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4069
|
-
type: "button",
|
|
4070
|
-
className: `${FallbacksCard_module_css_default.iconButton} ${FallbacksCard_module_css_default.iconButtonDanger}`,
|
|
4071
|
-
"data-tip": t("roles.removeRule"),
|
|
4072
|
-
"aria-label": t("roles.removeRule"),
|
|
4073
|
-
onClick: () => {
|
|
4074
|
-
setRuleRows((rows) => rows.filter((_, rowIndex) => rowIndex !== index));
|
|
4075
|
-
},
|
|
4076
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
|
|
4077
|
-
})
|
|
4078
|
-
})
|
|
4079
|
-
]
|
|
4080
|
-
}, index);
|
|
4081
|
-
})
|
|
4082
|
-
}),
|
|
4083
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4084
|
-
type: "button",
|
|
4085
|
-
className: `${FallbacksCard_module_css_default.outlineButton} ${FallbacksCard_module_css_default.addButton}`,
|
|
4086
|
-
disabled: !writable,
|
|
4087
|
-
onClick: () => {
|
|
4088
|
-
setRuleRows((rows) => [...rows, {
|
|
4089
|
-
provider: null,
|
|
4090
|
-
model: null,
|
|
4091
|
-
role: ""
|
|
4092
|
-
}]);
|
|
4093
|
-
},
|
|
4094
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, {}), t("roles.addRule")]
|
|
4095
|
-
})
|
|
4096
|
-
]
|
|
4097
|
-
}),
|
|
4098
3566
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
4099
3567
|
className: FallbacksCard_module_css_default.sectionHeading,
|
|
4100
3568
|
id: "fallbacks-advanced",
|
|
@@ -4119,16 +3587,6 @@ window.__ModuleLoader__.load({
|
|
|
4119
3587
|
id: "fallbacks-advanced-body",
|
|
4120
3588
|
className: FallbacksCard_module_css_default.advancedBody,
|
|
4121
3589
|
children: [
|
|
4122
|
-
liveErrors.advanced.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4123
|
-
className: FallbacksCard_module_css_default.error,
|
|
4124
|
-
role: "alert",
|
|
4125
|
-
children: `${t("validation.blocked")}${liveErrors.advanced.join("; ")}`
|
|
4126
|
-
}),
|
|
4127
|
-
lastSaveSection === "advanced" && state.status === "error" && state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4128
|
-
className: FallbacksCard_module_css_default.error,
|
|
4129
|
-
role: "alert",
|
|
4130
|
-
children: t("error.generic", { message: state.error })
|
|
4131
|
-
}),
|
|
4132
3590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4133
3591
|
className: FallbacksCard_module_css_default.field,
|
|
4134
3592
|
role: "group",
|
|
@@ -4289,126 +3747,11 @@ window.__ModuleLoader__.load({
|
|
|
4289
3747
|
]
|
|
4290
3748
|
})
|
|
4291
3749
|
]
|
|
4292
|
-
}),
|
|
4293
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4294
|
-
className: FallbacksCard_module_css_default.statusBlock,
|
|
4295
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4296
|
-
className: FallbacksCard_module_css_default.statusTitle,
|
|
4297
|
-
children: t("status.title")
|
|
4298
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4299
|
-
className: FallbacksCard_module_css_default.statusLine,
|
|
4300
|
-
role: state.switchesStatus === "error" ? "alert" : void 0,
|
|
4301
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4302
|
-
className: FallbacksCard_module_css_default.statusLineLabel,
|
|
4303
|
-
children: t("status.switches.label")
|
|
4304
|
-
}), switchesLine]
|
|
4305
|
-
})]
|
|
4306
3750
|
})
|
|
4307
3751
|
]
|
|
4308
3752
|
});
|
|
4309
3753
|
}
|
|
4310
3754
|
//#endregion
|
|
4311
|
-
//#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/GeneralFallbacksRow.module.css.mjs
|
|
4312
|
-
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";
|
|
4313
|
-
const tagId$2 = "dsh-llm-fallbacks/GeneralFallbacksRow.module.css";
|
|
4314
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
4315
|
-
const tag = document.createElement("style");
|
|
4316
|
-
tag.dataset.plugin = "dsh-llm-fallbacks";
|
|
4317
|
-
tag.dataset.pluginCss = tagId$2;
|
|
4318
|
-
tag.textContent = css$2;
|
|
4319
|
-
document.head.appendChild(tag);
|
|
4320
|
-
}
|
|
4321
|
-
var GeneralFallbacksRow_module_css_default = {
|
|
4322
|
-
"row": "_440e1d7b_row",
|
|
4323
|
-
"rowText": "_a4f2ceec_rowText",
|
|
4324
|
-
"title": "_9865b509_title",
|
|
4325
|
-
"summary": "_10a44713_summary",
|
|
4326
|
-
"badge": "_35648278_badge",
|
|
4327
|
-
"badgeEnabled": "_3b9e8609_badgeEnabled"
|
|
4328
|
-
};
|
|
4329
|
-
//#endregion
|
|
4330
|
-
//#region src/client/GeneralFallbacksRow.tsx
|
|
4331
|
-
/**
|
|
4332
|
-
* Fallbacks status row — the `fallbacks` read-only row on the dsh General
|
|
4333
|
-
* settings page (plan fallbacks-aux-seams, task 1). Registered into the
|
|
4334
|
-
* `settings.general.item` slot (id `fallbacks`, order 100 — after every
|
|
4335
|
-
* upstream preference row: agent-preset -25 / permission -20 / language 0 /
|
|
4336
|
-
* appearance 10 / composer-enter 20, so the informational row renders at the
|
|
4337
|
-
* column end). Owner props are intentionally empty (`children?: never`,
|
|
4338
|
-
* dsh-private ui-settings slots.ts:81-84 — the section column only stacks),
|
|
4339
|
-
* so all data flows through the shared {@link FallbacksSettingsController}
|
|
4340
|
-
* (the same instance the plugin-config card consumes): the row triggers the
|
|
4341
|
-
* first read on mount when the store is still idle, and the pushed
|
|
4342
|
-
* invalidations wired in `apply` (`settings/document-updated` fallbacks-ns +
|
|
4343
|
-
* `connection/reset`, which refresh only already-read stores) keep it fresh
|
|
4344
|
-
* afterwards — no new data path, no store API change.
|
|
4345
|
-
*
|
|
4346
|
-
* The row is read-only by design (偏好位语义: a General preference row is not
|
|
4347
|
-
* a control surface): an active/inactive badge (content presence — the
|
|
4348
|
-
* removed config-level `enabled` switch is re-keyed to `isFallbackActive`,
|
|
4349
|
-
* plan fallbacks-web-ux-alignment T2) + a compact last-switch summary.
|
|
4350
|
-
* Honest degraded states: a hard load error or an unreachable gateway
|
|
4351
|
-
* channel (`ready && !present`) render the neutral 'unknown' badge — a
|
|
4352
|
-
* channel-down read must never masquerade as 'disabled' (KD-G5); the
|
|
4353
|
-
* switches face keeps its own error/empty states (D-5 semantics unchanged).
|
|
4354
|
-
*
|
|
4355
|
-
* Geometry follows the upstream Setting-Cell (figma 501:30011 — gap 8,
|
|
4356
|
-
* pad 16/0, hairline separator, title over subtitle in the text column, a
|
|
4357
|
-
* small non-interactive pill on the right); every color resolves through a
|
|
4358
|
-
* `--dsw-alias-*` token (light/dark adaptive).
|
|
4359
|
-
*/
|
|
4360
|
-
/**
|
|
4361
|
-
* Render the Fallbacks status row.
|
|
4362
|
-
* @param props - composed slot props.
|
|
4363
|
-
* @returns the row element tree.
|
|
4364
|
-
*/
|
|
4365
|
-
function GeneralFallbacksRow({ controller, useSnapshot, t }) {
|
|
4366
|
-
const state = useSnapshot((snapshot) => snapshot);
|
|
4367
|
-
(0, react.useEffect)(() => {
|
|
4368
|
-
const snapshot = controller.store.getSnapshot();
|
|
4369
|
-
if (snapshot.status === "idle") controller.load();
|
|
4370
|
-
if (snapshot.switchesStatus === "idle") controller.loadSwitches();
|
|
4371
|
-
}, [controller]);
|
|
4372
|
-
const settled = state.status === "ready";
|
|
4373
|
-
const badgeKey = settled && state.present ? isFallbackActive(state.config) ? "general.enabled" : "general.disabled" : "general.unknown";
|
|
4374
|
-
const latestSwitch = state.switches[0];
|
|
4375
|
-
let summary;
|
|
4376
|
-
if (state.status === "error") summary = t("general.error", { message: state.error ?? "" });
|
|
4377
|
-
else if (!settled) summary = t("loading");
|
|
4378
|
-
else if (!state.present) summary = t("general.unavailable");
|
|
4379
|
-
else if (state.switchesStatus === "error") summary = t("status.switches.error", { message: state.switchesError ?? "" });
|
|
4380
|
-
else if (state.switchesStatus === "loading") summary = t("loading");
|
|
4381
|
-
else if (latestSwitch === void 0) summary = t("general.switch.empty");
|
|
4382
|
-
else {
|
|
4383
|
-
const reasonKey = SWITCH_REASON_KEYS[latestSwitch.reason];
|
|
4384
|
-
const params = {
|
|
4385
|
-
from: `${latestSwitch.from.provider}/${latestSwitch.from.model}`,
|
|
4386
|
-
to: `${latestSwitch.to.provider}/${latestSwitch.to.model}`,
|
|
4387
|
-
role: latestSwitch.role,
|
|
4388
|
-
reason: reasonKey === void 0 ? latestSwitch.reason : t(reasonKey)
|
|
4389
|
-
};
|
|
4390
|
-
summary = latestSwitch.reason === "role-inject" ? t("general.switch.roleInject", params) : t("general.switch", params);
|
|
4391
|
-
}
|
|
4392
|
-
const alert = state.status === "error" || state.switchesStatus === "error";
|
|
4393
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4394
|
-
className: GeneralFallbacksRow_module_css_default.row,
|
|
4395
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4396
|
-
className: GeneralFallbacksRow_module_css_default.rowText,
|
|
4397
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4398
|
-
className: GeneralFallbacksRow_module_css_default.title,
|
|
4399
|
-
children: t("general.title")
|
|
4400
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4401
|
-
className: GeneralFallbacksRow_module_css_default.summary,
|
|
4402
|
-
role: alert ? "alert" : void 0,
|
|
4403
|
-
children: summary
|
|
4404
|
-
})]
|
|
4405
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4406
|
-
className: `${GeneralFallbacksRow_module_css_default.badge} ${badgeKey === "general.enabled" ? GeneralFallbacksRow_module_css_default.badgeEnabled : ""}`,
|
|
4407
|
-
children: t(badgeKey)
|
|
4408
|
-
})]
|
|
4409
|
-
});
|
|
4410
|
-
}
|
|
4411
|
-
//#endregion
|
|
4412
3755
|
//#region src/client/switch-guard.ts
|
|
4413
3756
|
/**
|
|
4414
3757
|
* True when `value` is a well-formed `fallbacks/switch` payload — the ONE
|
|
@@ -4762,7 +4105,7 @@ window.__ModuleLoader__.load({
|
|
|
4762
4105
|
const connection = ctx.get("connection");
|
|
4763
4106
|
const sessions = ctx.get("sessions");
|
|
4764
4107
|
const controller = new FallbacksSettingsController(ctx.remote, connection.rpc);
|
|
4765
|
-
|
|
4108
|
+
controller.store;
|
|
4766
4109
|
ctx.effect(() => {
|
|
4767
4110
|
const syncSession = () => {
|
|
4768
4111
|
controller.setCurrentSession(sessions === void 0 ? void 0 : viewedSessionId(sessions.list.getSnapshot()));
|
|
@@ -4771,7 +4114,6 @@ window.__ModuleLoader__.load({
|
|
|
4771
4114
|
const refresh = (ns) => {
|
|
4772
4115
|
if (ns !== void 0 && ns !== "fallbacks") return;
|
|
4773
4116
|
refreshFallbacksIfLoaded(controller);
|
|
4774
|
-
refreshSwitchesIfLoaded(controller);
|
|
4775
4117
|
};
|
|
4776
4118
|
const refreshCatalog = () => {
|
|
4777
4119
|
refreshCatalogIfLoaded(controller);
|
|
@@ -4812,18 +4154,6 @@ window.__ModuleLoader__.load({
|
|
|
4812
4154
|
})
|
|
4813
4155
|
}, FallbacksCard);
|
|
4814
4156
|
});
|
|
4815
|
-
ctx.slots.inject("settings.general.item", function* () {
|
|
4816
|
-
yield ctx.slots.register({
|
|
4817
|
-
name: "settings.general.item",
|
|
4818
|
-
id: "fallbacks",
|
|
4819
|
-
order: 100,
|
|
4820
|
-
locale: NS,
|
|
4821
|
-
inject: () => ({
|
|
4822
|
-
controller,
|
|
4823
|
-
useSnapshot
|
|
4824
|
-
})
|
|
4825
|
-
}, GeneralFallbacksRow);
|
|
4826
|
-
});
|
|
4827
4157
|
ctx.effect(() => ctx.uiConversation.events.register(fallbackSwitchDefinition), "llm-fallbacks: conversation node definition");
|
|
4828
4158
|
ctx.slots.inject("conversation.chat.node", function* () {
|
|
4829
4159
|
yield ctx.slots.register({
|