@hakam-aldeen-kh/blix 0.9.0 → 0.10.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 CHANGED
@@ -810,6 +810,12 @@ that one: emptying Redux leaves Network alone. Pinned entries survive it, and
810
810
  it is disabled when the source has nothing left to clear. To empty every
811
811
  source at once, use **Clear log** in the overflow menu, or `⇧C`.
812
812
 
813
+ **The launcher badge summarizes the log while the panel is closed.** It shows
814
+ one count per source that has captured anything — Network, Realtime, Redux,
815
+ Query, each with its rail icon — and, in red beside a count, how many of that
816
+ source's entries failed. The dot is capture state: green while capturing,
817
+ amber while paused, pulsing while something is in flight.
818
+
813
819
  The panel opens **floating** the first time. Dock it to the bottom or the
814
820
  right from the header; the choice is saved per project.
815
821
 
@@ -868,9 +874,12 @@ literal rather than a pre-serialized string.
868
874
  ### Exporting the log
869
875
 
870
876
  The **Export** button in the header offers six formats and a scope
871
- toggle — **Shown** (what the current section and filters leave visible) or
872
- **All**. It defaults to Shown, with both counts on the control, so an export
873
- says what it will contain before you pick a format.
877
+ toggle — **Selected** (the one entry open in the detail pane: a request, a
878
+ Redux action or a query), **Shown** (what the current section and filters
879
+ leave visible) or **All**. It defaults to Shown, with every count on the
880
+ control, so an export says what it will contain before you pick a format.
881
+ Selected is disabled until you select an entry; if it was your last choice and
882
+ nothing is selected, the menu opens on Shown instead.
874
883
 
875
884
  | | Format | For |
876
885
  | --- | --- | --- |
@@ -922,7 +922,26 @@ function Icon({ name, size = 15 }) {
922
922
  /* @__PURE__ */ jsx("ellipse", { cx: "8", cy: "4.1", rx: "5.1", ry: "2" }),
923
923
  /* @__PURE__ */ jsx("path", { d: "M2.9 4.1v3.8c0 1.1 2.3 2 5.1 2s5.1-.9 5.1-2V4.1" }),
924
924
  /* @__PURE__ */ jsx("path", { d: "M2.9 7.9v3.8c0 1.1 2.3 2 5.1 2s5.1-.9 5.1-2V7.9" })
925
- ] })
925
+ ] }),
926
+ name === "lock" && /* @__PURE__ */ jsxs(Fragment, { children: [
927
+ /* @__PURE__ */ jsx("rect", { x: "3.2", y: "7", width: "9.6", height: "6.6", rx: "1.4" }),
928
+ /* @__PURE__ */ jsx("path", { d: "M5.4 7V5.2a2.6 2.6 0 0 1 5.2 0V7" })
929
+ ] }),
930
+ name === "columns" && /* @__PURE__ */ jsxs(Fragment, { children: [
931
+ /* @__PURE__ */ jsx("rect", { x: "2.2", y: "2.8", width: "11.6", height: "10.4", rx: "1.6" }),
932
+ /* @__PURE__ */ jsx("path", { d: "M6.1 2.8v10.4M9.9 2.8v10.4" })
933
+ ] }),
934
+ name === "corner" && /* @__PURE__ */ jsxs(Fragment, { children: [
935
+ /* @__PURE__ */ jsx("path", { d: "M3 13V7.4A4.4 4.4 0 0 1 7.4 3H13" }),
936
+ /* @__PURE__ */ jsx("path", { d: "M8 13h5V8", strokeDasharray: "1.4 1.8" })
937
+ ] }),
938
+ name === "purge" && /* @__PURE__ */ jsxs(Fragment, { children: [
939
+ /* @__PURE__ */ jsx("ellipse", { cx: "7", cy: "4.1", rx: "4.6", ry: "1.8" }),
940
+ /* @__PURE__ */ jsx("path", { d: "M2.4 4.1v7.6c0 1 2 1.8 4.6 1.8" }),
941
+ /* @__PURE__ */ jsx("path", { d: "M11.6 4.1v3.1" }),
942
+ /* @__PURE__ */ jsx("path", { d: "M10 10l3.6 3.6M13.6 10 10 13.6" })
943
+ ] }),
944
+ name === "command" && /* @__PURE__ */ jsx("path", { d: "M6 6h4v4H6zM6 6V4.5A1.5 1.5 0 1 0 4.5 6H6M10 6V4.5A1.5 1.5 0 1 1 11.5 6H10M6 10v1.5A1.5 1.5 0 1 1 4.5 10H6M10 10v1.5a1.5 1.5 0 1 0 1.5-1.5H10" })
926
945
  ]
927
946
  }
928
947
  );
@@ -4592,8 +4611,130 @@ function useFabDrag(onOpen, onDock) {
4592
4611
  return { dragPos, docking, start };
4593
4612
  }
4594
4613
 
4595
- // src/ui/components/MonitorFab.tsx
4614
+ // src/ui/components/SourcesRail.tsx
4596
4615
  import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4616
