dsh-decision-layer 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -69,7 +69,7 @@ var dictionaries = {
69
69
  checkPassRate: "\u901A\u8FC7\u7387",
70
70
  narrowApplyRate: "\u6536\u7A84\u7387",
71
71
  rateNa: "\u2014",
72
- enabled: "\u542F\u7528\u81EA\u52A8\u4ECB\u5165",
72
+ enabled: "\u672C\u4F1A\u8BDD\u5185\u542F\u7528\u81EA\u52A8\u4ECB\u5165",
73
73
  backendStatus: "\u540E\u7AEF\u8FDE\u63A5",
74
74
  statusChecking: "\u68C0\u6D4B\u4E2D\u2026",
75
75
  statusIdle: "\u672A\u68C0\u6D4B",
@@ -218,7 +218,7 @@ var dictionaries = {
218
218
  checkPassRate: "Pass rate",
219
219
  narrowApplyRate: "Narrow rate",
220
220
  rateNa: "\u2014",
221
- enabled: "Enable automatic interventions",
221
+ enabled: "Enable automatic interventions in this session",
222
222
  backendStatus: "Backend",
223
223
  statusChecking: "Checking\u2026",
224
224
  statusIdle: "Not checked",
@@ -569,6 +569,37 @@ function DecisionLogList({ entries, ratings, onRate, t, newestFirst = true }) {
569
569
  ] }, entry.id ?? `${entry.at}-${index}`)) });
570
570
  }
571
571
 
572
+ // src/client/cards.tsx
573
+ var import_jsx_runtime2 = require("react/jsx-runtime");
574
+ function CardHead({ title, total, unit, rate }) {
575
+ const totalSpan = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
576
+ total,
577
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: unit })
578
+ ] });
579
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: title }),
581
+ rate ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-head-metrics", children: [
582
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-rate-inline", children: [
583
+ rate.label,
584
+ " ",
585
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: rate.value })
586
+ ] }),
587
+ totalSpan
588
+ ] }) : totalSpan
589
+ ] });
590
+ }
591
+ function Stat({ label, value, pct, tone }) {
592
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
593
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `decision-stat-num${tone ? ` decision-stat-${tone}` : ""}`, children: value }),
594
+ typeof pct === "number" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-stat-pct", children: [
595
+ pct,
596
+ "%"
597
+ ] }),
598
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: label })
599
+ ] });
600
+ }
601
+ var share = (n, total) => total > 0 ? Math.round(n / total * 100) : null;
602
+
572
603
  // src/client/use-annotations.ts
573
604
  var import_react = require("react");
574
605
  function useAnnotations(sessionId, onFail) {
@@ -577,7 +608,11 @@ function useAnnotations(sessionId, onFail) {
577
608
  setRatings(analysis?.ratings ?? {});
578
609
  }, []);
579
610
  const annotate = (0, import_react.useCallback)(async (id, rating) => {
580
- setRatings((prev) => ({ ...prev, [id]: rating }));
611
+ let previous;
612
+ setRatings((prev) => {
613
+ previous = prev[id];
614
+ return { ...prev, [id]: rating };
615
+ });
581
616
  try {
582
617
  await request("annotate", {
583
618
  method: "POST",
@@ -588,7 +623,8 @@ function useAnnotations(sessionId, onFail) {
588
623
  onFail();
589
624
  setRatings((prev) => {
590
625
  const next = { ...prev };
591
- delete next[id];
626
+ if (previous === void 0) delete next[id];
627
+ else next[id] = previous;
592
628
  return next;
593
629
  });
594
630
  }
@@ -597,29 +633,32 @@ function useAnnotations(sessionId, onFail) {
597
633
  }
598
634
 
599
635
  // src/client/panel.tsx
600
- var import_jsx_runtime2 = require("react/jsx-runtime");
636
+ var import_jsx_runtime3 = require("react/jsx-runtime");
601
637
  function DecisionPanel(props) {
602
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SessionPanel, { ...props }, props.sessionId);
638
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SessionPanel, { ...props }, props.sessionId);
603
639
  }
604
640
  function Bar({ allow, ask, deny }) {
605
641
  const total = allow + ask + deny;
606
- if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "decision-bar decision-bar-empty", "aria-hidden": "true" });
642
+ if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "decision-bar decision-bar-empty", "aria-hidden": "true" });
607
643
  const pct = (n) => `${n / total * 100}%`;
608
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-bar", "aria-hidden": "true", children: [
609
- allow > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-allow", style: { width: pct(allow) } }),
610
- ask > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-ask", style: { width: pct(ask) } }),
611
- deny > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-deny", style: { width: pct(deny) } })
644
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-bar", "aria-hidden": "true", children: [
645
+ allow > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-bar-allow", style: { width: pct(allow) } }),
646
+ ask > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-bar-ask", style: { width: pct(ask) } }),
647
+ deny > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-bar-deny", style: { width: pct(deny) } })
612
648
  ] });
613
649
  }
