simvyn 2.5.4 → 2.6.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.
@@ -420,6 +420,10 @@
420
420
  top: 50%;
421
421
  }
422
422
 
423
+ .top-2 {
424
+ top: calc(var(--spacing) * 2);
425
+ }
426
+
423
427
  .top-full {
424
428
  top: 100%;
425
429
  }
@@ -428,6 +432,10 @@
428
432
  right: calc(var(--spacing) * 0);
429
433
  }
430
434
 
435
+ .right-2 {
436
+ right: calc(var(--spacing) * 2);
437
+ }
438
+
431
439
  .right-3 {
432
440
  right: calc(var(--spacing) * 3);
433
441
  }
@@ -853,6 +861,10 @@
853
861
  min-width: calc(var(--spacing) * 0);
854
862
  }
855
863
 
864
+ .min-w-\[60px\] {
865
+ min-width: 60px;
866
+ }
867
+
856
868
  .min-w-\[140px\] {
857
869
  min-width: 140px;
858
870
  }
@@ -2132,6 +2144,16 @@
2132
2144
  }
2133
2145
  }
2134
2146
 
2147
+ .hover\:bg-white\/\[0\.06\]:hover {
2148
+ background-color: #ffffff0f;
2149
+ }
2150
+
2151
+ @supports (color: color-mix(in lab, red, red)) {
2152
+ .hover\:bg-white\/\[0\.06\]:hover {
2153
+ background-color: color-mix(in oklab, var(--color-white) 6%, transparent);
2154
+ }
2155
+ }
2156
+
2135
2157
  .hover\:text-accent-blue:hover {
2136
2158
  color: var(--color-accent-blue);
2137
2159
  }
@@ -18544,27 +18544,27 @@ const createLucideIcon = (iconName, iconNode) => {
18544
18544
  Component.displayName = toPascalCase(iconName);
18545
18545
  return Component;
18546
18546
  };