+ var SECTION_DEFS = [
4617
+ { id: "network", label: "Network", hotkey: "1", title: "HTTP requests (1)", icon: "network" },
4618
+ { id: "realtime", label: "Realtime", hotkey: "2", title: "WebSocket / ActionCable connections (2)", icon: "bolt" },
4619
+ { id: "redux", label: "Redux", hotkey: "3", title: "Redux actions (3)", icon: "stack" },
4620
+ { id: "query", label: "Query", hotkey: "4", title: "TanStack Query cache activity (4)", icon: "database" }
4621
+ ];
4622
+ function SourcesRail({
4623
+ width,
4624
+ mini,
4625
+ section,
4626
+ counts,
4627
+ stats,
4628
+ onSection,
4629
+ onToggle,
4630
+ onOpenPalette
4631
+ }) {
4632
+ const rows = [
4633
+ ["Captured", String(stats.captured), ""],
4634
+ ["Transferred", formatBytes(stats.transferred), ""],
4635
+ ["Slowest", stats.slowestMs > 0 ? formatDuration(stats.slowestMs) : "\u2014", stats.slowestMs > 0 ? "nm-hot" : "nm-zero"],
4636
+ ["Failing", String(stats.failing), stats.failing > 0 ? "nm-bad" : "nm-zero"],
4637
+ ...stats.persistedLabel ? [["On disk", stats.persistedLabel, stats.persistedLabel === "0 B" ? "nm-zero" : ""]] : []
4638
+ ];
4639
+ return /* @__PURE__ */ jsxs15(
4640
+ "nav",
4641
+ {
4642
+ className: `nm-rail${mini ? " nm-rail-mini" : ""}`,
4643
+ style: { width },
4644
+ "aria-label": "Sources",
4645
+ children: [
4646
+ /* @__PURE__ */ jsxs15("div", { className: "nm-rail-head", children: [
4647
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-label", children: "SOURCES" }),
4648
+ /* @__PURE__ */ jsx15(
4649
+ "button",
4650
+ {
4651
+ type: "button",
4652
+ className: "nm-rail-collapse",
4653
+ onClick: onToggle,
4654
+ "aria-label": mini ? "Expand the sidebar" : "Collapse the sidebar",
4655
+ "aria-expanded": !mini,
4656
+ title: mini ? "Expand" : "Collapse",
4657
+ children: /* @__PURE__ */ jsx15(Icon, { name: "chevron", size: 10 })
4658
+ }
4659
+ )
4660
+ ] }),
4661
+ /* @__PURE__ */ jsx15("div", { className: "nm-rail-list", role: "tablist", "aria-orientation": "vertical", children: SECTION_DEFS.map((s) => {
4662
+ const empty = counts[s.id] === 0;
4663
+ const active = section === s.id;
4664
+ return /* @__PURE__ */ jsxs15(
4665
+ "button",
4666
+ {
4667
+ type: "button",
4668
+ role: "tab",
4669
+ "aria-selected": active,
4670
+ "data-src": s.id,
4671
+ className: `nm-rail-item${active ? " active" : ""}${empty ? " nm-rail-empty" : ""}`,
4672
+ onClick: () => onSection(s.id),
4673
+ title: `${s.title} \u2014 ${counts[s.id]}`,
4674
+ children: [
4675
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-ico", children: /* @__PURE__ */ jsx15(Icon, { name: s.icon, size: 12 }) }),
4676
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-name", children: s.label }),
4677
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-n", children: counts[s.id] }),
4678
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-key", children: s.hotkey })
4679
+ ]
4680
+ },
4681
+ s.id
4682
+ );
4683
+ }) }),
4684
+ /* @__PURE__ */ jsxs15("div", { className: "nm-rail-stats", children: [
4685
+ /* @__PURE__ */ jsx15("div", { className: "nm-rail-stats-head", children: "SESSION" }),
4686
+ rows.map(([k, v, tone]) => /* @__PURE__ */ jsxs15("div", { className: "nm-rail-stat", children: [
4687
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-stat-k", children: k }),
4688
+ /* @__PURE__ */ jsx15("span", { className: `nm-rail-stat-v ${tone}`, children: v })
4689
+ ] }, k))
4690
+ ] }),
4691
+ /* @__PURE__ */ jsx15("div", { className: "nm-rail-spacer" }),
4692
+ /* @__PURE__ */ jsx15("div", { className: "nm-rail-cmdwrap", children: /* @__PURE__ */ jsx15(
4693
+ "button",
4694
+ {
4695
+ type: "button",
4696
+ className: "nm-rail-cmd",
4697
+ onClick: onOpenPalette,
4698
+ "aria-label": "All commands",
4699
+ title: "All commands (\u2318K)",
4700
+ children: mini ? /* @__PURE__ */ jsx15(Icon, { name: "search", size: 12 }) : /* @__PURE__ */ jsxs15(Fragment9, { children: [
4701
+ /* @__PURE__ */ jsx15("span", { children: "All commands" }),
4702
+ /* @__PURE__ */ jsx15("span", { className: "nm-rail-key", children: "\u2318K" })
4703
+ ] })
4704
+ }
4705
+ ) })
4706
+ ]
4707
+ }
4708
+ );
4709
+ }
4710
+
4711
+ // src/ui/components/MonitorFab.tsx
4712
+ import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4713
+ function SourceCounts({
4714
+ counts,
4715
+ errors
4716
+ }) {
4717
+ const present = SECTION_DEFS.filter((s) => counts[s.id] > 0);
4718
+ if (present.length === 0) return /* @__PURE__ */ jsx16("span", { className: "nm-fab-count", children: "0" });
4719
+ return /* @__PURE__ */ jsx16(Fragment10, { children: present.map((s) => {
4720
+ var _a;
4721
+ const failed = (_a = errors == null ? void 0 : errors[s.id]) != null ? _a : 0;
4722
+ return /* @__PURE__ */ jsxs16(
4723
+ "span",
4724
+ {
4725
+ className: "nm-fab-src",
4726
+ "data-src": s.id,
4727
+ title: `${counts[s.id]} ${s.label}${failed > 0 ? `, ${failed} failing` : ""}`,
4728
+ children: [
4729
+ /* @__PURE__ */ jsx16(Icon, { name: s.icon, size: 11 }),
4730
+ counts[s.id],
4731
+ failed > 0 && /* @__PURE__ */ jsx16("span", { className: "nm-fab-err", children: failed })
4732
+ ]
4733
+ },
4734
+ s.id
4735
+ );
4736
+ }) });
4737
+ }
4597
4738
  var CORNER_STYLE = {
4598
4739
  "bottom-left": { bottom: MARGIN, left: MARGIN },
4599
4740
  "bottom-right": { bottom: MARGIN, right: MARGIN },
@@ -4610,11 +4751,13 @@ var BADGE_LABEL = "blix";
4610
4751
  function MonitorFab({
4611
4752
  paused,
4612
4753
  total,
4754
+ counts,
4755
+ sourceErrors,
4613
4756
  errors,
4614
4757
  pending,
4615
4758
  onPointerDown
4616
4759
  }) {
4617
- return /* @__PURE__ */ jsxs15(
4760
+ return /* @__PURE__ */ jsxs16(
4618
4761
  "button",
4619
4762
  {
4620
4763
  type: "button",
@@ -4625,16 +4768,13 @@ function MonitorFab({
4625
4768
  "aria-label": `Blix \u2014 ${total} captured${errors > 0 ? `, ${errors} failing` : ""}`,
4626
4769
  title: "Blix \u2014 click to open, drag to a corner",
4627
4770
  children: [
4628
- /* @__PURE__ */ jsxs15("span", { className: "nm-fab-brand", children: [
4629
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-dot" }),
4630
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-label", children: BADGE_LABEL })
4631
- ] }),
4632
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-sep" }),
4633
- /* @__PURE__ */ jsxs15("span", { className: "nm-fab-stats", children: [
4634
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-count", title: `${total} captured`, children: total }),
4635
- errors > 0 && /* @__PURE__ */ jsx15("span", { className: "nm-fab-err", title: `${errors} failing`, children: errors })
4771
+ /* @__PURE__ */ jsxs16("span", { className: "nm-fab-brand", children: [
4772
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-dot" }),
4773
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-label", children: BADGE_LABEL })
4636
4774
  ] }),
4637
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-grip", "aria-hidden": true, children: /* @__PURE__ */ jsx15(Icon, { name: "grip", size: 12 }) })
4775
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-sep" }),
4776
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-stats", children: /* @__PURE__ */ jsx16(SourceCounts, { counts, errors: sourceErrors }) }),
4777
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-grip", "aria-hidden": true, children: /* @__PURE__ */ jsx16(Icon, { name: "grip", size: 12 }) })
4638
4778
  ]
4639
4779
  }
4640
4780
  );
@@ -4642,11 +4782,11 @@ function MonitorFab({
4642
4782
  function FabDragPreview({
4643
4783
  pos,
4644
4784
  paused,
4645
- total
4785
+ counts
4646
4786
  }) {
4647
4787
  const target = nearestCorner(pos.x, pos.y);
4648
- return /* @__PURE__ */ jsxs15(Fragment9, { children: [
4649
- CORNERS.map((c) => /* @__PURE__ */ jsx15(
4788
+ return /* @__PURE__ */ jsxs16(Fragment10, { children: [
4789
+ CORNERS.map((c) => /* @__PURE__ */ jsx16(
4650
4790
  "span",
4651
4791
  {
4652
4792
  className: `nm-zone${target === c.id ? " active" : ""}`,
@@ -4655,19 +4795,19 @@ function FabDragPreview({
4655
4795
  },
4656
4796
  c.id
4657
4797
  )),
4658
- /* @__PURE__ */ jsxs15(
4798
+ /* @__PURE__ */ jsxs16(
4659
4799
  "span",
4660
4800
  {
4661
4801
  className: "nm-fab nm-fab-ghost",
4662
4802
  "data-paused": paused || void 0,
4663
4803
  style: { left: pos.x, top: pos.y },
4664
4804
  children: [
4665
- /* @__PURE__ */ jsxs15("span", { className: "nm-fab-brand", children: [
4666
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-dot" }),
4667
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-label", children: BADGE_LABEL })
4805
+ /* @__PURE__ */ jsxs16("span", { className: "nm-fab-brand", children: [
4806
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-dot" }),
4807
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-label", children: BADGE_LABEL })
4668
4808
  ] }),
4669
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-sep" }),
4670
- /* @__PURE__ */ jsx15("span", { className: "nm-fab-stats", children: /* @__PURE__ */ jsx15("span", { className: "nm-fab-count", children: total }) })
4809
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-sep" }),
4810
+ /* @__PURE__ */ jsx16("span", { className: "nm-fab-stats", children: /* @__PURE__ */ jsx16(SourceCounts, { counts }) })
4671
4811
  ]
4672
4812
  }
4673
4813
  )
@@ -4676,7 +4816,7 @@ function FabDragPreview({
4676
4816
 
4677
4817
  // src/ui/components/MonitorToolbar.tsx
4678
4818
  import { useRef as useRef6 } from "react";
4679
- import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4819
+ import { Fragment as Fragment11, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
4680
4820
  var DOCKS = [
4681
4821
  { mode: "bottom", icon: "dock-bottom", title: "Dock to the bottom" },
4682
4822
  { mode: "right", icon: "dock-right", title: "Dock to the right" },
@@ -4728,15 +4868,15 @@ function MonitorToolbar(props) {
4728
4868
  right: Math.max(8, window.innerWidth - rect.right)
4729
4869
  } : null;
4730
4870
  };
4731
- return /* @__PURE__ */ jsxs16(
4871
+ return /* @__PURE__ */ jsxs17(
4732
4872
  "div",
4733
4873
  {
4734
4874
  className: `nm-header${mode === "float" && !maximized ? " nm-draggable" : ""}`,
4735
4875
  onPointerDown,
4736
4876
  children: [
4737
- mode === "float" && !maximized && /* @__PURE__ */ jsx16("button", { type: "button", className: "nm-grip", "aria-label": "Move panel", children: /* @__PURE__ */ jsx16(Icon, { name: "grip", size: 12 }) }),
4738
- /* @__PURE__ */ jsx16("span", { className: "nm-title", children: "blix" }),
4739
- /* @__PURE__ */ jsxs16(
4877
+ mode === "float" && !maximized && /* @__PURE__ */ jsx17("button", { type: "button", className: "nm-grip", "aria-label": "Move panel", children: /* @__PURE__ */ jsx17(Icon, { name: "grip", size: 12 }) }),
4878
+ /* @__PURE__ */ jsx17("span", { className: "nm-title", children: "blix" }),
4879
+ /* @__PURE__ */ jsxs17(
4740
4880
  "button",
4741
4881
  {
4742
4882
  type: "button",
@@ -4746,14 +4886,14 @@ function MonitorToolbar(props) {
4746
4886
  "aria-pressed": paused,
4747
4887
  title: paused ? "Resume capturing (Space)" : "Pause capturing (Space)",
4748
4888
  children: [
4749
- /* @__PURE__ */ jsx16("span", { className: "nm-chip-dot" }),
4750
- /* @__PURE__ */ jsx16("span", { children: paused ? "paused" : "capturing" })
4889
+ /* @__PURE__ */ jsx17("span", { className: "nm-chip-dot" }),
4890
+ /* @__PURE__ */ jsx17("span", { children: paused ? "paused" : "capturing" })
4751
4891
  ]
4752
4892
  }
4753
4893
  ),
4754
- /* @__PURE__ */ jsxs16("div", { className: "nm-filter", onPointerDown: (e) => e.stopPropagation(), children: [
4755
- /* @__PURE__ */ jsx16("span", { className: "nm-filter-ico", "aria-hidden": true, children: /* @__PURE__ */ jsx16(Icon, { name: "search", size: 11 }) }),
4756
- /* @__PURE__ */ jsx16(
4894
+ /* @__PURE__ */ jsxs17("div", { className: "nm-filter", onPointerDown: (e) => e.stopPropagation(), children: [
4895
+ /* @__PURE__ */ jsx17("span", { className: "nm-filter-ico", "aria-hidden": true, children: /* @__PURE__ */ jsx17(Icon, { name: "search", size: 11 }) }),
4896
+ /* @__PURE__ */ jsx17(
4757
4897
  "input",
4758
4898
  {
4759
4899
  ref: searchRef,
@@ -4765,13 +4905,13 @@ function MonitorToolbar(props) {
4765
4905
  placeholder: "method:post is:error route:tickets -cached"
4766
4906
  }
4767
4907
  ),
4768
- searching && /* @__PURE__ */ jsxs16(Fragment10, { children: [
4769
- /* @__PURE__ */ jsxs16("span", { className: "nm-filter-count", children: [
4908
+ searching && /* @__PURE__ */ jsxs17(Fragment11, { children: [
4909
+ /* @__PURE__ */ jsxs17("span", { className: "nm-filter-count", children: [
4770
4910
  shown,
4771
4911
  " of ",
4772
4912
  total
4773
4913
  ] }),
4774
- /* @__PURE__ */ jsx16(
4914
+ /* @__PURE__ */ jsx17(
4775
4915
  "button",
4776
4916
  {
4777
4917
  type: "button",
@@ -4783,7 +4923,7 @@ function MonitorToolbar(props) {
4783
4923
  }
4784
4924
  )
4785
4925
  ] }),
4786
- /* @__PURE__ */ jsx16(
4926
+ /* @__PURE__ */ jsx17(
4787
4927
  "button",
4788
4928
  {
4789
4929
  type: "button",
@@ -4795,9 +4935,9 @@ function MonitorToolbar(props) {
4795
4935
  children: ".*"
4796
4936
  }
4797
4937
  ),
4798
- /* @__PURE__ */ jsx16("button", { type: "button", className: "nm-kbd", onClick: onOpenPalette, title: "Command palette", children: "\u2318K" })
4938
+ /* @__PURE__ */ jsx17("button", { type: "button", className: "nm-kbd", onClick: onOpenPalette, title: "Command palette", children: "\u2318K" })
4799
4939
  ] }),
4800
- /* @__PURE__ */ jsxs16(
4940
+ /* @__PURE__ */ jsxs17(
4801
4941
  "button",
4802
4942
  {
4803
4943
  type: "button",
@@ -4807,13 +4947,13 @@ function MonitorToolbar(props) {
4807
4947
  "aria-pressed": preserveLog,
4808
4948
  title: preserveLog ? "Preserve log is on \u2014 captured traffic survives a reload (Shift+L)" : "Preserve log \u2014 keep captured traffic across reloads (Shift+L)",
4809
4949
  children: [
4810
- /* @__PURE__ */ jsx16("span", { className: "nm-chip-ico", "aria-hidden": true, children: /* @__PURE__ */ jsx16(Icon, { name: "preserve", size: 10 }) }),
4811
- /* @__PURE__ */ jsx16("span", { children: preserveLog ? "preserving" : "preserve" })
4950
+ /* @__PURE__ */ jsx17("span", { className: "nm-chip-ico", "aria-hidden": true, children: /* @__PURE__ */ jsx17(Icon, { name: "preserve", size: 10 }) }),
4951
+ /* @__PURE__ */ jsx17("span", { children: preserveLog ? "preserving" : "preserve" })
4812
4952
  ]
4813
4953
  }
4814
4954
  ),
4815
- /* @__PURE__ */ jsxs16("div", { className: "nm-actions", children: [
4816
- /* @__PURE__ */ jsx16(
4955
+ /* @__PURE__ */ jsxs17("div", { className: "nm-actions", children: [
4956
+ /* @__PURE__ */ jsx17(
4817
4957
  "button",
4818
4958
  {
4819
4959
  type: "button",
@@ -4825,10 +4965,10 @@ function MonitorToolbar(props) {
4825
4965
  "aria-haspopup": "menu",
4826
4966
  "aria-expanded": exportOpen,
4827
4967
  title: "Export the log",
4828
- children: /* @__PURE__ */ jsx16(Icon, { name: "download", size: 13 })
4968
+ children: /* @__PURE__ */ jsx17(Icon, { name: "download", size: 13 })
4829
4969
  }
4830
4970
  ),
4831
- /* @__PURE__ */ jsx16(
4971
+ /* @__PURE__ */ jsx17(
4832
4972
  "button",
4833
4973
  {
4834
4974
  type: "button",
@@ -4837,10 +4977,10 @@ function MonitorToolbar(props) {
4837
4977
  disabled: clearDisabled,
4838
4978
  "aria-label": `Clear the ${sectionLabel} log`,
4839
4979
  title: `Clear the ${sectionLabel} log \u2014 other sources and pinned entries are kept`,
4840
- children: /* @__PURE__ */ jsx16(Icon, { name: "clear", size: 13 })
4980
+ children: /* @__PURE__ */ jsx17(Icon, { name: "clear", size: 13 })
4841
4981
  }
4842
4982
  ),
4843
- /* @__PURE__ */ jsx16(
4983
+ /* @__PURE__ */ jsx17(
4844
4984
  "button",
4845
4985
  {
4846
4986
  type: "button",
@@ -4851,10 +4991,10 @@ function MonitorToolbar(props) {
4851
4991
  "aria-haspopup": "menu",
4852
4992
  "aria-expanded": themeOpen,
4853
4993
  title: `Appearance \u2014 ${themeLabel}`,
4854
- children: /* @__PURE__ */ jsx16(Icon, { name: "theme", size: 13 })
4994
+ children: /* @__PURE__ */ jsx17(Icon, { name: "theme", size: 13 })
4855
4995
  }
4856
4996
  ),
4857
- /* @__PURE__ */ jsx16(
4997
+ /* @__PURE__ */ jsx17(
4858
4998
  "button",
4859
4999
  {
4860
5000
  type: "button",
@@ -4865,14 +5005,14 @@ function MonitorToolbar(props) {
4865
5005
  "aria-haspopup": "menu",
4866
5006
  "aria-expanded": moreOpen,
4867
5007
  title: "More options",
4868
- children: /* @__PURE__ */ jsx16(Icon, { name: "more", size: 13 })
5008
+ children: /* @__PURE__ */ jsx17(Icon, { name: "more", size: 13 })
4869
5009
  }
4870
5010
  )
4871
5011
  ] }),
4872
- !tiny && /* @__PURE__ */ jsxs16(Fragment10, { children: [
4873
- /* @__PURE__ */ jsx16("span", { className: "nm-hsep" }),
4874
- /* @__PURE__ */ jsxs16("div", { className: "nm-actions", children: [
4875
- DOCKS.map((d) => /* @__PURE__ */ jsx16(
5012
+ !tiny && /* @__PURE__ */ jsxs17(Fragment11, { children: [
5013
+ /* @__PURE__ */ jsx17("span", { className: "nm-hsep" }),
5014
+ /* @__PURE__ */ jsxs17("div", { className: "nm-actions", children: [
5015
+ DOCKS.map((d) => /* @__PURE__ */ jsx17(
4876
5016
  "button",
4877
5017
  {
4878
5018
  type: "button",
@@ -4881,11 +5021,11 @@ function MonitorToolbar(props) {
4881
5021
  "aria-label": d.title,
4882
5022
  "aria-pressed": mode === d.mode,
4883
5023
  title: d.title,
4884
- children: /* @__PURE__ */ jsx16(Icon, { name: d.icon, size: 13 })
5024
+ children: /* @__PURE__ */ jsx17(Icon, { name: d.icon, size: 13 })
4885
5025
  },
4886
5026
  d.mode
4887
5027
  )),
4888
- !compact && mode === "float" && /* @__PURE__ */ jsx16(
5028
+ !compact && mode === "float" && /* @__PURE__ */ jsx17(
4889
5029
  "button",
4890
5030
  {
4891
5031
  type: "button",
@@ -4893,10 +5033,10 @@ function MonitorToolbar(props) {
4893
5033
  onClick: onToggleMaximize,
4894
5034
  "aria-label": maximized ? "Restore" : "Maximize",
4895
5035
  title: maximized ? "Restore" : "Maximize",
4896
- children: /* @__PURE__ */ jsx16(Icon, { name: maximized ? "restore" : "maximize", size: 13 })
5036
+ children: /* @__PURE__ */ jsx17(Icon, { name: maximized ? "restore" : "maximize", size: 13 })
4897
5037
  }
4898
5038
  ),
4899
- /* @__PURE__ */ jsx16(
5039
+ /* @__PURE__ */ jsx17(
4900
5040
  "button",
4901
5041
  {
4902
5042
  type: "button",
@@ -4904,7 +5044,7 @@ function MonitorToolbar(props) {
4904
5044
  onClick: onClose,
4905
5045
  "aria-label": "Close the panel",
4906
5046
  title: "Close (Esc)",
4907
- children: /* @__PURE__ */ jsx16(Icon, { name: "close", size: 13 })
5047
+ children: /* @__PURE__ */ jsx17(Icon, { name: "close", size: 13 })
4908
5048
  }
4909
5049
  )
4910
5050
  ] })
@@ -4916,7 +5056,7 @@ function MonitorToolbar(props) {
4916
5056
 
4917
5057
  // src/ui/components/RequestTable.tsx
4918
5058
  import { memo, useEffect as useEffect6, useRef as useRef7 } from "react";
4919
- import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
5059
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4920
5060
  var COLUMNS = {
4921
5061
  network: ["STATUS", "TIME"],
4922
5062
  realtime: ["STATE", "OPEN"],
@@ -4979,7 +5119,7 @@ var RequestRow = memo(function RequestRow2({
4979
5119
  const route = splitRoute(entry);
4980
5120
  const note = rowNote(entry);
4981
5121
  const slow = ((_a = entry.durationMs) != null ? _a : 0) >= 1e3;
4982
- return /* @__PURE__ */ jsxs17(
5122
+ return /* @__PURE__ */ jsxs18(
4983
5123
  "div",
4984
5124
  {
4985
5125
  role: "row",
@@ -4998,8 +5138,8 @@ var RequestRow = memo(function RequestRow2({
4998
5138
  onContextMenu: (e) => onContextMenu(e, entry),
4999
5139
  title: `${route.head}${route.tail}`,
5000
5140
  children: [
5001
- /* @__PURE__ */ jsx17("span", { className: `nm-row-rail${forced ? " nm-row-rail-forced" : ""}`, "aria-hidden": true }),
5002
- /* @__PURE__ */ jsx17("span", { className: "nm-col-pin", "data-col": "pin", role: "gridcell", children: /* @__PURE__ */ jsx17(
5141
+ /* @__PURE__ */ jsx18("span", { className: `nm-row-rail${forced ? " nm-row-rail-forced" : ""}`, "aria-hidden": true }),
5142
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-pin", "data-col": "pin", role: "gridcell", children: /* @__PURE__ */ jsx18(
5003
5143
  "button",
5004
5144
  {
5005
5145
  type: "button",
@@ -5012,10 +5152,10 @@ var RequestRow = memo(function RequestRow2({
5012
5152
  "aria-pressed": entry.pinned,
5013
5153
  title: entry.pinned ? "Unpin" : "Pin (survives Clear)",
5014
5154
  tabIndex: tabbable ? 0 : -1,
5015
- children: /* @__PURE__ */ jsx17(Icon, { name: "pin", size: 10 })
5155
+ children: /* @__PURE__ */ jsx18(Icon, { name: "pin", size: 10 })
5016
5156
  }
5017
5157
  ) }),
5018
- /* @__PURE__ */ jsx17(
5158
+ /* @__PURE__ */ jsx18(
5019
5159
  "span",
5020
5160
  {
5021
5161
  className: "nm-col-method",
@@ -5025,21 +5165,21 @@ var RequestRow = memo(function RequestRow2({
5025
5165
  children: methodLabel(entry)
5026
5166
  }
5027
5167
  ),
5028
- /* @__PURE__ */ jsxs17("span", { className: "nm-col-route", role: "gridcell", children: [
5029
- /* @__PURE__ */ jsx17("span", { className: "nm-route-head", children: route.head }),
5030
- /* @__PURE__ */ jsxs17("span", { className: "nm-route-tail", children: [
5168
+ /* @__PURE__ */ jsxs18("span", { className: "nm-col-route", role: "gridcell", children: [
5169
+ /* @__PURE__ */ jsx18("span", { className: "nm-route-head", children: route.head }),
5170
+ /* @__PURE__ */ jsxs18("span", { className: "nm-route-tail", children: [
5031
5171
  route.tail,
5032
- note && /* @__PURE__ */ jsx17("span", { className: "nm-route-note", children: note })
5172
+ note && /* @__PURE__ */ jsx18("span", { className: "nm-route-note", children: note })
5033
5173
  ] })
5034
5174
  ] }),
5035
- /* @__PURE__ */ jsx17("span", { className: "nm-col-size", "data-col": "size", role: "gridcell", children: sizeLabel(entry) }),
5036
- /* @__PURE__ */ jsx17("span", { className: "nm-col-status", "data-col": "status", role: "gridcell", children: entryStatusLabel(entry) }),
5037
- /* @__PURE__ */ jsx17("span", { className: "nm-col-time", "data-col": "time", role: "gridcell", "data-slow": slow || void 0, children: timeLabel(entry) }),
5038
- /* @__PURE__ */ jsx17("span", { className: "nm-col-links", "data-col": "links", role: "gridcell", children: linkTags ? linkTags.split(",").map((s) => /* @__PURE__ */ jsx17("span", { className: "nm-link-tick", "data-src": s, title: `Linked ${s} event` }, s)) : null }),
5039
- /* @__PURE__ */ jsx17("span", { className: "nm-col-timing", "data-col": "timing", role: "gridcell", children: /* @__PURE__ */ jsxs17("span", { className: "nm-tbar", "aria-hidden": true, children: [
5040
- /* @__PURE__ */ jsx17("span", { className: "nm-tbar-off", style: { width: `${timing.offsetPct}%` } }),
5041
- /* @__PURE__ */ jsx17("span", { className: "nm-tbar-wait", style: { width: `${timing.waitPct}%` } }),
5042
- /* @__PURE__ */ jsx17("span", { className: "nm-tbar-xfer", style: { width: `${timing.transferPct}%` } })
5175
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-size", "data-col": "size", role: "gridcell", children: sizeLabel(entry) }),
5176
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-status", "data-col": "status", role: "gridcell", children: entryStatusLabel(entry) }),
5177
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-time", "data-col": "time", role: "gridcell", "data-slow": slow || void 0, children: timeLabel(entry) }),
5178
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-links", "data-col": "links", role: "gridcell", children: linkTags ? linkTags.split(",").map((s) => /* @__PURE__ */ jsx18("span", { className: "nm-link-tick", "data-src": s, title: `Linked ${s} event` }, s)) : null }),
5179
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-timing", "data-col": "timing", role: "gridcell", children: /* @__PURE__ */ jsxs18("span", { className: "nm-tbar", "aria-hidden": true, children: [
5180
+ /* @__PURE__ */ jsx18("span", { className: "nm-tbar-off", style: { width: `${timing.offsetPct}%` } }),
5181
+ /* @__PURE__ */ jsx18("span", { className: "nm-tbar-wait", style: { width: `${timing.waitPct}%` } }),
5182
+ /* @__PURE__ */ jsx18("span", { className: "nm-tbar-xfer", style: { width: `${timing.transferPct}%` } })
5043
5183
  ] }) })
5044
5184
  ]
5045
5185
  }
@@ -5094,7 +5234,7 @@ function RequestTable({
5094
5234
  ];
5095
5235
  if (counts.aborted > 0) segments.push(["aborted", "Aborted", counts.aborted]);
5096
5236
  const ticks = axisTicks(scaleMs);
5097
- const header = (key, label, className, col) => /* @__PURE__ */ jsxs17(
5237
+ const header = (key, label, className, col) => /* @__PURE__ */ jsxs18(
5098
5238
  "button",
5099
5239
  {
5100
5240
  type: "button",
@@ -5106,12 +5246,12 @@ function RequestTable({
5106
5246
  title: `Sort by ${label.toLowerCase()}`,
5107
5247
  children: [
5108
5248
  label,
5109
- sort.key === key && /* @__PURE__ */ jsx17("span", { className: "nm-sort-arrow", children: sort.dir === "asc" ? "\u25B2" : "\u25BC" })
5249
+ sort.key === key && /* @__PURE__ */ jsx18("span", { className: "nm-sort-arrow", children: sort.dir === "asc" ? "\u25B2" : "\u25BC" })
5110
5250
  ]
5111
5251
  }
5112
5252
  );
5113
- return /* @__PURE__ */ jsxs17("div", { className: "nm-table", "data-hide": hidden.size ? [...hidden].join(" ") : void 0, children: [
5114
- /* @__PURE__ */ jsx17("div", { className: "nm-fchips nm-strip-scroll", role: "tablist", children: segments.map(([key, label, n]) => /* @__PURE__ */ jsxs17(
5253
+ return /* @__PURE__ */ jsxs18("div", { className: "nm-table", "data-hide": hidden.size ? [...hidden].join(" ") : void 0, children: [
5254
+ /* @__PURE__ */ jsx18("div", { className: "nm-fchips nm-strip-scroll", role: "tablist", children: segments.map(([key, label, n]) => /* @__PURE__ */ jsxs18(
5115
5255
  "button",
5116
5256
  {
5117
5257
  type: "button",
@@ -5122,15 +5262,15 @@ function RequestTable({
5122
5262
  onClick: () => onStateFilter(key),
5123
5263
  title: `${label} \u2014 ${n} ${n === 1 ? "entry" : "entries"}`,
5124
5264
  children: [
5125
- /* @__PURE__ */ jsx17("span", { className: "nm-fchip-dot" }),
5126
- /* @__PURE__ */ jsx17("span", { className: "nm-fchip-label", children: label }),
5127
- /* @__PURE__ */ jsx17("span", { className: "nm-fchip-n", children: n })
5265
+ /* @__PURE__ */ jsx18("span", { className: "nm-fchip-dot" }),
5266
+ /* @__PURE__ */ jsx18("span", { className: "nm-fchip-label", children: label }),
5267
+ /* @__PURE__ */ jsx18("span", { className: "nm-fchip-n", children: n })
5128
5268
  ]
5129
5269
  },
5130
5270
  key
5131
5271
  )) }),
5132
- /* @__PURE__ */ jsxs17("div", { className: "nm-grid", role: "grid", "aria-label": "Captured entries", ref: gridRef, children: [
5133
- /* @__PURE__ */ jsxs17(
5272
+ /* @__PURE__ */ jsxs18("div", { className: "nm-grid", role: "grid", "aria-label": "Captured entries", ref: gridRef, children: [
5273
+ /* @__PURE__ */ jsxs18(
5134
5274
  "div",
5135
5275
  {
5136
5276
  className: "nm-cols",
@@ -5141,32 +5281,32 @@ function RequestTable({
5141
5281
  },
5142
5282
  title: "Right-click to choose columns",
5143
5283
  children: [
5144
- /* @__PURE__ */ jsx17("span", { className: "nm-col-lead", "data-col": "pin", role: "columnheader", "aria-label": "Pin" }),
5145
- /* @__PURE__ */ jsx17("span", { className: "nm-col-method", "data-col": "method", role: "columnheader", children: "METHOD" }),
5146
- /* @__PURE__ */ jsx17("span", { className: "nm-col-route", role: "columnheader", children: "ROUTE" }),
5147
- /* @__PURE__ */ jsx17("span", { className: "nm-col-size", "data-col": "size", role: "columnheader", children: "SIZE" }),
5284
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-lead", "data-col": "pin", role: "columnheader", "aria-label": "Pin" }),
5285
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-method", "data-col": "method", role: "columnheader", children: "METHOD" }),
5286
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-route", role: "columnheader", children: "ROUTE" }),
5287
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-size", "data-col": "size", role: "columnheader", children: "SIZE" }),
5148
5288
  header("status", statusCol, "nm-col-status", "status"),
5149
5289
  header("duration", timeCol, "nm-col-time", "time"),
5150
- /* @__PURE__ */ jsx17("span", { className: "nm-col-links", "data-col": "links", role: "columnheader", "aria-label": "Linked entries" }),
5151
- /* @__PURE__ */ jsxs17("span", { className: "nm-col-timing nm-axis", "data-col": "timing", role: "columnheader", children: [
5152
- /* @__PURE__ */ jsx17("span", { children: ticks[0] }),
5153
- /* @__PURE__ */ jsx17("span", { children: ticks[1] }),
5154
- /* @__PURE__ */ jsx17("span", { children: ticks[2] })
5290
+ /* @__PURE__ */ jsx18("span", { className: "nm-col-links", "data-col": "links", role: "columnheader", "aria-label": "Linked entries" }),
5291
+ /* @__PURE__ */ jsxs18("span", { className: "nm-col-timing nm-axis", "data-col": "timing", role: "columnheader", children: [
5292
+ /* @__PURE__ */ jsx18("span", { children: ticks[0] }),
5293
+ /* @__PURE__ */ jsx18("span", { children: ticks[1] }),
5294
+ /* @__PURE__ */ jsx18("span", { children: ticks[2] })
5155
5295
  ] })
5156
5296
  ]
5157
5297
  }
5158
5298
  ),
5159
- /* @__PURE__ */ jsx17(
5299
+ /* @__PURE__ */ jsx18(
5160
5300
  "div",
5161
5301
  {
5162
5302
  className: "nm-tbody nm-scroll",
5163
5303
  role: rows.length ? "rowgroup" : "presentation",
5164
5304
  ref: scrollerRef,
5165
5305
  onScroll: virtual.onScroll,
5166
- children: rows.length === 0 ? empty : /* @__PURE__ */ jsx17("div", { style: { height: virtual.totalHeight, position: "relative" }, role: "presentation", children: /* @__PURE__ */ jsx17("div", { style: { transform: `translateY(${virtual.offsetY}px)` }, role: "presentation", children: slice.map(
5306
+ children: rows.length === 0 ? empty : /* @__PURE__ */ jsx18("div", { style: { height: virtual.totalHeight, position: "relative" }, role: "presentation", children: /* @__PURE__ */ jsx18("div", { style: { transform: `translateY(${virtual.offsetY}px)` }, role: "presentation", children: slice.map(
5167
5307
  (row, i) => {
5168
5308
  var _a2, _b2;
5169
- return row.kind === "divider" ? /* @__PURE__ */ jsx17("div", { className: "nm-load-divider", role: "presentation", children: /* @__PURE__ */ jsx17("span", { children: "page load" }) }, `d-${row.loadId}`) : /* @__PURE__ */ jsx17(
5309
+ return row.kind === "divider" ? /* @__PURE__ */ jsx18("div", { className: "nm-load-divider", role: "presentation", children: /* @__PURE__ */ jsx18("span", { children: "page load" }) }, `d-${row.loadId}`) : /* @__PURE__ */ jsx18(
5170
5310
  RequestRow,
5171
5311
  {
5172
5312
  entry: row.entry,
@@ -5194,7 +5334,7 @@ function RequestTable({
5194
5334
  }
5195
5335
 
5196
5336
  // src/ui/components/ShortcutsSheet.tsx
5197
- import { Fragment as Fragment11, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
5337
+ import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
5198
5338
  var SHORTCUT_GROUPS = [
5199
5339
  {
5200
5340
  title: "PANEL",
@@ -5268,9 +5408,9 @@ function ShortcutsSheet({ onClose }) {
5268
5408
  SHORTCUT_GROUPS.filter((g) => g.col === 0),
5269
5409
  SHORTCUT_GROUPS.filter((g) => g.col === 1)
5270
5410
  ];
5271
- return /* @__PURE__ */ jsxs18(Fragment11, { children: [
5272
- /* @__PURE__ */ jsx18("div", { className: "nm-sheet-scrim", onClick: onClose }),
5273
- /* @__PURE__ */ jsxs18(
5411
+ return /* @__PURE__ */ jsxs19(Fragment12, { children: [
5412
+ /* @__PURE__ */ jsx19("div", { className: "nm-sheet-scrim", onClick: onClose }),
5413
+ /* @__PURE__ */ jsxs19(
5274
5414
  "div",
5275
5415
  {
5276
5416
  className: "nm-sheet",
@@ -5279,31 +5419,31 @@ function ShortcutsSheet({ onClose }) {
5279
5419
  "aria-label": "Keyboard shortcuts",
5280
5420
  onPointerDown: (e) => e.stopPropagation(),
5281
5421
  children: [
5282
- /* @__PURE__ */ jsxs18("div", { className: "nm-sheet-head", children: [
5283
- /* @__PURE__ */ jsx18("h3", { children: "Keyboard shortcuts" }),
5284
- /* @__PURE__ */ jsx18("button", { type: "button", className: "nm-sheet-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx18(Icon, { name: "close", size: 13 }) })
5422
+ /* @__PURE__ */ jsxs19("div", { className: "nm-sheet-head", children: [
5423
+ /* @__PURE__ */ jsx19("h3", { children: "Keyboard shortcuts" }),
5424
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "nm-sheet-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx19(Icon, { name: "close", size: 13 }) })
5285
5425
  ] }),
5286
- /* @__PURE__ */ jsxs18("div", { className: "nm-sheet-lead", children: [
5426
+ /* @__PURE__ */ jsxs19("div", { className: "nm-sheet-lead", children: [
5287
5427
  "Sorting, density, the dock, the copy formats and the filter syntax all live in the command palette rather than each having a button \u2014 the header keeps only what you read constantly. Payloads render as ",
5288
- /* @__PURE__ */ jsx18("b", { children: "Tree" }),
5428
+ /* @__PURE__ */ jsx19("b", { children: "Tree" }),
5289
5429
  ", ",
5290
- /* @__PURE__ */ jsx18("b", { children: "Table" }),
5430
+ /* @__PURE__ */ jsx19("b", { children: "Table" }),
5291
5431
  ",",
5292
5432
  " ",
5293
- /* @__PURE__ */ jsx18("b", { children: "JSON" }),
5433
+ /* @__PURE__ */ jsx19("b", { children: "JSON" }),
5294
5434
  ", ",
5295
- /* @__PURE__ */ jsx18("b", { children: "YAML" }),
5435
+ /* @__PURE__ */ jsx19("b", { children: "YAML" }),
5296
5436
  " or ",
5297
- /* @__PURE__ */ jsx18("b", { children: "Text" }),
5437
+ /* @__PURE__ */ jsx19("b", { children: "Text" }),
5298
5438
  ", and ",
5299
- /* @__PURE__ */ jsx18("b", { children: "Copy" }),
5439
+ /* @__PURE__ */ jsx19("b", { children: "Copy" }),
5300
5440
  " gives you whichever one you are looking at."
5301
5441
  ] }),
5302
- /* @__PURE__ */ jsx18("div", { className: "nm-sheet-cols", children: columns.map((groups, i) => /* @__PURE__ */ jsx18("div", { className: "nm-sheet-col", children: groups.map((group) => /* @__PURE__ */ jsxs18("div", { children: [
5303
- /* @__PURE__ */ jsx18("div", { className: "nm-sheet-group", children: group.title }),
5304
- group.items.map((item) => /* @__PURE__ */ jsxs18("div", { className: "nm-sheet-row", children: [
5305
- /* @__PURE__ */ jsx18("span", { className: "nm-sheet-key", children: item.keys }),
5306
- /* @__PURE__ */ jsx18("span", { className: "nm-sheet-desc", children: item.desc })
5442
+ /* @__PURE__ */ jsx19("div", { className: "nm-sheet-cols", children: columns.map((groups, i) => /* @__PURE__ */ jsx19("div", { className: "nm-sheet-col", children: groups.map((group) => /* @__PURE__ */ jsxs19("div", { children: [
5443
+ /* @__PURE__ */ jsx19("div", { className: "nm-sheet-group", children: group.title }),
5444
+ group.items.map((item) => /* @__PURE__ */ jsxs19("div", { className: "nm-sheet-row", children: [
5445
+ /* @__PURE__ */ jsx19("span", { className: "nm-sheet-key", children: item.keys }),
5446
+ /* @__PURE__ */ jsx19("span", { className: "nm-sheet-desc", children: item.desc })
5307
5447
  ] }, item.desc))
5308
5448
  ] }, group.title)) }, i)) })
5309
5449
  ]
@@ -5312,103 +5452,6 @@ function ShortcutsSheet({ onClose }) {
5312
5452
  ] });
5313
5453
  }
5314
5454
 
5315
- // src/ui/components/SourcesRail.tsx
5316
- import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
5317
- var SECTION_DEFS = [
5318
- { id: "network", label: "Network", hotkey: "1", title: "HTTP requests (1)", icon: "network" },
5319
- { id: "realtime", label: "Realtime", hotkey: "2", title: "WebSocket / ActionCable connections (2)", icon: "bolt" },
5320
- { id: "redux", label: "Redux", hotkey: "3", title: "Redux actions (3)", icon: "stack" },
5321
- { id: "query", label: "Query", hotkey: "4", title: "TanStack Query cache activity (4)", icon: "database" }
5322
- ];
5323
- function SourcesRail({
5324
- width,
5325
- mini,
5326
- section,
5327
- counts,
5328
- stats,
5329
- onSection,
5330
- onToggle,
5331
- onOpenPalette
5332
- }) {
5333
- const rows = [
5334
- ["Captured", String(stats.captured), ""],
5335
- ["Transferred", formatBytes(stats.transferred), ""],
5336
- ["Slowest", stats.slowestMs > 0 ? formatDuration(stats.slowestMs) : "\u2014", stats.slowestMs > 0 ? "nm-hot" : "nm-zero"],
5337
- ["Failing", String(stats.failing), stats.failing > 0 ? "nm-bad" : "nm-zero"],
5338
- ...stats.persistedLabel ? [["On disk", stats.persistedLabel, stats.persistedLabel === "0 B" ? "nm-zero" : ""]] : []
5339
- ];
5340
- return /* @__PURE__ */ jsxs19(
5341
- "nav",
5342
- {
5343
- className: `nm-rail${mini ? " nm-rail-mini" : ""}`,
5344
- style: { width },
5345
- "aria-label": "Sources",
5346
- children: [
5347
- /* @__PURE__ */ jsxs19("div", { className: "nm-rail-head", children: [
5348
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-label", children: "SOURCES" }),
5349
- /* @__PURE__ */ jsx19(
5350
- "button",
5351
- {
5352
- type: "button",
5353
- className: "nm-rail-collapse",
5354
- onClick: onToggle,
5355
- "aria-label": mini ? "Expand the sidebar" : "Collapse the sidebar",
5356
- "aria-expanded": !mini,
5357
- title: mini ? "Expand" : "Collapse",
5358
- children: /* @__PURE__ */ jsx19(Icon, { name: "chevron", size: 10 })
5359
- }
5360
- )
5361
- ] }),
5362
- /* @__PURE__ */ jsx19("div", { className: "nm-rail-list", role: "tablist", "aria-orientation": "vertical", children: SECTION_DEFS.map((s) => {
5363
- const empty = counts[s.id] === 0;
5364
- const active = section === s.id;
5365
- return /* @__PURE__ */ jsxs19(
5366
- "button",
5367
- {
5368
- type: "button",
5369
- role: "tab",
5370
- "aria-selected": active,
5371
- "data-src": s.id,
5372
- className: `nm-rail-item${active ? " active" : ""}${empty ? " nm-rail-empty" : ""}`,
5373
- onClick: () => onSection(s.id),
5374
- title: `${s.title} \u2014 ${counts[s.id]}`,
5375
- children: [
5376
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-ico", children: /* @__PURE__ */ jsx19(Icon, { name: s.icon, size: 12 }) }),
5377
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-name", children: s.label }),
5378
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-n", children: counts[s.id] }),
5379
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-key", children: s.hotkey })
5380
- ]
5381
- },
5382
- s.id
5383
- );
5384
- }) }),
5385
- /* @__PURE__ */ jsxs19("div", { className: "nm-rail-stats", children: [
5386
- /* @__PURE__ */ jsx19("div", { className: "nm-rail-stats-head", children: "SESSION" }),
5387
- rows.map(([k, v, tone]) => /* @__PURE__ */ jsxs19("div", { className: "nm-rail-stat", children: [
5388
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-stat-k", children: k }),
5389
- /* @__PURE__ */ jsx19("span", { className: `nm-rail-stat-v ${tone}`, children: v })
5390
- ] }, k))
5391
- ] }),
5392
- /* @__PURE__ */ jsx19("div", { className: "nm-rail-spacer" }),
5393
- /* @__PURE__ */ jsx19("div", { className: "nm-rail-cmdwrap", children: /* @__PURE__ */ jsx19(
5394
- "button",
5395
- {
5396
- type: "button",
5397
- className: "nm-rail-cmd",
5398
- onClick: onOpenPalette,
5399
- "aria-label": "All commands",
5400
- title: "All commands (\u2318K)",
5401
- children: mini ? /* @__PURE__ */ jsx19(Icon, { name: "search", size: 12 }) : /* @__PURE__ */ jsxs19(Fragment12, { children: [
5402
- /* @__PURE__ */ jsx19("span", { children: "All commands" }),
5403
- /* @__PURE__ */ jsx19("span", { className: "nm-rail-key", children: "\u2318K" })
5404
- ] })
5405
- }
5406
- ) })
5407
- ]
5408
- }
5409
- );
5410
- }
5411
-
5412
5455
  // src/ui/components/StatusBar.tsx
5413
5456
  import { Fragment as Fragment13, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
5414
5457
  function Seg({
@@ -5634,6 +5677,7 @@ function Menu({
5634
5677
  }
5635
5678
  function MenuItem({
5636
5679
  children,
5680
+ icon,
5637
5681
  state,
5638
5682
  on,
5639
5683
  value,
@@ -5653,6 +5697,7 @@ function MenuItem({
5653
5697
  onPointerEnter: onPreview,
5654
5698
  onFocus: onPreview,
5655
5699
  children: [
5700
+ icon && /* @__PURE__ */ jsx21("span", { className: "nm-menu-ico", "aria-hidden": true, children: /* @__PURE__ */ jsx21(Icon, { name: icon, size: 13 }) }),
5656
5701
  /* @__PURE__ */ jsx21("span", { className: "nm-menu-label", children }),
5657
5702
  state != null && /* @__PURE__ */ jsx21("span", { className: "nm-menu-state", "data-on": on || void 0, "data-value": value || void 0, children: state }),
5658
5703
  /* @__PURE__ */ jsx21("span", { className: "nm-menu-arrow", "aria-hidden": true, children: arrow ? "\u203A" : "" })
@@ -5983,11 +6028,13 @@ function ExportMenu({
5983
6028
  anchor,
5984
6029
  scope,
5985
6030
  onScope,
6031
+ selected,
5986
6032
  shown,
5987
6033
  all,
5988
6034
  onDone
5989
6035
  }) {
5990
- const entries = scope === "all" ? all : shown;
6036
+ const effective = scope === "selected" && !selected ? "shown" : scope;
6037
+ const entries = effective === "all" ? all : effective === "selected" && selected ? [selected] : shown;
5991
6038
  const stamp = fileStamp();
5992
6039
  const empty = entries.length === 0;
5993
6040
  const run = (fn) => () => {
@@ -6003,7 +6050,21 @@ function ExportMenu({
6003
6050
  "button",
6004
6051
  {
6005
6052
  type: "button",
6006
- className: `nm-scope-btn${scope === "shown" ? " active" : ""}`,
6053
+ className: `nm-scope-btn${effective === "selected" ? " active" : ""}`,
6054
+ onClick: () => onScope("selected"),
6055
+ disabled: !selected,
6056
+ title: selected ? "Only the entry open in the detail pane" : "Select an entry in the list to export it on its own",
6057
+ children: [
6058
+ "Selected ",
6059
+ /* @__PURE__ */ jsx22("b", { children: selected ? 1 : 0 })
6060
+ ]
6061
+ }
6062
+ ),
6063
+ /* @__PURE__ */ jsxs22(
6064
+ "button",
6065
+ {
6066
+ type: "button",
6067
+ className: `nm-scope-btn${effective === "shown" ? " active" : ""}`,
6007
6068
  onClick: () => onScope("shown"),
6008
6069
  title: "Only what the current section and filters show",
6009
6070
  children: [
@@ -6016,7 +6077,7 @@ function ExportMenu({
6016
6077
  "button",
6017
6078
  {
6018
6079
  type: "button",
6019
- className: `nm-scope-btn${scope === "all" ? " active" : ""}`,
6080
+ className: `nm-scope-btn${effective === "all" ? " active" : ""}`,
6020
6081
  onClick: () => onScope("all"),
6021
6082
  title: "Everything in the buffer, across all four sources",
6022
6083
  children: [
@@ -8435,6 +8496,13 @@ var FAB = `
8435
8496
  font-family: var(--bx-font-mono); font-variant-numeric: tabular-nums;
8436
8497
  }
8437
8498
  .nm-fab-count { color: var(--bx-fg-2); }
8499
+ .nm-fab-src { display: inline-flex; align-items: center; gap: 3px; color: var(--bx-fg-2); }
8500
+ .nm-fab-src svg { flex: none; }
8501
+ .nm-fab-src .nm-fab-err { margin-left: 2px; }
8502
+ .nm-fab-src[data-src="network"] svg { color: var(--bx-src-network); }
8503
+ .nm-fab-src[data-src="realtime"] svg { color: var(--bx-src-realtime); }
8504
+ .nm-fab-src[data-src="redux"] svg { color: var(--bx-src-redux); }
8505
+ .nm-fab-src[data-src="query"] svg { color: var(--bx-src-query); }
8438
8506
  .nm-fab-err { color: var(--bx-err); }
8439
8507
  .nm-fab-grip { display: flex; align-items: center; padding-right: var(--bx-3); color: var(--bx-mark); }
8440
8508
 
@@ -9430,6 +9498,9 @@ var MENU = `
9430
9498
  }
9431
9499
  .nm-menu-item:hover, .nm-menu-item.active { background: var(--bx-hover-float); }
9432
9500
  .nm-menu-item:disabled { cursor: not-allowed; }
9501
+ .nm-menu-ico { display: flex; flex: none; margin-right: var(--bx-3); color: var(--bx-fg-3); }
9502
+ .nm-menu-item:hover .nm-menu-ico, .nm-menu-item:focus-visible .nm-menu-ico { color: var(--bx-fg-2); }
9503
+ .nm-menu-item:disabled .nm-menu-ico { color: var(--bx-mark); }
9433
9504
  .nm-menu-label { flex: 1 1 auto; min-width: 0; font-size: var(--bx-fs-md); color: var(--bx-fg); }
9434
9505
  .nm-menu-item:disabled .nm-menu-label { color: var(--bx-fg-3); }
9435
9506
  /* A row that carries a state prints it where the icon used to be. A disabled
@@ -9460,6 +9531,7 @@ var MENU = `
9460
9531
  .nm-scope-btn:first-child { border-radius: var(--bx-r-ctl) 0 0 var(--bx-r-ctl); }
9461
9532
  .nm-scope-btn:last-child { border-radius: 0 var(--bx-r-ctl) var(--bx-r-ctl) 0; }
9462
9533
  .nm-scope-btn.active { background: var(--bx-hover-float); color: var(--bx-fg); }
9534
+ .nm-scope-btn:disabled { color: var(--bx-mark); cursor: not-allowed; }
9463
9535
  .nm-scope-btn b {
9464
9536
  font-family: var(--bx-font-mono); font-weight: 400; color: var(--bx-fg-3);
9465
9537
  font-variant-numeric: tabular-nums;
@@ -10631,15 +10703,28 @@ function DevTools() {
10631
10703
  const timer = window.setTimeout(() => setPurgeError(null), 8e3);
10632
10704
  return () => window.clearTimeout(timer);
10633
10705
  }, [purgeError]);
10634
- const { sectionEntries, sectionCounts } = useMemo9(() => {
10706
+ const { sectionEntries, sectionCounts, sectionErrors, allErrors, allPending } = useMemo9(() => {
10635
10707
  const counts = { network: 0, realtime: 0, redux: 0, query: 0 };
10708
+ const errorsBySection = { network: 0, realtime: 0, redux: 0, query: 0 };
10636
10709
  const filtered = [];
10710
+ let errors = 0;
10711
+ let pending = 0;
10637
10712
  for (const e of entries) {
10638
10713
  const s = sectionOf(e);
10639
10714
  counts[s] += 1;
10640
10715
  if (s === section) filtered.push(e);
10716
+ if (e.state === "error") {
10717
+ errors += 1;
10718
+ errorsBySection[s] += 1;
10719
+ } else if (e.state === "pending") pending += 1;
10641
10720
  }
10642
- return { sectionEntries: filtered, sectionCounts: counts };
10721
+ return {
10722
+ sectionEntries: filtered,
10723
+ sectionCounts: counts,
10724
+ sectionErrors: errorsBySection,
10725
+ allErrors: errors,
10726
+ allPending: pending
10727
+ };
10643
10728
  }, [entries, section]);
10644
10729
  const parsedFilter = useMemo9(() => parseFilter(query), [query]);
10645
10730
  const list = useMonitorList(
@@ -11185,12 +11270,14 @@ function DevTools() {
11185
11270
  {
11186
11271
  paused,
11187
11272
  total: entries.length,
11188
- errors: list.counts.error,
11189
- pending: list.counts.pending,
11273
+ counts: sectionCounts,
11274
+ sourceErrors: sectionErrors,
11275
+ errors: allErrors,
11276
+ pending: allPending,
11190
11277
  onPointerDown: fab.start
11191
11278
  }
11192
11279
  ),
11193
- fab.dragPos && /* @__PURE__ */ jsx24(FabDragPreview, { pos: fab.dragPos, paused, total: entries.length }),
11280
+ fab.dragPos && /* @__PURE__ */ jsx24(FabDragPreview, { pos: fab.dragPos, paused, counts: sectionCounts }),
11194
11281
  open && /* @__PURE__ */ jsxs24(
11195
11282
  "div",
11196
11283
  {
@@ -11437,6 +11524,7 @@ function DevTools() {
11437
11524
  anchor: exportAnchor,
11438
11525
  scope: exportScope,
11439
11526
  onScope: setExportScope,
11527
+ selected: selectedEntry != null ? selectedEntry : null,
11440
11528
  shown: list.filtered,
11441
11529
  all: entries,
11442
11530
  onDone: closeMenus
@@ -11457,6 +11545,7 @@ function DevTools() {
11457
11545
  /* @__PURE__ */ jsx24(
11458
11546
  MenuItem,
11459
11547
  {
11548
+ icon: "clear",
11460
11549
  state: "\u21E7C",
11461
11550
  onSelect: () => {
11462
11551
  networkMonitor.clear();
@@ -11469,6 +11558,7 @@ function DevTools() {
11469
11558
  /* @__PURE__ */ jsx24(
11470
11559
  MenuItem,
11471
11560
  {
11561
+ icon: "preserve",
11472
11562
  state: prefs2.preserveLog ? "ON" : "OFF",
11473
11563
  on: prefs2.preserveLog,
11474
11564
  onSelect: () => {
@@ -11481,6 +11571,7 @@ function DevTools() {
11481
11571
  /* @__PURE__ */ jsx24(
11482
11572
  MenuItem,
11483
11573
  {
11574
+ icon: "purge",
11484
11575
  disabled: persisted.count === 0,
11485
11576
  state: persisted.count ? formatBytes(persisted.bytes) : "nothing saved",
11486
11577
  value: persisted.count > 0,
@@ -11494,6 +11585,7 @@ function DevTools() {
11494
11585
  /* @__PURE__ */ jsx24(
11495
11586
  MenuItem,
11496
11587
  {
11588
+ icon: "database",
11497
11589
  arrow: true,
11498
11590
  state: isSharedDefaultDb() ? "shared" : void 0,
11499
11591
  on: isSharedDefaultDb(),
@@ -11508,6 +11600,7 @@ function DevTools() {
11508
11600
  /* @__PURE__ */ jsx24(
11509
11601
  MenuItem,
11510
11602
  {
11603
+ icon: "lock",
11511
11604
  state: authUnmasked ? "ON" : "OFF",
11512
11605
  on: authUnmasked,
11513
11606
  onSelect: () => {
@@ -11520,6 +11613,7 @@ function DevTools() {
11520
11613
  /* @__PURE__ */ jsx24(
11521
11614
  MenuItem,
11522
11615
  {
11616
+ icon: "follow",
11523
11617
  state: selection.isFollowing ? "ON" : "OFF",
11524
11618
  on: selection.isFollowing,
11525
11619
  onSelect: () => {
@@ -11532,6 +11626,7 @@ function DevTools() {
11532
11626
  /* @__PURE__ */ jsx24(
11533
11627
  MenuItem,
11534
11628
  {
11629
+ icon: "columns",
11535
11630
  arrow: true,
11536
11631
  value: true,
11537
11632
  state: `${TOGGLEABLE_COLUMNS.length - hiddenColumns.size} of ${TOGGLEABLE_COLUMNS.length}`,
@@ -11542,6 +11637,7 @@ function DevTools() {
11542
11637
  /* @__PURE__ */ jsx24(
11543
11638
  MenuItem,
11544
11639
  {
11640
+ icon: "corner",
11545
11641
  arrow: true,
11546
11642
  value: true,
11547
11643
  state: FAB_RADIUS[fabRadius].label.toUpperCase(),
@@ -11556,6 +11652,7 @@ function DevTools() {
11556
11652
  /* @__PURE__ */ jsx24(
11557
11653
  MenuItem,
11558
11654
  {
11655
+ icon: "density",
11559
11656
  arrow: true,
11560
11657
  value: true,
11561
11658
  state: density.toUpperCase(),
@@ -11569,6 +11666,7 @@ function DevTools() {
11569
11666
  /* @__PURE__ */ jsx24(
11570
11667
  MenuItem,
11571
11668
  {
11669
+ icon: "download",
11572
11670
  disabled: entries.length === 0,
11573
11671
  onSelect: () => moreAnchor && setExportAnchor(moreAnchor),
11574
11672
  children: "Export\u2026"
@@ -11577,6 +11675,7 @@ function DevTools() {
11577
11675
  panel.tinyToolbar && /* @__PURE__ */ jsx24(
11578
11676
  MenuItem,
11579
11677
  {
11678
+ icon: "dock-float",
11580
11679
  value: true,
11581
11680
  state: dock.mode.toUpperCase(),
11582
11681
  onSelect: () => {
@@ -11592,6 +11691,7 @@ function DevTools() {
11592
11691
  /* @__PURE__ */ jsx24(
11593
11692
  MenuItem,
11594
11693
  {
11694
+ icon: "command",
11595
11695
  state: "\u2318K",
11596
11696
  onSelect: () => {
11597
11697
  closeMenus();
@@ -11603,6 +11703,7 @@ function DevTools() {
11603
11703
  /* @__PURE__ */ jsx24(
11604
11704
  MenuItem,
11605
11705
  {
11706
+ icon: "keyboard",
11606
11707
  state: "?",
11607
11708
  onSelect: () => {
11608
11709
  setShowShortcuts(true);
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ function Blix({ store, apiClient, dbName }) {
37
37
  }, []);
38
38
  if (mounted && (process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_BLIX === "true")) {
39
39
  if (!panelRef.current) {
40
- panelRef.current = lazy(() => import("./DevToolsPanel-5XOY5WN5.js"));
40
+ panelRef.current = lazy(() => import("./DevToolsPanel-KK5DTPZA.js"));
41
41
  }
42
42
  const Panel = panelRef.current;
43
43
  if (dbName) configureDbName(dbName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hakam-aldeen-kh/blix",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Dev-tools panel for React apps — HTTP, Redux, Query and Realtime monitoring.",
5
5
  "keywords": [
6
6
  "devtools",