614
650
  function SessionPanel({ sessionId, t }) {
615
651
  const dialog = (0, import_react2.useRef)(null);
616
652
  const trigger = (0, import_react2.useRef)(null);
653
+ const mounted = (0, import_react2.useRef)(true);
654
+ (0, import_react2.useEffect)(() => () => {
655
+ mounted.current = false;
656
+ }, []);
617
657
  const [open, setOpen] = (0, import_react2.useState)(false);
618
658
  const [enabled, setEnabled] = (0, import_react2.useState)(null);
619
659
  const [busy, setBusy] = (0, import_react2.useState)(false);
620
660
  const [metrics, setMetrics] = (0, import_react2.useState)(null);
621
661
  const [log, setLog] = (0, import_react2.useState)([]);
622
- const [analysis, setAnalysis] = (0, import_react2.useState)(null);
623
662
  const [status, setStatus] = (0, import_react2.useState)("idle");
624
663
  const [message, setMessage] = (0, import_react2.useState)("");
625
664
  const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
@@ -638,13 +677,12 @@ function SessionPanel({ sessionId, t }) {
638
677
  live = false;
639
678
  };
640
679
  }, [sessionId, t]);
641
- const checkStatus = (live) => {
680
+ const checkStatus = () => {
642
681
  setStatus("checking");
643
682
  void request("probe", { method: "POST" }).then((value) => {
644
- if (!live()) return;
645
- setStatus(value.connected ? "ok" : "down");
683
+ if (mounted.current) setStatus(value.connected ? "ok" : "down");
646
684
  }).catch(() => {
647
- if (live()) setStatus("down");
685
+ if (mounted.current) setStatus("down");
648
686
  });
649
687
  };
650
688
  (0, import_react2.useEffect)(() => {
@@ -663,17 +701,14 @@ function SessionPanel({ sessionId, t }) {
663
701
  }).catch(() => {
664
702
  });
665
703
  void request(`logs?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
666
- if (alive) {
667
- setAnalysis(value);
668
- seed(value);
669
- }
704
+ if (alive) seed(value);
670
705
  }).catch(() => {
671
706
  });
672
707
  return () => {
673
708
  alive = false;
674
709
  element.close();
675
710
  };
676
- }, [open, t, sessionId, seed]);
711
+ }, [open, sessionId, seed]);
677
712
  const updateEnabled = async (next) => {
678
713
  setBusy(true);
679
714
  try {
@@ -689,7 +724,6 @@ function SessionPanel({ sessionId, t }) {
689
724
  setBusy(false);
690
725
  }
691
726
  };
692
- const time2 = (at) => new Date(at).toLocaleTimeString();
693
727
  const statusLabel = status === "ok" ? t("statusOk") : status === "down" ? t("statusDown") : status === "checking" ? t("statusChecking") : t("statusIdle");
694
728
  const attempts = metrics?.attempts ?? 0;
695
729
  const severity = metrics?.check?.trend?.severity ?? "normal";
@@ -698,19 +732,19 @@ function SessionPanel({ sessionId, t }) {
698
732
  const checkPassRate = metrics?.check && checkValid > 0 ? Math.round((checkValid - metrics.check.low) / checkValid * 100) : null;
699
733
  const narrowValid = metrics?.narrow ? metrics.narrow.attempts - metrics.narrow.failures : 0;
700
734
  const narrowApplyRate = metrics?.narrow && narrowValid > 0 ? Math.round(metrics.narrow.applied / narrowValid * 100) : null;
701
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
702
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { ref: trigger, className: `decision-trigger decision-trend-${severity}`, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-busy": enabled === null, "aria-label": t("button"), title: trendTitle, onClick: () => setOpen(true), children: [
703
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "decision-trigger-icon", width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
704
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6.4", stroke: "currentColor", "stroke-width": "1.3" }),
705
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 4.6 11.3 11H4.7L8 4.6Z", fill: "currentColor", stroke: "none" })
735
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
736
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { ref: trigger, className: `decision-trigger decision-trend-${severity}`, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-busy": enabled === null, "aria-label": t("button"), title: trendTitle, onClick: () => setOpen(true), children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "decision-trigger-icon", width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
738
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "8", r: "6.4", stroke: "currentColor", "stroke-width": "1.3" }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M8 4.6 11.3 11H4.7L8 4.6Z", fill: "currentColor", stroke: "none" })
706
740
  ] }),
707
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-trigger-text", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-trigger-text", children: [
708
742
  t("triggerDecisions"),
709
743
  " ",
710
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: attempts })
744
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: attempts })
711
745
  ] })
712
746
  ] }),
713
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
747
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
714
748
  "dialog",
715
749
  {
716
750
  ref: dialog,
@@ -735,19 +769,19 @@ function SessionPanel({ sessionId, t }) {
735
769
  trigger.current?.focus();
736
770
  },
737
771
  children: [
738
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-head", children: [
739
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { id: "decision-panel-title", children: t("title") }),
740
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => {
772
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-head", children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { id: "decision-panel-title", children: t("title") }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => {
741
775
  dialog.current?.close();
742
776
  }, "aria-label": t("close"), children: "\xD7" })
743
777
  ] }),
744
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-body", children: [
745
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
746
- enabled === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", children: t("loading") }),
747
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-toggle", children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-text", children: t("enabled") }),
749
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-toggle-box", children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-body", children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { children: [
780
+ enabled === null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", children: t("loading") }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "decision-toggle", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-toggle-text", children: t("enabled") }),
783
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-toggle-box", children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
751
785
  "input",
752
786
  {
753
787
  className: "decision-toggle-input",
@@ -757,152 +791,118 @@ function SessionPanel({ sessionId, t }) {
757
791
  onChange: (event) => void updateEnabled(event.target.checked)
758
792
  }
759
793
  ),
760
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
794
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
761
795
  ] })
762
796
  ] }),
763
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-status", children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `decision-status-dot decision-status-${status}`, "aria-hidden": "true" }),
765
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-status-label", children: [
797
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-status", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `decision-status-dot decision-status-${status}`, "aria-hidden": "true" }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-status-label", children: [
766
800
  t("backendStatus"),
767
801
  "\uFF1A",
768
802
  statusLabel
769
803
  ] }),
770
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "decision-status-recheck", disabled: status === "checking", onClick: () => checkStatus(() => true), children: t("recheck") })
804
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "decision-status-recheck", disabled: status === "checking", onClick: () => checkStatus(), children: t("recheck") })
771
805
  ] })
772
806
  ] }),
773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
774
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("metrics") }),
775
- !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-cards", children: [
776
- metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card decision-card-wide", children: [
777
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
778
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
779
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
807
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("metrics") }),
809
+ !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-cards", children: [
810
+ metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card decision-card-wide", children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
780
814
  metrics.gate.attempts,
781
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
815
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
782
816
  ] })
783
817
  ] }),
784
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Bar, { allow: metrics.gate.allow, ask: metrics.gate.ask, deny: metrics.gate.deny }),
785
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "decision-legend", children: [
786
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
787
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-allow", "aria-hidden": "true" }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Bar, { allow: metrics.gate.allow, ask: metrics.gate.ask, deny: metrics.gate.deny }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("ul", { className: "decision-legend", children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-legend-dot decision-dot-allow", "aria-hidden": "true" }),
788
822
  t("allow"),
789
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.allow })
823
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: metrics.gate.allow })
790
824
  ] }),
791
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
792
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-ask", "aria-hidden": "true" }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
826
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-legend-dot decision-dot-ask", "aria-hidden": "true" }),
793
827
  t("ask"),
794
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.ask })
828
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: metrics.gate.ask })
795
829
  ] }),
796
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
797
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-deny", "aria-hidden": "true" }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-legend-dot decision-dot-deny", "aria-hidden": "true" }),
798
832
  t("deny"),
799
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.deny })
833
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: metrics.gate.deny })
800
834
  ] })
801
835
  ] }),
802
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("footer", { className: "decision-card-foot", children: [
803
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("footer", { className: "decision-card-foot", children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
804
838
  t("actualShort"),
805
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("b", { children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("b", { children: [
806
840
  metrics.gate.actual.allow,
807
841
  " / ",
808
842
  metrics.gate.actual.deny
809
843
  ] })
810
844
  ] }),
811
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
812
846
  t("failures"),
813
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.failures })
847
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: metrics.gate.failures })
814
848
  ] })
815
849
  ] })
816
850
  ] }),
817
- metrics.check && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
820
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
821
- metrics.check.attempts,
822
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
823
- ] })
824
- ] }),
825
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
827
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.check.low }),
828
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkLow") })
829
- ] }),
830
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
831
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.check.attempts - metrics.check.low - metrics.check.failures }),
832
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkOk") })
833
- ] }),
834
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
835
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.check.failures }),
836
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
837
- ] })
838
- ] }),
839
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
840
- t("checkPassRate"),
841
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: checkPassRate === null ? t("rateNa") : `${checkPassRate}%` })
842
- ] }) })
851
+ metrics.check && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
853
+ CardHead,
854
+ {
855
+ title: t("cardCheck"),
856
+ total: metrics.check.attempts,
857
+ unit: t("cardTimes"),
858
+ rate: { label: t("checkPassRate"), value: checkPassRate === null ? t("rateNa") : `${checkPassRate}%` }
859
+ }
860
+ ),
861
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkLow"), value: metrics.check.low, tone: "warn" }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkOk"), value: metrics.check.attempts - metrics.check.low - metrics.check.failures }),
864
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: metrics.check.failures, tone: "muted" })
865
+ ] })
843
866
  ] }),
844
- metrics.narrow && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
845
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
846
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
847
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
848
- metrics.narrow.attempts,
849
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
850
- ] })
851
- ] }),
852
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
853
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
854
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.applied }),
855
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowAppliedLabel") })
856
- ] }),
857
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
858
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.dropped }),
859
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowDroppedLabel") })
860
- ] }),
861
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
862
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.narrow.failures }),
863
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
864
- ] })
865
- ] }),
866
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
867
- t("narrowApplyRate"),
868
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: narrowApplyRate === null ? t("rateNa") : `${narrowApplyRate}%` })
869
- ] }) })
867
+ metrics.narrow && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
868
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
869
+ CardHead,
870
+ {
871
+ title: t("cardNarrow"),
872
+ total: metrics.narrow.attempts,
873
+ unit: t("cardTimes"),
874
+ rate: { label: t("narrowApplyRate"), value: narrowApplyRate === null ? t("rateNa") : `${narrowApplyRate}%` }
875
+ }
876
+ ),
877
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowAppliedLabel"), value: metrics.narrow.applied }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowDroppedLabel"), value: metrics.narrow.dropped }),
880
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: metrics.narrow.failures, tone: "muted" })
881
+ ] })
870
882
  ] }),
871
- metrics.complete && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
872
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
873
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
874
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
875
- metrics.complete.attempts,
876
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
877
- ] })
878
- ] }),
879
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
881
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.complete.satisfied }),
882
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeSatisfied") })
883
- ] }),
884
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
885
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.complete.unsatisfied }),
886
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeUnsatisfied") })
887
- ] }),
888
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
889
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.complete.insufficient }),
890
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeInsufficient") })
891
- ] })
892
- ] }),
893
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
894
- t("failures"),
895
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.complete.failures })
896
- ] }) })
883
+ metrics.complete && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
885
+ CardHead,
886
+ {
887
+ title: t("cardComplete"),
888
+ total: metrics.complete.attempts,
889
+ unit: t("cardTimes"),
890
+ rate: { label: t("failures"), value: `${metrics.complete.failures}` }
891
+ }
892
+ ),
893
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeSatisfied"), value: metrics.complete.satisfied }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeUnsatisfied"), value: metrics.complete.unsatisfied, tone: "warn" }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeInsufficient"), value: metrics.complete.insufficient, tone: "muted" })
897
+ ] })
897
898
  ] })
898
899
  ] })
899
900
  ] }),
900
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
901
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("log") }),
902
- log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
903
- analysis && analysis.annotations.rated > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: analysis.annotations.rated, good: analysis.annotations.good, bad: analysis.annotations.bad, unsure: analysis.annotations.unsure }) })
901
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { children: [
902
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("log") }),
903
+ log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t })
904
904
  ] }),
905
- message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
905
+ message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
906
906
  ] })
907
907
  ]
908
908
  }
@@ -912,13 +912,7 @@ function SessionPanel({ sessionId, t }) {
912
912
 
913
913
  // src/client/analysis-view.tsx
914
914
  var import_react3 = require("react");
915
- var import_jsx_runtime3 = require("react/jsx-runtime");
916
- function Stat({ label, value, tone }) {
917
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-stat", children: [
918
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `decision-stat-num${tone ? ` decision-stat-${tone}` : ""}`, children: value }),
919
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-stat-label", children: label })
920
- ] });
921
- }
915
+ var import_jsx_runtime4 = require("react/jsx-runtime");
922
916
  function AnalysisView({ sessionId, t }) {
923
917
  const [analysis, setAnalysis] = (0, import_react3.useState)(null);
924
918
  const [log, setLog] = (0, import_react3.useState)([]);
@@ -950,101 +944,78 @@ function AnalysisView({ sessionId, t }) {
950
944
  const backtest = analysis?.trendBacktest;
951
945
  const annotations = analysis?.annotations;
952
946
  const meanScore = profile && profile.check.scoreCount > 0 ? Math.round(profile.check.scoreSum / profile.check.scoreCount * 10) / 10 : null;
953
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "decision-analysis", role: "region", "aria-label": t("analysisTitle"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-inner", children: [
954
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
955
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") }),
956
- loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
957
- profile && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
958
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisProfile") }),
959
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-cards", children: [
960
- profile.gate.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
961
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
962
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
963
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
964
- profile.gate.attempts,
965
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
966
- ] })
967
- ] }),
968
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
969
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("deny"), value: profile.gate.deny, tone: "warn" }),
970
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("allow"), value: profile.gate.allow }),
971
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("ask"), value: profile.gate.ask }),
972
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.gate.error, tone: "muted" })
947
+ const completeTotal = profile ? profile.complete.satisfied + profile.complete.unsatisfied + profile.complete.insufficient : 0;
948
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "decision-analysis", role: "region", "aria-label": t("analysisTitle"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-inner", children: [
949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") }),
951
+ loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
952
+ profile && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("analysisProfile") }),
954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-cards", children: [
955
+ profile.gate.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("cardGate"), total: profile.gate.attempts, unit: t("cardTimes") }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("deny"), value: profile.gate.deny, pct: share(profile.gate.deny, profile.gate.attempts), tone: "warn" }),
959
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("allow"), value: profile.gate.allow, pct: share(profile.gate.allow, profile.gate.attempts) }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("ask"), value: profile.gate.ask, pct: share(profile.gate.ask, profile.gate.attempts) }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("failures"), value: profile.gate.error, pct: share(profile.gate.error, profile.gate.attempts), tone: "muted" })
973
962
  ] })
974
963
  ] }),
975
- profile.check.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
976
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
977
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
978
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
979
- profile.check.attempts,
980
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
981
- ] })
982
- ] }),
983
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
984
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkLow"), value: profile.check.low, tone: "warn" }),
985
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkOk"), value: profile.check.ok }),
986
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisMeanScore"), value: meanScore === null ? t("rateNa") : `${meanScore}/2` }),
987
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.check.error, tone: "muted" })
964
+ profile.check.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
965
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("cardCheck"), total: profile.check.attempts, unit: t("cardTimes") }),
966
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
967
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkLow"), value: profile.check.low, pct: share(profile.check.low, profile.check.attempts), tone: "warn" }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkOk"), value: profile.check.ok, pct: share(profile.check.ok, profile.check.attempts) }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("analysisMeanScore"), value: meanScore === null ? t("rateNa") : `${meanScore}/2` }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("failures"), value: profile.check.error, pct: share(profile.check.error, profile.check.attempts), tone: "muted" })
988
971
  ] })
989
972
  ] }),
990
- profile.narrow.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
991
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
992
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
993
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
994
- profile.narrow.attempts,
995
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
996
- ] })
997
- ] }),
998
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
999
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowAppliedLabel"), value: profile.narrow.applied }),
1000
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowDroppedLabel"), value: profile.narrow.droppedSum }),
1001
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.narrow.error, tone: "muted" })
973
+ profile.narrow.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("cardNarrow"), total: profile.narrow.attempts, unit: t("cardTimes") }),
975
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("narrowAppliedLabel"), value: profile.narrow.applied, pct: share(profile.narrow.applied, profile.narrow.attempts) }),
977
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("narrowDroppedLabel"), value: profile.narrow.droppedSum }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("failures"), value: profile.narrow.error, pct: share(profile.narrow.error, profile.narrow.attempts), tone: "muted" })
1002
979
  ] })
1003
980
  ] }),
1004
- profile.complete.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
1005
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
1006
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
1007
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
1008
- profile.complete.attempts,
1009
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
1010
- ] })
1011
- ] }),
1012
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
1013
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeSatisfied"), value: profile.complete.satisfied }),
1014
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeUnsatisfied"), value: profile.complete.unsatisfied, tone: "warn" }),
1015
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeInsufficient"), value: profile.complete.insufficient, tone: "muted" })
981
+ profile.complete.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("cardComplete"), total: profile.complete.attempts, unit: t("cardTimes") }),
983
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
984
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("completeSatisfied"), value: profile.complete.satisfied, pct: share(profile.complete.satisfied, completeTotal) }),
985
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("completeUnsatisfied"), value: profile.complete.unsatisfied, pct: share(profile.complete.unsatisfied, completeTotal), tone: "warn" }),
986
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("completeInsufficient"), value: profile.complete.insufficient, pct: share(profile.complete.insufficient, completeTotal), tone: "muted" })
1016
987
  ] })
1017
988
  ] })
1018
989
  ] })
1019
990
  ] }),
1020
- backtest && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1021
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisTrend") }),
1022
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats decision-analysis-trend", children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendWarn"), value: backtest.warnHits }),
1024
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendSevere"), value: backtest.severeHits, tone: "warn" }),
1025
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendMaxRun"), value: backtest.maxRun })
991
+ backtest && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
992
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("analysisTrend") }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats decision-analysis-trend", children: [
994
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("analysisTrendWarn"), value: backtest.warnHits }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("analysisTrendSevere"), value: backtest.severeHits, tone: "warn" }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("analysisTrendMaxRun"), value: backtest.maxRun })
1026
997
  ] }),
1027
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisTrendNote") })
998
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisTrendNote") })
1028
999
  ] }),
1029
- annotations && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1030
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisAccuracy") }),
1031
- annotations.rated === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted", children: t("analysisAccuracyEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: annotations.rated, good: annotations.good, bad: annotations.bad, unsure: annotations.unsure }) })
1000
+ annotations && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("analysisAccuracy") }),
1002
+ annotations.rated === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("analysisAccuracyEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: annotations.rated, good: annotations.good, bad: annotations.bad, unsure: annotations.unsure }) })
1032
1003
  ] }),
1033
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1034
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("log") }),
1035
- log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1036
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
1037
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisLogNote") })
1004
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("log") }),
1006
+ log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1007
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisLogNote") })
1038
1009
  ] })
1039
1010
  ] })
1040
1011
  ] }),
1041
- message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
1012
+ message && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
1042
1013
  ] }) });
1043
1014
  }
1044
1015
 
1045
1016
  // src/client/config-form.tsx
1046
1017
  var import_react4 = require("react");
1047
- var import_jsx_runtime4 = require("react/jsx-runtime");
1018
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1048
1019
  var api = "/plugins/dsh-decision-layer/api";
1049
1020
  async function request2(path, fetchFn, init) {
1050
1021
  const response = await fetchFn(`${api}/${path}`, init);
@@ -1061,31 +1032,31 @@ var maxCandidatesOf = (settings) => settings?.maxCandidates ?? DEFAULT_MAX_CANDI
1061
1032
  var thresholdOf = (settings) => settings?.threshold ?? DEFAULT_THRESHOLD;
1062
1033
  function HeadIcon({ name }) {
1063
1034
  const glyphs = {
1064
- features: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1065
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "3", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1066
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "11.9", cy: "5.1", r: "1.25", fill: "currentColor" }),
1067
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "8.8", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1068
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "4.1", cy: "10.9", r: "1.25", fill: "currentColor" })
1035
+ features: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1036
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2", y: "3", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "11.9", cy: "5.1", r: "1.25", fill: "currentColor" }),
1038
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2", y: "8.8", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "4.1", cy: "10.9", r: "1.25", fill: "currentColor" })
1069
1040
  ] }),
1070
- advanced: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1071
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M2.5 5h11M2.5 11h11", stroke: "currentColor", "stroke-width": "1.3", "stroke-linecap": "round" }),
1072
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "10.5", cy: "5", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" }),
1073
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5.5", cy: "11", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" })
1041
+ advanced: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1042
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M2.5 5h11M2.5 11h11", stroke: "currentColor", "stroke-width": "1.3", "stroke-linecap": "round" }),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "10.5", cy: "5", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" }),
1044
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "5.5", cy: "11", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" })
1074
1045
  ] }),
1075
- backend: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1076
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "2.6", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1077
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "8.8", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1078
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "4.9", r: ".7", fill: "currentColor" }),
1079
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "11.1", r: ".7", fill: "currentColor" })
1046
+ backend: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1047
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2.3", y: "2.6", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1048
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2.3", y: "8.8", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1049
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "5", cy: "4.9", r: ".7", fill: "currentColor" }),
1050
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "5", cy: "11.1", r: ".7", fill: "currentColor" })
1080
1051
  ] })
1081
1052
  };
1082
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `decision-head-icon decision-head-icon-${name}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", children: glyphs[name] }) });
1053
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: `decision-head-icon decision-head-icon-${name}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", children: glyphs[name] }) });
1083
1054
  }
1084
1055
  function Toggle({ label, checked, disabled, onChange }) {
1085
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-toggle", children: [
1086
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-text", children: label }),
1087
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "decision-toggle-box", children: [
1088
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1056
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-toggle", children: [
1057
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-toggle-text", children: label }),
1058
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "decision-toggle-box", children: [
1059
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1089
1060
  "input",
1090
1061
  {
1091
1062
  className: "decision-toggle-input",
@@ -1095,7 +1066,7 @@ function Toggle({ label, checked, disabled, onChange }) {
1095
1066
  onChange: (event) => onChange(event.target.checked)
1096
1067
  }
1097
1068
  ),
1098
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
1069
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
1099
1070
  ] })
1100
1071
  ] });
1101
1072
  }
@@ -1237,17 +1208,17 @@ ${url}`)) return;
1237
1208
  setBusy(false);
1238
1209
  }