18547
- const __iconNode$V = [
18547
+ const __iconNode$W = [
18548
18548
  ["path", { d: "M12 5v14", key: "s699le" }],
18549
18549
  ["path", { d: "m19 12-7 7-7-7", key: "1idqje" }]
18550
18550
  ];
18551
- const ArrowDown = createLucideIcon("arrow-down", __iconNode$V);
18552
- const __iconNode$U = [
18551
+ const ArrowDown = createLucideIcon("arrow-down", __iconNode$W);
18552
+ const __iconNode$V = [
18553
18553
  ["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
18554
18554
  ["path", { d: "M19 12H5", key: "x3x0zl" }]
18555
18555
  ];
18556
- const ArrowLeft = createLucideIcon("arrow-left", __iconNode$U);
18557
- const __iconNode$T = [
18556
+ const ArrowLeft = createLucideIcon("arrow-left", __iconNode$V);
18557
+ const __iconNode$U = [
18558
18558
  ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
18559
18559
  ["path", { d: "M12 19V5", key: "x0mq9r" }]
18560
18560
  ];
18561
- const ArrowUp = createLucideIcon("arrow-up", __iconNode$T);
18562
- const __iconNode$S = [
18561
+ const ArrowUp = createLucideIcon("arrow-up", __iconNode$U);
18562
+ const __iconNode$T = [
18563
18563
  ["path", { d: "M 22 14 L 22 10", key: "nqc4tb" }],
18564
18564
  ["rect", { x: "2", y: "6", width: "16", height: "12", rx: "2", key: "13zb55" }]
18565
18565
  ];
18566
- const Battery = createLucideIcon("battery", __iconNode$S);
18567
- const __iconNode$R = [
18566
+ const Battery = createLucideIcon("battery", __iconNode$T);
18567
+ const __iconNode$S = [
18568
18568
  ["path", { d: "M12 20v-9", key: "1qisl0" }],
18569
18569
  ["path", { d: "M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z", key: "uouzyp" }],
18570
18570
  ["path", { d: "M14.12 3.88 16 2", key: "qol33r" }],
@@ -18577,8 +18577,8 @@ const __iconNode$R = [
18577
18577
  ["path", { d: "m8 2 1.88 1.88", key: "fmnt4t" }],
18578
18578
  ["path", { d: "M9 7.13V6a3 3 0 1 1 6 0v1.13", key: "1vgav8" }]
18579
18579
  ];
18580
- const Bug = createLucideIcon("bug", __iconNode$R);
18581
- const __iconNode$Q = [
18580
+ const Bug = createLucideIcon("bug", __iconNode$S);
18581
+ const __iconNode$R = [
18582
18582
  [
18583
18583
  "path",
18584
18584
  {
@@ -18588,46 +18588,52 @@ const __iconNode$Q = [
18588
18588
  ],
18589
18589
  ["circle", { cx: "12", cy: "13", r: "3", key: "1vg3eu" }]
18590
18590
  ];
18591
- const Camera = createLucideIcon("camera", __iconNode$Q);
18592
- const __iconNode$P = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
18593
- const Check = createLucideIcon("check", __iconNode$P);
18594
- const __iconNode$O = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
18595
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$O);
18596
- const __iconNode$N = [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]];
18597
- const ChevronLeft = createLucideIcon("chevron-left", __iconNode$N);
18598
- const __iconNode$M = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
18599
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$M);
18600
- const __iconNode$L = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
18601
- const ChevronUp = createLucideIcon("chevron-up", __iconNode$L);
18602
- const __iconNode$K = [
18591
+ const Camera = createLucideIcon("camera", __iconNode$R);
18592
+ const __iconNode$Q = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
18593
+ const Check = createLucideIcon("check", __iconNode$Q);
18594
+ const __iconNode$P = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
18595
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$P);
18596
+ const __iconNode$O = [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]];
18597
+ const ChevronLeft = createLucideIcon("chevron-left", __iconNode$O);
18598
+ const __iconNode$N = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
18599
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$N);
18600
+ const __iconNode$M = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
18601
+ const ChevronUp = createLucideIcon("chevron-up", __iconNode$M);
18602
+ const __iconNode$L = [
18603
18603
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
18604
18604
  ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
18605
18605
  ];
18606
- const Clock = createLucideIcon("clock", __iconNode$K);
18607
- const __iconNode$J = [
18606
+ const Clock = createLucideIcon("clock", __iconNode$L);
18607
+ const __iconNode$K = [
18608
18608
  ["line", { x1: "15", x2: "15", y1: "12", y2: "18", key: "1p7wdc" }],
18609
18609
  ["line", { x1: "12", x2: "18", y1: "15", y2: "15", key: "1nscbv" }],
18610
18610
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
18611
18611
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
18612
18612
  ];
18613
- const CopyPlus = createLucideIcon("copy-plus", __iconNode$J);
18614
- const __iconNode$I = [
18613
+ const CopyPlus = createLucideIcon("copy-plus", __iconNode$K);
18614
+ const __iconNode$J = [
18615
18615
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
18616
18616
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
18617
18617
  ];
18618
- const Copy = createLucideIcon("copy", __iconNode$I);
18619
- const __iconNode$H = [
18618
+ const Copy = createLucideIcon("copy", __iconNode$J);
18619
+ const __iconNode$I = [
18620
18620
  ["ellipse", { cx: "12", cy: "5", rx: "9", ry: "3", key: "msslwz" }],
18621
18621
  ["path", { d: "M3 5V19A9 3 0 0 0 21 19V5", key: "1wlel7" }],
18622
18622
  ["path", { d: "M3 12A9 3 0 0 0 21 12", key: "mv7ke4" }]
18623
18623
  ];
18624
- const Database = createLucideIcon("database", __iconNode$H);
18625
- const __iconNode$G = [
18624
+ const Database = createLucideIcon("database", __iconNode$I);
18625
+ const __iconNode$H = [
18626
18626
  ["path", { d: "M12 15V3", key: "m9g1x1" }],
18627
18627
  ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
18628
18628
  ["path", { d: "m7 10 5 5 5-5", key: "brsn70" }]
18629
18629
  ];
18630
- const Download = createLucideIcon("download", __iconNode$G);
18630
+ const Download = createLucideIcon("download", __iconNode$H);
18631
+ const __iconNode$G = [
18632
+ ["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }],
18633
+ ["circle", { cx: "19", cy: "12", r: "1", key: "1wjl8i" }],
18634
+ ["circle", { cx: "5", cy: "12", r: "1", key: "1pcz8c" }]
18635
+ ];
18636
+ const Ellipsis = createLucideIcon("ellipsis", __iconNode$G);
18631
18637
  const __iconNode$F = [
18632
18638
  [
18633
18639
  "path",
@@ -47524,8 +47530,7 @@ const useLogStore = create((set) => ({
47524
47530
  entries: [],
47525
47531
  firstItemIndex: INITIAL_INDEX,
47526
47532
  cursor: null,
47527
- hasMore: true,
47528
- isPaused: false
47533
+ hasMore: true
47529
47534
  }),
47530
47535
  reset: () => set({ ...initialState }),
47531
47536
  setStreaming: (deviceId) => set({ streamDeviceId: deviceId, isStreaming: deviceId !== null }),
@@ -47563,6 +47568,42 @@ function filterEntries(entries2, enabledLevels, processFilter, searchPattern) {
47563
47568
  }
47564
47569
  return filtered;
47565
47570
  }
47571
+ const useSearchStore = create((set) => ({
47572
+ isOpen: false,
47573
+ query: "",
47574
+ isRegex: false,
47575
+ matchIndices: [],
47576
+ currentMatchIdx: -1,
47577
+ totalMatches: 0,
47578
+ open: () => set({ isOpen: true }),
47579
+ close: () => set({
47580
+ isOpen: false,
47581
+ query: "",
47582
+ matchIndices: [],
47583
+ currentMatchIdx: -1,
47584
+ totalMatches: 0
47585
+ }),
47586
+ setQuery: (q) => set({
47587
+ query: q,
47588
+ currentMatchIdx: q ? 0 : -1
47589
+ }),
47590
+ toggleRegex: () => set((s) => ({ isRegex: !s.isRegex })),
47591
+ setMatches: (indices) => set((s) => ({
47592
+ matchIndices: indices,
47593
+ totalMatches: indices.length,
47594
+ currentMatchIdx: indices.length === 0 ? -1 : s.currentMatchIdx >= indices.length ? 0 : s.currentMatchIdx < 0 ? 0 : s.currentMatchIdx
47595
+ })),
47596
+ nextMatch: () => set((s) => {
47597
+ if (s.totalMatches === 0) return s;
47598
+ return { currentMatchIdx: (s.currentMatchIdx + 1) % s.totalMatches };
47599
+ }),
47600
+ prevMatch: () => set((s) => {
47601
+ if (s.totalMatches === 0) return s;
47602
+ return {
47603
+ currentMatchIdx: (s.currentMatchIdx - 1 + s.totalMatches) % s.totalMatches
47604
+ };
47605
+ })
47606
+ }));
47566
47607
  const levelColors = {
47567
47608
  verbose: "text-gray-500",
47568
47609
  debug: "text-cyan-400",
@@ -47583,6 +47624,64 @@ function formatTime(ts) {
47583
47624
  return ts;
47584
47625
  }
47585
47626
  }
47627
+ function escapeRegex(s) {
47628
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
47629
+ }
47630
+ function highlightText(text, query, isRegex, isActiveRow) {
47631
+ if (!query) return text;
47632
+ let re2;
47633
+ try {
47634
+ re2 = isRegex ? new RegExp(query, "gi") : new RegExp(escapeRegex(query), "gi");
47635
+ } catch {
47636
+ return text;
47637
+ }
47638
+ const parts = [];
47639
+ let lastIndex = 0;
47640
+ let match;
47641
+ let i = 0;
47642
+ while ((match = re2.exec(text)) !== null) {
47643
+ if (match.index > lastIndex) {
47644
+ parts.push(text.slice(lastIndex, match.index));
47645
+ }
47646
+ parts.push(
47647
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47648
+ "mark",
47649
+ {
47650
+ style: isActiveRow ? {
47651
+ background: "rgba(245,158,11,0.4)",
47652
+ color: "rgb(253,230,138)",
47653
+ borderRadius: 2,
47654
+ padding: "0 2px",
47655
+ boxShadow: "0 0 0 1px rgba(245,158,11,0.5)"
47656
+ } : {
47657
+ background: "rgba(245,158,11,0.2)",
47658
+ color: "rgb(253,211,155)",
47659
+ borderRadius: 2,
47660
+ padding: "0 2px"
47661
+ },
47662
+ children: match[0]
47663
+ },
47664
+ i++
47665
+ )
47666
+ );
47667
+ lastIndex = re2.lastIndex;
47668
+ if (match[0].length === 0) re2.lastIndex++;
47669
+ }
47670
+ if (lastIndex < text.length) {
47671
+ parts.push(text.slice(lastIndex));
47672
+ }
47673
+ return parts.length > 0 ? parts : text;
47674
+ }
47675
+ function testMatch(entry, query, isRegex) {
47676
+ if (!query) return false;
47677
+ try {
47678
+ const re2 = isRegex ? new RegExp(query, "i") : new RegExp(escapeRegex(query), "i");
47679
+ return re2.test(entry.message) || re2.test(entry.processName);
47680
+ } catch {
47681
+ const lq = query.toLowerCase();
47682
+ return entry.message.toLowerCase().includes(lq) || entry.processName.toLowerCase().includes(lq);
47683
+ }
47684
+ }
47586
47685
  function LogList({ onLoadMore }) {
47587
47686
  const virtuosoRef = reactExports.useRef(null);
47588
47687
  const [locked, setLocked] = reactExports.useState(true);
@@ -47598,6 +47697,33 @@ function LogList({ onLoadMore }) {
47598
47697
  const firstItemIndex = useLogStore((s) => s.firstItemIndex);
47599
47698
  const hasMore = useLogStore((s) => s.hasMore);
47600
47699
  const isLoadingHistory = useLogStore((s) => s.isLoadingHistory);
47700
+ const searchQuery = useSearchStore((s) => s.query);
47701
+ const searchIsRegex = useSearchStore((s) => s.isRegex);
47702
+ const currentMatchIdx = useSearchStore((s) => s.currentMatchIdx);
47703
+ const matchIndices = useSearchStore((s) => s.matchIndices);
47704
+ reactExports.useEffect(() => {
47705
+ if (!searchQuery) {
47706
+ useSearchStore.getState().setMatches([]);
47707
+ return;
47708
+ }
47709
+ const indices = [];
47710
+ for (let i = 0; i < entries2.length; i++) {
47711
+ if (testMatch(entries2[i], searchQuery, searchIsRegex)) {
47712
+ indices.push(i);
47713
+ }
47714
+ }
47715
+ useSearchStore.getState().setMatches(indices);
47716
+ }, [searchQuery, searchIsRegex, entries2]);
47717
+ reactExports.useEffect(() => {
47718
+ if (currentMatchIdx >= 0 && currentMatchIdx < matchIndices.length) {
47719
+ const entryIndex = matchIndices[currentMatchIdx];
47720
+ virtuosoRef.current?.scrollToIndex({
47721
+ index: entryIndex,
47722
+ align: "center",
47723
+ behavior: "auto"
47724
+ });
47725
+ }
47726
+ }, [currentMatchIdx, matchIndices]);
47601
47727
  const jumpToBottom = reactExports.useCallback(() => {
47602
47728
  setLocked(true);
47603
47729
  virtuosoRef.current?.scrollToIndex({
@@ -47621,6 +47747,7 @@ function LogList({ onLoadMore }) {
47621
47747
  ) }) : null,
47622
47748
  [hasMore, isLoadingHistory, onLoadMore]
47623
47749
  );
47750
+ const activeMatchIndex = currentMatchIdx >= 0 ? matchIndices[currentMatchIdx] : -1;
47624
47751
  if (entries2.length === 0) {
47625
47752
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-empty-state h-full flex items-center justify-center", children: "No log entries" });
47626
47753
  }
@@ -47639,12 +47766,23 @@ function LogList({ onLoadMore }) {
47639
47766
  increaseViewportBy: 200,
47640
47767
  computeItemKey: (index, entry) => `${entry.timestamp}-${entry.pid}-${index}`,
47641
47768
  className: "glass-panel h-full font-mono text-xs leading-relaxed",
47642
- itemContent: (_index, entry) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 px-3 py-0.5 hover:bg-white/[0.02]", children: [
47643
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-muted shrink-0 w-[90px]", children: formatTime(entry.timestamp) }),
47644
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `shrink-0 w-[56px] uppercase ${levelColors[entry.level]}`, children: entry.level.slice(0, 5).padEnd(5) }),
47645
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-secondary shrink-0 w-[120px] truncate", children: entry.processName }),
47646
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-primary break-all", children: entry.message })
47647
- ] })
47769
+ itemContent: (index, entry) => {
47770
+ const isActiveRow = index === activeMatchIndex;
47771
+ const hasQuery = !!searchQuery;
47772
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
47773
+ "div",
47774
+ {
47775
+ className: "flex gap-2 px-3 py-0.5 hover:bg-white/[0.02]",
47776
+ style: isActiveRow ? { background: "rgba(245,158,11,0.06)" } : void 0,
47777
+ children: [
47778
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-muted shrink-0 w-[90px]", children: formatTime(entry.timestamp) }),
47779
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `shrink-0 w-[56px] uppercase ${levelColors[entry.level]}`, children: entry.level.slice(0, 5).padEnd(5) }),
47780
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-secondary shrink-0 w-[120px] truncate", children: hasQuery ? highlightText(entry.processName, searchQuery, searchIsRegex, isActiveRow) : entry.processName }),
47781
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-primary break-all", children: hasQuery ? highlightText(entry.message, searchQuery, searchIsRegex, isActiveRow) : entry.message })
47782
+ ]
47783
+ }
47784
+ );
47785
+ }
47648
47786
  }
47649
47787
  ),
47650
47788
  !locked && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -47734,6 +47872,18 @@ function LogToolbar({ selectedDeviceId }) {
47734
47872
  [entries2, enabledLevels, processFilter, searchPattern]
47735
47873
  );
47736
47874
  const searchTimer = reactExports.useRef(null);
47875
+ const [menuOpen, setMenuOpen] = reactExports.useState(false);
47876
+ const menuRef = reactExports.useRef(null);
47877
+ reactExports.useEffect(() => {
47878
+ if (!menuOpen) return;
47879
+ function handleClickOutside(e) {
47880
+ if (menuRef.current && !menuRef.current.contains(e.target)) {
47881
+ setMenuOpen(false);
47882
+ }
47883
+ }
47884
+ document.addEventListener("mousedown", handleClickOutside);
47885
+ return () => document.removeEventListener("mousedown", handleClickOutside);
47886
+ }, [menuOpen]);
47737
47887
  const handleSearch = reactExports.useCallback(
47738
47888
  (value) => {
47739
47889
  if (searchTimer.current) clearTimeout(searchTimer.current);
@@ -47848,9 +47998,158 @@ function LogToolbar({ selectedDeviceId }) {
47848
47998
  children: "Clear"
47849
47999
  }
47850
48000
  ),
47851
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs ml-auto ${isPaused ? "text-yellow-400" : "text-text-muted"}`, children: isPaused ? "Paused" : filteredCount === totalCount ? `${totalCount} entries` : `${filteredCount} / ${totalCount} entries` })
48001
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs ml-auto ${isPaused ? "text-yellow-400" : "text-text-muted"}`, children: isPaused ? "Paused" : filteredCount === totalCount ? `${totalCount} entries` : `${filteredCount} / ${totalCount} entries` }),
48002
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", ref: menuRef, children: [
48003
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48004
+ "button",
48005
+ {
48006
+ type: "button",
48007
+ onClick: () => setMenuOpen((v) => !v),
48008
+ className: "glass-button px-1.5",
48009
+ title: "More actions",
48010
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Ellipsis, { size: 14 })
48011
+ }
48012
+ ),
48013
+ menuOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
48014
+ "div",
48015
+ {
48016
+ className: "glass-panel absolute right-0 top-full mt-1 py-1 z-50 min-w-[140px]",
48017
+ style: { borderRadius: 8 },
48018
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
48019
+ "button",
48020
+ {
48021
+ type: "button",
48022
+ className: "w-full flex items-center justify-between px-3 py-1.5 text-xs text-text-primary hover:bg-white/[0.06] transition-colors",
48023
+ onClick: () => {
48024
+ useSearchStore.getState().open();
48025
+ setMenuOpen(false);
48026
+ },
48027
+ children: [
48028
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Search" }),
48029
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-text-muted text-[10px]", children: navigator.platform.includes("Mac") ? "⌘F" : "Ctrl+F" })
48030
+ ]
48031
+ }
48032
+ )
48033
+ }
48034
+ )
48035
+ ] })
47852
48036
  ] });