1239
1210
  };
1240
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-config", children: [
1241
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", open: true, children: [
1242
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
1243
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "features" }),
1211
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-config", children: [
1212
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("details", { className: "decision-collapse", open: true, children: [
1213
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("summary", { className: "decision-collapse-head", children: [
1214
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h3", { children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HeadIcon, { name: "features" }),
1245
1216
  t("basicFeatures")
1246
1217
  ] }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1218
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1248
1219
  ] }),
1249
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-collapse-body", children: [
1250
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1220
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-collapse-body", children: [
1221
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1251
1222
  Toggle,
1252
1223
  {
1253
1224
  label: t("featureGate"),
@@ -1259,8 +1230,8 @@ ${url}`)) return;
1259
1230
  }
1260
1231
  }
1261
1232
  ),
1262
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureGateHint") }),
1263
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1233
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("featureGateHint") }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1264
1235
  Toggle,
1265
1236
  {
1266
1237
  label: t("featureCheck"),
@@ -1272,8 +1243,8 @@ ${url}`)) return;
1272
1243
  }
1273
1244
  }
1274
1245
  ),
1275
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCheckHint") }),
1276
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1246
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("featureCheckHint") }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1277
1248
  Toggle,
1278
1249
  {
1279
1250
  label: t("featureNarrow"),
@@ -1285,8 +1256,8 @@ ${url}`)) return;
1285
1256
  }
1286
1257
  }
1287
1258
  ),
1288
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureNarrowHint") }),
1289
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1259
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("featureNarrowHint") }),
1260
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1290
1261
  Toggle,
1291
1262
  {
1292
1263
  label: t("featureComplete"),
@@ -1298,22 +1269,22 @@ ${url}`)) return;
1298
1269
  }
1299
1270
  }
1300
1271
  ),
1301
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCompleteHint") })
1272
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("featureCompleteHint") })
1302
1273
  ] })
1303
1274
  ] }),
1304
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", children: [
1305
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
1306
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1307
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "advanced" }),
1275
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("details", { className: "decision-collapse", children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("summary", { className: "decision-collapse-head", children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h3", { children: [
1278
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HeadIcon, { name: "advanced" }),
1308
1279
  t("advancedConfig")
1309
1280
  ] }),
1310
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1281
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1311
1282
  ] }),
1312
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "decision-collapse-body", onSubmit: (event) => void saveAdvanced(event), children: [
1313
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-advanced-grid", children: [
1314
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
1315
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedSwitches") }),
1316
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1283
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { className: "decision-collapse-body", onSubmit: (event) => void saveAdvanced(event), children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-advanced-grid", children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-field-block", children: [
1286
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-subhead", children: t("advancedSwitches") }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1317
1288
  Toggle,
1318
1289
  {
1319
1290
  label: t("selfCheckSteer"),
@@ -1325,8 +1296,8 @@ ${url}`)) return;
1325
1296
  }
1326
1297
  }
1327
1298
  ),
1328
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("selfCheckHint") }),
1329
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1299
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("selfCheckHint") }),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1330
1301
  Toggle,
1331
1302
  {
1332
1303
  label: t("narrowObserveLabel"),
@@ -1338,8 +1309,8 @@ ${url}`)) return;
1338
1309
  }
1339
1310
  }