47853
48037
  }
48038
+ function SearchOverlay() {
48039
+ const isOpen = useSearchStore((s) => s.isOpen);
48040
+ const query = useSearchStore((s) => s.query);
48041
+ const isRegex = useSearchStore((s) => s.isRegex);
48042
+ const currentMatchIdx = useSearchStore((s) => s.currentMatchIdx);
48043
+ const totalMatches = useSearchStore((s) => s.totalMatches);
48044
+ const setQuery = useSearchStore((s) => s.setQuery);
48045
+ const toggleRegex = useSearchStore((s) => s.toggleRegex);
48046
+ const nextMatch = useSearchStore((s) => s.nextMatch);
48047
+ const prevMatch = useSearchStore((s) => s.prevMatch);
48048
+ const close = useSearchStore((s) => s.close);
48049
+ const inputRef = reactExports.useRef(null);
48050
+ const debounceRef = reactExports.useRef(null);
48051
+ reactExports.useEffect(() => {
48052
+ if (isOpen) {
48053
+ requestAnimationFrame(() => inputRef.current?.focus());
48054
+ }
48055
+ }, [isOpen]);
48056
+ const handleInputChange = reactExports.useCallback(
48057
+ (value) => {
48058
+ if (debounceRef.current) clearTimeout(debounceRef.current);
48059
+ debounceRef.current = setTimeout(() => setQuery(value), 100);
48060
+ },
48061
+ [setQuery]
48062
+ );
48063
+ const handleKeyDown = reactExports.useCallback(
48064
+ (e) => {
48065
+ if (e.key === "Enter" && e.shiftKey) {
48066
+ e.preventDefault();
48067
+ prevMatch();
48068
+ } else if (e.key === "Enter") {
48069
+ e.preventDefault();
48070
+ nextMatch();
48071
+ } else if (e.key === "Escape") {
48072
+ e.preventDefault();
48073
+ close();
48074
+ }
48075
+ },
48076
+ [nextMatch, prevMatch, close]
48077
+ );
48078
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs(
48079
+ motion.div,
48080
+ {
48081
+ initial: { y: -10, opacity: 0 },
48082
+ animate: { y: 0, opacity: 1 },
48083
+ exit: { y: -10, opacity: 0 },
48084
+ transition: { duration: 0.15, ease: "easeOut" },
48085
+ className: "absolute top-2 right-2 z-50 flex items-center gap-1.5 glass-panel px-2.5 py-1.5",
48086
+ style: { borderRadius: 10 },
48087
+ children: [
48088
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48089
+ "input",
48090
+ {
48091
+ ref: inputRef,
48092
+ type: "text",
48093
+ placeholder: "Search logs...",
48094
+ defaultValue: query,
48095
+ onChange: (e) => handleInputChange(e.target.value),
48096
+ onKeyDown: handleKeyDown,
48097
+ className: "glass-input text-xs py-1 px-2 w-[180px]",
48098
+ style: { background: "rgba(0,0,0,0.15)", borderRadius: 6 }
48099
+ }
48100
+ ),
48101
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48102
+ "button",
48103
+ {
48104
+ type: "button",
48105
+ onClick: toggleRegex,
48106
+ className: "glass-button text-xs px-1.5 py-0.5",
48107
+ style: isRegex ? {
48108
+ borderColor: "rgba(0,180,255,0.5)",
48109
+ background: "rgba(0,180,255,0.15)",
48110
+ color: "rgba(100,200,255,1)"
48111
+ } : { fontSize: "0.7rem" },
48112
+ title: "Toggle regex",
48113
+ children: ".*"
48114
+ }
48115
+ ),
48116
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48117
+ "button",
48118
+ {
48119
+ type: "button",
48120
+ onClick: prevMatch,
48121
+ disabled: totalMatches === 0,
48122
+ className: "glass-button px-1 py-0.5",
48123
+ title: "Previous match (Shift+Enter)",
48124
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 })
48125
+ }
48126
+ ),
48127
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48128
+ "button",
48129
+ {
48130
+ type: "button",
48131
+ onClick: nextMatch,
48132
+ disabled: totalMatches === 0,
48133
+ className: "glass-button px-1 py-0.5",
48134
+ title: "Next match (Enter)",
48135
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 })
48136
+ }
48137
+ ),
48138
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-text-muted whitespace-nowrap min-w-[60px] text-center", children: query ? totalMatches > 0 ? `${currentMatchIdx + 1} of ${totalMatches}` : "No results" : "" }),
48139
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
48140
+ "button",
48141
+ {
48142
+ type: "button",
48143
+ onClick: close,
48144
+ className: "glass-button px-1 py-0.5",
48145
+ title: "Close (Escape)",
48146
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { size: 14 })
48147
+ }
48148
+ )
48149
+ ]
48150
+ }
48151
+ ) });
48152
+ }
47854
48153
  function LogPanel() {
47855
48154
  const { send } = useWs();
47856
48155
  const selectedDeviceId = useDeviceStore((s) => s.selectedDeviceIds[0] ?? null);
@@ -47929,6 +48228,16 @@ function LogPanel() {
47929
48228
  }
47930
48229
  wasPausedRef.current = isPaused;
47931
48230
  }, [isPaused, send, clear]);