1340
1311
  ),
1341
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("narrowHint") }),
1342
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1312
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("narrowHint") }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1343
1314
  Toggle,
1344
1315
  {
1345
1316
  label: t("completeSteer"),
@@ -1351,13 +1322,13 @@ ${url}`)) return;
1351
1322
  }
1352
1323
  }
1353
1324
  ),
1354
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("completeHint") })
1325
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("completeHint") })
1355
1326
  ] }),
1356
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
1357
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedParams") }),
1358
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1327
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-field-block", children: [
1328
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-subhead", children: t("advancedParams") }),
1329
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field", children: [
1359
1330
  t("narrowKeepLabel"),
1360
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1331
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1361
1332
  "textarea",
1362
1333
  {
1363
1334
  className: "decision-prefixes",
@@ -1371,11 +1342,11 @@ ${url}`)) return;
1371
1342
  }
1372
1343
  }
1373
1344
  ),
1374
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowKeepHint") })
1345
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("narrowKeepHint") })
1375
1346
  ] }),
1376
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1347
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1377
1348
  t("narrowMaxLabel"),
1378
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1349
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1379
1350
  "input",
1380
1351
  {
1381
1352
  className: "decision-narrow-cap",
@@ -1392,11 +1363,11 @@ ${url}`)) return;
1392
1363
  }
1393
1364
  }
1394
1365
  ),
1395
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowMaxHint") })
1366
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("narrowMaxHint") })
1396
1367
  ] }),
1397
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1368
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1398
1369
  t("narrowThresholdLabel"),
1399
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1370
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1400
1371
  "input",
1401
1372
  {
1402
1373
  className: "decision-narrow-cap",
@@ -1413,26 +1384,26 @@ ${url}`)) return;
1413
1384
  }
1414
1385
  }
1415
1386
  ),
1416
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowThresholdHint") })
1387
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("narrowThresholdHint") })
1417
1388
  ] })
1418
1389
  ] })
1419
1390
  ] }),
1420
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "decision-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready || !advancedDirty, type: "submit", children: t("saveAdvanced") }) })
1391
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "decision-actions", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { disabled: busy || !ready || !advancedDirty, type: "submit", children: t("saveAdvanced") }) })
1421
1392
  ] })
1422
1393
  ] }),
1423
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { children: [
1424
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1425
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "backend" }),
1394
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h3", { children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HeadIcon, { name: "backend" }),
1426
1397
  t("backend")
1427
1398
  ] }),
1428
- ready && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "decision-muted decision-destination", children: [
1399
+ ready && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "decision-muted decision-destination", children: [
1429
1400
  t("destination"),
1430
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: config.effectiveUrl })
1401
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: config.effectiveUrl })
1431
1402
  ] }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit: (event) => void saveBackend(event), children: [
1433
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1403
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit: (event) => void saveBackend(event), children: [
1404
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field", children: [
1434
1405
  t("url"),
1435
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1406
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1436
1407
  "input",
1437
1408
  {
1438
1409
  type: "url",
@@ -1447,9 +1418,9 @@ ${url}`)) return;
1447
1418
  }
1448
1419
  )
1449
1420
  ] }),
1450
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field", children: [
1451
1422
  t("model"),
1452
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1423
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1453
1424
  "input",
1454
1425
  {
1455
1426
  value: config.model,
@@ -1463,9 +1434,9 @@ ${url}`)) return;
1463
1434
  }