48231
+ reactExports.useEffect(() => {
48232
+ function handleKeyDown(e) {
48233
+ if ((e.metaKey || e.ctrlKey) && e.key === "f") {
48234
+ e.preventDefault();
48235
+ useSearchStore.getState().open();
48236
+ }
48237
+ }
48238
+ window.addEventListener("keydown", handleKeyDown);
48239
+ return () => window.removeEventListener("keydown", handleKeyDown);
48240
+ }, []);
47932
48241
  const loadMoreHistory = reactExports.useCallback(() => {
47933
48242
  if (!selectedDeviceId || !hasMore || isLoadingHistory) return;
47934
48243
  setLoadingHistory(true);
@@ -48000,7 +48309,10 @@ function LogPanel() {
48000
48309
  !selectedDeviceId && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-empty-state flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Select a booted device to stream logs" }) }),
48001
48310
  selectedDeviceId && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
48002
48311
  /* @__PURE__ */ jsxRuntimeExports.jsx(LogToolbar, { selectedDeviceId }),
48003
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogList, { onLoadMore: loadMoreHistory }) })
48312
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-h-0 relative", children: [
48313
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SearchOverlay, {}),
48314
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LogList, { onLoadMore: loadMoreHistory })
48315
+ ] })
48004
48316
  ] })
48005
48317
  ] });
48006
48318
  }
@@ -52043,7 +52355,7 @@ function ToolSettingsPanel() {
52043
52355
  const [copied, setCopied] = reactExports.useState(false);
52044
52356
  const devices = useDeviceStore((s) => s.devices);
52045
52357
  const modules = useModuleStore((s) => s.modules);
52046
- const version = "2.5.4";
52358
+ const version = "2.6.0";
52047
52359
  const fetchStorage = reactExports.useCallback(() => {
52048
52360
  fetch("/api/tool-settings/storage").then((r) => r.json()).then((data) => setStorage(data)).catch(() => {
52049
52361
  });
@@ -53392,4 +53704,4 @@ function App() {
53392
53704
  clientExports.createRoot(document.getElementById("root")).render(
53393
53705
  /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) })
53394
53706
  );
53395
- //# sourceMappingURL=index-rWeZ9VzX.js.map
53707
+ //# sourceMappingURL=index-y3nYH57L.js.map