1464
1435
  )
1465
1436
  ] }),
1466
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field", children: [
1467
1438
  t("key"),
1468
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1439
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1469
1440
  "input",
1470
1441
  {
1471
1442
  type: "password",
@@ -1480,23 +1451,23 @@ ${url}`)) return;
1480
1451
  }
1481
1452
  }
1482
1453
  ),
1483
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: config.apiKeySet ? t("keyHintSaved") : t("keyHintEmpty") })
1454
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: config.apiKeySet ? t("keyHintSaved") : t("keyHintEmpty") })
1484
1455
  ] }),
1485
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-actions", children: [
1486
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready, type: "submit", children: t("save") }),
1487
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "decision-secondary", disabled: busy || !ready || backendDirty, type: "button", onClick: () => void probe(), children: t("probe") }),
1488
- probeResult && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { role: "status", className: `decision-probe-result${probeResult.ok ? " decision-message-ok" : " decision-message-warn"}`, children: probeResult.text })
1456
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-actions", children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { disabled: busy || !ready, type: "submit", children: t("save") }),
1458
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "decision-secondary", disabled: busy || !ready || backendDirty, type: "button", onClick: () => void probe(), children: t("probe") }),
1459
+ probeResult && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "status", className: `decision-probe-result${probeResult.ok ? " decision-message-ok" : " decision-message-warn"}`, children: probeResult.text })
1489
1460
  ] })
1490
1461
  ] }),
1491
- config.apiKeySet && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-row", children: [
1492
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-text", children: [
1493
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-danger-title", children: t("removeKey") }),
1494
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("removeKeyHint") })
1462
+ config.apiKeySet && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-danger-row", children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-danger-text", children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-danger-title", children: t("removeKey") }),
1465
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("removeKeyHint") })
1495
1466
  ] }),
1496
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "decision-danger-button", disabled: busy || !ready, onClick: () => void removeSavedKey(), children: t("removeKeyButton") })
1467
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "decision-danger-button", disabled: busy || !ready, onClick: () => void removeSavedKey(), children: t("removeKeyButton") })
1497
1468
  ] })
1498
1469
  ] }),
1499
- message && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "status", className: `decision-message${message.ok ? " decision-message-ok" : " decision-message-warn"}`, children: message.text })
1470
+ message && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "status", className: `decision-message${message.ok ? " decision-message-ok" : " decision-message-warn"}`, children: message.text })
1500
1471
  ] });
1501
1472
  }
1502
1473
 
@@ -1538,6 +1509,9 @@ var css = `
1538
1509
  .decision-card-title{font-size:.8rem;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);letter-spacing:.02em}
1539
1510
  .decision-card-total{font-size:1.5rem;font-weight:700;line-height:1;color:var(--dsw-alias-label-primary,#202124);font-variant-numeric:tabular-nums}
1540
1511
  .decision-card-total small{font-size:.7rem;font-weight:500;color:var(--dsw-alias-label-tertiary,#77818b);margin-left:.15rem}
1512
+ .decision-card-head-metrics{display:flex;align-items:baseline;gap:.6rem}
1513
+ .decision-card-rate-inline{font-size:.76rem;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap}
1514
+ .decision-card-rate-inline b{color:var(--dsw-alias-label-primary,#202124);font-variant-numeric:tabular-nums;margin-left:.2rem;font-weight:600}
1541
1515
  .decision-bar{display:flex;height:8px;border-radius:999px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,#eef1f3)}
1542
1516
  .decision-bar-empty{background:var(--dsw-alias-bg-layer-1,#eef1f3)}
1543
1517
  .decision-bar-allow{background:#1f8a70}
@@ -1552,10 +1526,10 @@ var css = `
1552
1526
  .decision-dot-deny{background:#c0392b}
1553
1527
  .decision-card-foot{display:flex;justify-content:space-between;gap:1rem;margin-top:.7rem;padding-top:.6rem;border-top:1px solid var(--dsw-alias-border-l2,#eef1f3);font-size:.76rem;color:var(--dsw-alias-label-secondary,#59636e)}
1554
1528
  .decision-card-foot b{color:var(--dsw-alias-label-primary,#202124);font-variant-numeric:tabular-nums;margin-left:.3rem}
1555
- .decision-card-rate{justify-content:center}
1556
1529
  .decision-card-stats{display:flex;gap:.6rem}
1557
1530
  .decision-stat{flex:1 1 0;display:flex;flex-direction:column;align-items:center;gap:.2rem;padding:.5rem .3rem;border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f6f8fa)}
1558
1531
  .decision-stat-num{font-size:1.35rem;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#202124)}
1532
+ .decision-stat-pct{font-size:.74rem;font-weight:600;line-height:1;color:var(--dsw-alias-label-tertiary,#77818b);font-variant-numeric:tabular-nums}
1559
1533
  .decision-stat-warn{color:#c98a1e}
1560
1534
  .decision-stat-muted{color:var(--dsw-alias-label-tertiary,#9aa5b1)}
1561
1535
  .decision-stat-label{font-size:.72rem;color:var(--dsw-alias-label-secondary,#59636e)}
@@ -1691,7 +1665,7 @@ function installStyles() {
1691
1665
  }
1692
1666
 
1693
1667
  // src/client/index.tsx
1694
- var import_jsx_runtime5 = require("react/jsx-runtime");
1668
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1695
1669
  var inject = ["slots", "locale"];
1696
1670
  function apply(ctx) {
1697
1671
  const t = ctx.locale.bind("dsh-decision-layer");
@@ -1716,7 +1690,7 @@ function apply(ctx) {
1716
1690
  }, AnalysisView));
1717
1691
  ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register(
1718
1692
  { name: "plugins.bundle.config", key: "dsh-decision-layer", locale: "dsh-decision-layer" },
1719
- (props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConfigForm, { t: props.t })
1693
+ (props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConfigForm, { t: props.t })
1720
1694
  ));
1721
1695
  }
1722
1696