@silvery/examples 0.17.3 → 0.17.4

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.
Files changed (112) hide show
  1. package/dist/UPNG-Cy7ViL8f.mjs +5074 -0
  2. package/dist/__vite-browser-external-2447137e-BML7CYau.mjs +4 -0
  3. package/dist/_banner-DLPxCqVy.mjs +44 -0
  4. package/dist/ansi-CCE2pVS0.mjs +16397 -0
  5. package/dist/apng-HhhBjRGt.mjs +68 -0
  6. package/dist/apng-mwUQbTTF.mjs +3 -0
  7. package/dist/apps/aichat/index.mjs +1299 -0
  8. package/dist/apps/app-todo.mjs +139 -0
  9. package/dist/apps/async-data.mjs +204 -0
  10. package/dist/apps/cli-wizard.mjs +339 -0
  11. package/dist/apps/clipboard.mjs +198 -0
  12. package/dist/apps/components.mjs +864 -0
  13. package/dist/apps/data-explorer.mjs +483 -0
  14. package/dist/apps/dev-tools.mjs +397 -0
  15. package/dist/apps/explorer.mjs +698 -0
  16. package/dist/apps/gallery.mjs +766 -0
  17. package/dist/apps/inline-bench.mjs +115 -0
  18. package/dist/apps/kanban.mjs +280 -0
  19. package/dist/apps/layout-ref.mjs +187 -0
  20. package/dist/apps/outline.mjs +203 -0
  21. package/dist/apps/paste-demo.mjs +189 -0
  22. package/dist/apps/scroll.mjs +86 -0
  23. package/dist/apps/search-filter.mjs +287 -0
  24. package/dist/apps/selection.mjs +355 -0
  25. package/dist/apps/spatial-focus-demo.mjs +388 -0
  26. package/dist/apps/task-list.mjs +258 -0
  27. package/dist/apps/terminal-caps-demo.mjs +315 -0
  28. package/dist/apps/terminal.mjs +872 -0
  29. package/dist/apps/text-selection-demo.mjs +254 -0
  30. package/dist/apps/textarea.mjs +178 -0
  31. package/dist/apps/theme.mjs +661 -0
  32. package/dist/apps/transform.mjs +215 -0
  33. package/dist/apps/virtual-10k.mjs +422 -0
  34. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  35. package/dist/backends-Bahh9mKN.mjs +1179 -0
  36. package/dist/backends-CCtCDQ94.mjs +3 -0
  37. package/dist/{cli.mjs → bin/cli.mjs} +15 -19
  38. package/dist/chunk-BSw8zbkd.mjs +37 -0
  39. package/dist/components/counter.mjs +48 -0
  40. package/dist/components/hello.mjs +31 -0
  41. package/dist/components/progress-bar.mjs +59 -0
  42. package/dist/components/select-list.mjs +85 -0
  43. package/dist/components/spinner.mjs +57 -0
  44. package/dist/components/text-input.mjs +62 -0
  45. package/dist/components/virtual-list.mjs +51 -0
  46. package/dist/flexily-zero-adapter-UB-ra8fR.mjs +3374 -0
  47. package/dist/gif-BZaqPPVX.mjs +3 -0
  48. package/dist/gif-BtnXuxLF.mjs +71 -0
  49. package/dist/gifenc-CLRW41dk.mjs +728 -0
  50. package/dist/jsx-runtime-dMs_8fNu.mjs +241 -0
  51. package/dist/key-mapping-5oYQdAQE.mjs +3 -0
  52. package/dist/key-mapping-D4LR1go6.mjs +130 -0
  53. package/dist/layout/dashboard.mjs +1204 -0
  54. package/dist/layout/live-resize.mjs +303 -0
  55. package/dist/layout/overflow.mjs +70 -0
  56. package/dist/layout/text-layout.mjs +335 -0
  57. package/dist/node-NuJ94BWl.mjs +1083 -0
  58. package/dist/plugins-D1KtkT4a.mjs +3057 -0
  59. package/dist/resvg-js-C_8Wps1F.mjs +201 -0
  60. package/dist/src-BTEVGpd9.mjs +23538 -0
  61. package/dist/src-CUUOuRH6.mjs +5322 -0
  62. package/dist/src-CzfRafCQ.mjs +814 -0
  63. package/dist/usingCtx-CsEf0xO3.mjs +57 -0
  64. package/dist/yoga-adapter-BVtQ5OJR.mjs +237 -0
  65. package/package.json +18 -13
  66. package/_banner.tsx +0 -60
  67. package/apps/aichat/components.tsx +0 -469
  68. package/apps/aichat/index.tsx +0 -220
  69. package/apps/aichat/script.ts +0 -460
  70. package/apps/aichat/state.ts +0 -325
  71. package/apps/aichat/types.ts +0 -19
  72. package/apps/app-todo.tsx +0 -201
  73. package/apps/async-data.tsx +0 -196
  74. package/apps/cli-wizard.tsx +0 -332
  75. package/apps/clipboard.tsx +0 -183
  76. package/apps/components.tsx +0 -658
  77. package/apps/data-explorer.tsx +0 -490
  78. package/apps/dev-tools.tsx +0 -395
  79. package/apps/explorer.tsx +0 -731
  80. package/apps/gallery.tsx +0 -653
  81. package/apps/inline-bench.tsx +0 -138
  82. package/apps/kanban.tsx +0 -265
  83. package/apps/layout-ref.tsx +0 -173
  84. package/apps/outline.tsx +0 -160
  85. package/apps/panes/index.tsx +0 -203
  86. package/apps/paste-demo.tsx +0 -185
  87. package/apps/scroll.tsx +0 -80
  88. package/apps/search-filter.tsx +0 -240
  89. package/apps/selection.tsx +0 -346
  90. package/apps/spatial-focus-demo.tsx +0 -372
  91. package/apps/task-list.tsx +0 -271
  92. package/apps/terminal-caps-demo.tsx +0 -317
  93. package/apps/terminal.tsx +0 -784
  94. package/apps/text-selection-demo.tsx +0 -193
  95. package/apps/textarea.tsx +0 -155
  96. package/apps/theme.tsx +0 -515
  97. package/apps/transform.tsx +0 -229
  98. package/apps/virtual-10k.tsx +0 -405
  99. package/apps/vterm-demo/index.tsx +0 -216
  100. package/components/counter.tsx +0 -49
  101. package/components/hello.tsx +0 -38
  102. package/components/progress-bar.tsx +0 -52
  103. package/components/select-list.tsx +0 -54
  104. package/components/spinner.tsx +0 -44
  105. package/components/text-input.tsx +0 -61
  106. package/components/virtual-list.tsx +0 -56
  107. package/dist/cli.d.mts +0 -1
  108. package/dist/cli.mjs.map +0 -1
  109. package/layout/dashboard.tsx +0 -953
  110. package/layout/live-resize.tsx +0 -282
  111. package/layout/overflow.tsx +0 -51
  112. package/layout/text-layout.tsx +0 -283
@@ -0,0 +1,698 @@
1
+ import { t as _usingCtx } from "../usingCtx-CsEf0xO3.mjs";
2
+ import { t as require_jsx_runtime } from "../jsx-runtime-dMs_8fNu.mjs";
3
+ import { t as ExampleBanner } from "../_banner-DLPxCqVy.mjs";
4
+ import { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from "react";
5
+ import { Box, Divider, Kbd, ListView, Muted, Tab, TabList, Tabs, Text, TextInput, createTerm, render, useApp, useBoxRect, useInput } from "silvery";
6
+ //#region apps/explorer.tsx
7
+ /**
8
+ * Explorer — Log Viewer & Process Explorer
9
+ *
10
+ * A tabbed data exploration demo combining:
11
+ * - Streaming log viewer with ~2000 lines, severity-level coloring, and level toggles
12
+ * - Sortable process table with ~50 processes, live CPU/MEM jitter, and responsive columns
13
+ * - Shared TextInput search bar with useDeferredValue for non-blocking filtering
14
+ * - ListView with interactive scrolling for both tabs
15
+ *
16
+ * Usage: bun vendor/silvery/examples/apps/explorer.tsx
17
+ *
18
+ * Controls:
19
+ * Tab/h/l - Switch tabs (Logs / Processes)
20
+ * j/k or Up/Dn - Navigate rows
21
+ * d/u - Half-page down/up
22
+ * g/G - Jump to first/last
23
+ * / - Focus search bar
24
+ * 1-4 - Toggle log levels (Logs tab)
25
+ * s - Cycle sort column (Processes tab)
26
+ * Esc - Exit search / quit
27
+ * q - Quit (when not searching)
28
+ */
29
+ var import_jsx_runtime = require_jsx_runtime();
30
+ const meta = {
31
+ name: "Explorer",
32
+ description: "Log viewer and process explorer with ListView search",
33
+ demo: true,
34
+ features: [
35
+ "ListView",
36
+ "TextInput",
37
+ "useBoxRect()",
38
+ "useDeferredValue",
39
+ "2000+ rows"
40
+ ]
41
+ };
42
+ function seededRandom(seed) {
43
+ let s = seed;
44
+ return () => {
45
+ s = s * 1664525 + 1013904223 & 2147483647;
46
+ return s / 2147483647;
47
+ };
48
+ }
49
+ const SERVICES = [
50
+ "api",
51
+ "auth",
52
+ "db",
53
+ "cache",
54
+ "worker",
55
+ "gateway",
56
+ "scheduler",
57
+ "metrics",
58
+ "queue",
59
+ "ws"
60
+ ];
61
+ const LOG_TEMPLATES = {
62
+ DEBUG: [
63
+ "Cache miss for key user:session:{id}",
64
+ "Query plan: sequential scan on events ({n} rows)",
65
+ "WebSocket frame received: {n} bytes",
66
+ "GC pause: {n}ms (minor collection)",
67
+ "Connection pool stats: {n} active, {n} idle",
68
+ "Route matched: GET /api/v2/resources/{id}",
69
+ "DNS resolution took {n}ms for upstream.svc",
70
+ "Retry backoff: sleeping {n}ms before attempt"
71
+ ],
72
+ INFO: [
73
+ "Request completed: 200 OK ({n}ms)",
74
+ "User {id} authenticated via OAuth",
75
+ "Background job processed: email_dispatch #{id}",
76
+ "Server listening on port {n}",
77
+ "Database migration applied: v{n}",
78
+ "Health check passed (latency: {n}ms)",
79
+ "Deployed version 2.{n}.0 to production",
80
+ "Cache warmed: {n} entries loaded in {n}ms"
81
+ ],
82
+ WARN: [
83
+ "Slow query detected: {n}ms (threshold: 200ms)",
84
+ "Rate limit approaching: {n}/1000 requests",
85
+ "Memory usage: {n}% of allocated heap",
86
+ "Retry attempt {n}/3 for external API call",
87
+ "Certificate expires in {n} days",
88
+ "Connection pool near capacity: {n}/100",
89
+ "Request body exceeds {n}KB soft limit",
90
+ "Stale cache entry served for key products:{id}"
91
+ ],
92
+ ERROR: [
93
+ "Unhandled exception in request handler: TypeError",
94
+ "Database connection refused: ECONNREFUSED",
95
+ "Authentication failed for user {id}: invalid token",
96
+ "Timeout after {n}ms waiting for upstream service",
97
+ "Disk usage critical: {n}% on /var/data",
98
+ "Failed to process message from queue: malformed payload",
99
+ "OOM kill triggered for worker process PID {id}",
100
+ "TLS handshake failed: certificate chain incomplete"
101
+ ]
102
+ };
103
+ const LEVEL_COLORS = {
104
+ DEBUG: "$muted",
105
+ INFO: "$success",
106
+ WARN: "$warning",
107
+ ERROR: "$error"
108
+ };
109
+ const LEVEL_BADGES = {
110
+ DEBUG: "DBG",
111
+ INFO: "INF",
112
+ WARN: "WRN",
113
+ ERROR: "ERR"
114
+ };
115
+ function generateLogs(count) {
116
+ const rng = seededRandom(42);
117
+ const levels = [
118
+ "DEBUG",
119
+ "INFO",
120
+ "INFO",
121
+ "INFO",
122
+ "INFO",
123
+ "WARN",
124
+ "WARN",
125
+ "ERROR"
126
+ ];
127
+ const entries = [];
128
+ const baseHour = 14;
129
+ const baseMinute = 30;
130
+ for (let i = 0; i < count; i++) {
131
+ const level = levels[Math.floor(rng() * levels.length)];
132
+ const templates = LOG_TEMPLATES[level];
133
+ const message = templates[Math.floor(rng() * templates.length)].replace(/\{id\}/g, () => String(Math.floor(rng() * 99999))).replace(/\{n\}/g, () => String(Math.floor(rng() * 999)));
134
+ const totalSeconds = i / count * 1800;
135
+ const h = baseHour + Math.floor((baseMinute * 60 + totalSeconds) / 3600);
136
+ const m = Math.floor((baseMinute * 60 + totalSeconds) % 3600 / 60);
137
+ const s = Math.floor(totalSeconds % 60);
138
+ const ms = Math.floor(rng() * 1e3);
139
+ entries.push({
140
+ id: i,
141
+ timestamp: `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(ms).padStart(3, "0")}`,
142
+ service: SERVICES[Math.floor(rng() * SERVICES.length)],
143
+ level,
144
+ message
145
+ });
146
+ }
147
+ return entries;
148
+ }
149
+ const ALL_LOGS = generateLogs(2e3);
150
+ const PROCESS_NAMES = [
151
+ "node",
152
+ "bun",
153
+ "postgres",
154
+ "redis-server",
155
+ "nginx",
156
+ "docker",
157
+ "sshd",
158
+ "containerd",
159
+ "kubelet",
160
+ "etcd",
161
+ "coredns",
162
+ "prometheus",
163
+ "grafana",
164
+ "elasticsearch",
165
+ "rabbitmq",
166
+ "kafka",
167
+ "consul",
168
+ "vault",
169
+ "haproxy",
170
+ "traefik",
171
+ "envoy",
172
+ "mysql",
173
+ "mongo",
174
+ "clickhouse",
175
+ "influxdb",
176
+ "jenkins",
177
+ "cadvisor",
178
+ "telegraf",
179
+ "deno",
180
+ "esbuild",
181
+ "python3",
182
+ "ruby",
183
+ "java",
184
+ "go",
185
+ "rustc",
186
+ "webpack",
187
+ "vite",
188
+ "swc",
189
+ "chrome",
190
+ "code",
191
+ "tmux",
192
+ "zsh",
193
+ "cron",
194
+ "systemd",
195
+ "rsyslogd",
196
+ "logstash",
197
+ "kibana",
198
+ "alertmanager",
199
+ "buildkitd",
200
+ "registry"
201
+ ];
202
+ const PROCESS_STATUSES = [
203
+ "running",
204
+ "sleeping",
205
+ "stopped",
206
+ "zombie"
207
+ ];
208
+ function generateProcesses(count) {
209
+ const rng = seededRandom(123);
210
+ const procs = [];
211
+ for (let i = 0; i < count; i++) {
212
+ const nameBase = PROCESS_NAMES[Math.floor(rng() * PROCESS_NAMES.length)];
213
+ const hasInstance = rng() > .7;
214
+ const status = rng() < .65 ? "running" : PROCESS_STATUSES[Math.floor(rng() * PROCESS_STATUSES.length)];
215
+ procs.push({
216
+ pid: 1e3 + Math.floor(rng() * 6e4),
217
+ name: hasInstance ? `${nameBase}:${Math.floor(rng() * 16)}` : nameBase,
218
+ cpu: status === "running" ? Math.round(rng() * 1e3) / 10 : 0,
219
+ mem: Math.round(rng() * 800) / 10,
220
+ status
221
+ });
222
+ }
223
+ return procs;
224
+ }
225
+ const INITIAL_PROCESSES = generateProcesses(50);
226
+ const STATUS_COLORS = {
227
+ running: "$success",
228
+ sleeping: "$muted",
229
+ stopped: "$warning",
230
+ zombie: "$error"
231
+ };
232
+ const STATUS_ICONS = {
233
+ running: "▶",
234
+ sleeping: "◌",
235
+ stopped: "■",
236
+ zombie: "☠"
237
+ };
238
+ const SORT_COLUMNS = [
239
+ "cpu",
240
+ "mem",
241
+ "pid",
242
+ "name",
243
+ "status"
244
+ ];
245
+ function LogRow({ entry, isSelected }) {
246
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
247
+ paddingX: 1,
248
+ backgroundColor: isSelected ? "$mutedbg" : void 0,
249
+ children: [
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [entry.timestamp, " "] }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
252
+ color: LEVEL_COLORS[entry.level],
253
+ bold: true,
254
+ children: LEVEL_BADGES[entry.level]
255
+ }),
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
257
+ " [",
258
+ entry.service.padEnd(9),
259
+ "] "
260
+ ] }),
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: entry.message })
262
+ ]
263
+ });
264
+ }
265
+ function LogListArea({ entries, cursor }) {
266
+ const { height } = useBoxRect();
267
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ListView, {
268
+ items: entries,
269
+ height,
270
+ estimateHeight: 1,
271
+ scrollTo: cursor,
272
+ overscan: 5,
273
+ renderItem: (entry, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LogRow, {
274
+ entry,
275
+ isSelected: index === cursor
276
+ }, entry.id)
277
+ });
278
+ }
279
+ function LevelToggles({ levels, onToggle }) {
280
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
281
+ gap: 1,
282
+ children: [
283
+ "DEBUG",
284
+ "INFO",
285
+ "WARN",
286
+ "ERROR"
287
+ ].map((level, i) => {
288
+ const active = levels[level];
289
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
290
+ gap: 0,
291
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
292
+ color: "$muted",
293
+ dim: true,
294
+ children: [i + 1, ":"]
295
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
296
+ color: active ? LEVEL_COLORS[level] : "$muted",
297
+ bold: active,
298
+ dim: !active,
299
+ strikethrough: !active,
300
+ children: LEVEL_BADGES[level]
301
+ })]
302
+ }, level);
303
+ })
304
+ });
305
+ }
306
+ function useColumns(totalWidth) {
307
+ return useMemo(() => {
308
+ const pidW = 7;
309
+ const cpuW = 8;
310
+ const memW = 8;
311
+ const statusW = 11;
312
+ const fixed = pidW + cpuW + memW + statusW + 4;
313
+ return {
314
+ pidW,
315
+ nameW: Math.max(12, totalWidth - fixed),
316
+ cpuW,
317
+ memW,
318
+ statusW
319
+ };
320
+ }, [totalWidth]);
321
+ }
322
+ function ProcessHeader({ width }) {
323
+ const cols = useColumns(width);
324
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
325
+ paddingX: 1,
326
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
327
+ bold: true,
328
+ color: "$muted",
329
+ children: [
330
+ "PID".padEnd(cols.pidW),
331
+ "NAME".padEnd(cols.nameW),
332
+ "CPU%".padStart(cols.cpuW),
333
+ "MEM%".padStart(cols.memW),
334
+ " ",
335
+ "STATUS".padEnd(cols.statusW)
336
+ ]
337
+ })
338
+ });
339
+ }
340
+ function ProcessRow({ proc, isSelected, width }) {
341
+ const cols = useColumns(width);
342
+ const cpuColor = proc.cpu > 80 ? "$error" : proc.cpu > 40 ? "$warning" : "$success";
343
+ const displayName = proc.name.length > cols.nameW - 1 ? proc.name.slice(0, cols.nameW - 2) + "…" : proc.name;
344
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
345
+ paddingX: 1,
346
+ backgroundColor: isSelected ? "$mutedbg" : void 0,
347
+ children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
349
+ color: "$muted",
350
+ children: String(proc.pid).padEnd(cols.pidW)
351
+ }),
352
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
353
+ bold: isSelected,
354
+ children: displayName.padEnd(cols.nameW)
355
+ }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
357
+ color: cpuColor,
358
+ children: [proc.cpu.toFixed(1).padStart(cols.cpuW - 1), "%"]
359
+ }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
361
+ color: proc.mem > 40 ? "$warning" : "$muted",
362
+ children: [proc.mem.toFixed(1).padStart(cols.memW - 1), "%"]
363
+ }),
364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
366
+ color: STATUS_COLORS[proc.status],
367
+ children: [
368
+ STATUS_ICONS[proc.status],
369
+ " ",
370
+ proc.status.padEnd(cols.statusW - 2)
371
+ ]
372
+ })
373
+ ]
374
+ });
375
+ }
376
+ function ProcessListArea({ processes, cursor, width }) {
377
+ const { height } = useBoxRect();
378
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ListView, {
379
+ items: processes,
380
+ height,
381
+ estimateHeight: 1,
382
+ scrollTo: cursor,
383
+ overscan: 5,
384
+ renderItem: (proc, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProcessRow, {
385
+ proc,
386
+ isSelected: index === cursor,
387
+ width
388
+ }, proc.pid)
389
+ });
390
+ }
391
+ function Explorer() {
392
+ const { exit } = useApp();
393
+ const { width } = useBoxRect();
394
+ const [activeTab, setActiveTab] = useState("logs");
395
+ const [searchMode, setSearchMode] = useState(false);
396
+ const [query, setQuery] = useState("");
397
+ const deferredQuery = useDeferredValue(query);
398
+ const [logCursor, setLogCursor] = useState(0);
399
+ const [logLevels, setLogLevels] = useState({
400
+ DEBUG: true,
401
+ INFO: true,
402
+ WARN: true,
403
+ ERROR: true
404
+ });
405
+ const [procCursor, setProcCursor] = useState(0);
406
+ const [sortCol, setSortCol] = useState("cpu");
407
+ const [processes, setProcesses] = useState(INITIAL_PROCESSES);
408
+ const jitterRef = useRef(null);
409
+ useEffect(() => {
410
+ const rng = seededRandom(Date.now());
411
+ jitterRef.current = setInterval(() => {
412
+ setProcesses((prev) => prev.map((p) => {
413
+ if (p.status !== "running") return p;
414
+ const cpuDelta = (rng() - .5) * 6;
415
+ const memDelta = (rng() - .5) * 2;
416
+ return {
417
+ ...p,
418
+ cpu: Math.max(0, Math.min(100, Math.round((p.cpu + cpuDelta) * 10) / 10)),
419
+ mem: Math.max(0, Math.min(100, Math.round((p.mem + memDelta) * 10) / 10))
420
+ };
421
+ }));
422
+ }, 2e3);
423
+ return () => {
424
+ if (jitterRef.current) clearInterval(jitterRef.current);
425
+ };
426
+ }, []);
427
+ const filteredLogs = useMemo(() => {
428
+ let logs = ALL_LOGS.filter((e) => logLevels[e.level]);
429
+ if (deferredQuery) {
430
+ const q = deferredQuery.toLowerCase();
431
+ logs = logs.filter((e) => e.message.toLowerCase().includes(q) || e.service.toLowerCase().includes(q) || e.level.toLowerCase().includes(q));
432
+ }
433
+ return logs;
434
+ }, [deferredQuery, logLevels]);
435
+ const filteredProcesses = useMemo(() => {
436
+ let procs = processes;
437
+ if (deferredQuery) {
438
+ const q = deferredQuery.toLowerCase();
439
+ procs = procs.filter((p) => p.name.toLowerCase().includes(q) || p.status.includes(q) || String(p.pid).includes(q));
440
+ }
441
+ return [...procs].sort((a, b) => {
442
+ switch (sortCol) {
443
+ case "cpu": return b.cpu - a.cpu;
444
+ case "mem": return b.mem - a.mem;
445
+ case "pid": return a.pid - b.pid;
446
+ case "name": return a.name.localeCompare(b.name);
447
+ case "status": return a.status.localeCompare(b.status);
448
+ }
449
+ });
450
+ }, [
451
+ processes,
452
+ deferredQuery,
453
+ sortCol
454
+ ]);
455
+ const currentItems = activeTab === "logs" ? filteredLogs : filteredProcesses;
456
+ const setCursor = activeTab === "logs" ? setLogCursor : setProcCursor;
457
+ const halfPage = Math.max(1, Math.floor(20 / 2));
458
+ const effectiveLogCursor = Math.min(logCursor, Math.max(0, filteredLogs.length - 1));
459
+ const effectiveProcCursor = Math.min(procCursor, Math.max(0, filteredProcesses.length - 1));
460
+ const handleSearchSubmit = useCallback(() => {
461
+ setSearchMode(false);
462
+ }, []);
463
+ const toggleLevel = useCallback((level) => {
464
+ setLogLevels((prev) => ({
465
+ ...prev,
466
+ [level]: !prev[level]
467
+ }));
468
+ setLogCursor(0);
469
+ }, []);
470
+ useInput(useCallback((input, key) => {
471
+ if (searchMode) {
472
+ if (key.escape) {
473
+ setSearchMode(false);
474
+ return;
475
+ }
476
+ return;
477
+ }
478
+ if (input === "q" || key.escape) {
479
+ exit();
480
+ return;
481
+ }
482
+ if (input === "/") {
483
+ setSearchMode(true);
484
+ return;
485
+ }
486
+ if (activeTab === "logs") {
487
+ const levelMap = {
488
+ "1": "DEBUG",
489
+ "2": "INFO",
490
+ "3": "WARN",
491
+ "4": "ERROR"
492
+ };
493
+ if (levelMap[input]) {
494
+ toggleLevel(levelMap[input]);
495
+ return;
496
+ }
497
+ }
498
+ if (activeTab === "processes" && input === "s") {
499
+ setSortCol((prev) => {
500
+ return SORT_COLUMNS[(SORT_COLUMNS.indexOf(prev) + 1) % SORT_COLUMNS.length];
501
+ });
502
+ return;
503
+ }
504
+ if (input === "j" || key.downArrow) setCursor((c) => Math.min(currentItems.length - 1, c + 1));
505
+ if (input === "k" || key.upArrow) setCursor((c) => Math.max(0, c - 1));
506
+ if (input === "d" || key.pageDown) setCursor((c) => Math.min(currentItems.length - 1, c + halfPage));
507
+ if (input === "u" || key.pageUp) setCursor((c) => Math.max(0, c - halfPage));
508
+ if (input === "g" || key.home) setCursor(0);
509
+ if (input === "G" || key.end) setCursor(currentItems.length - 1);
510
+ if (key.backspace && query) {
511
+ setQuery("");
512
+ setCursor(0);
513
+ }
514
+ }, [
515
+ searchMode,
516
+ exit,
517
+ activeTab,
518
+ currentItems.length,
519
+ halfPage,
520
+ query,
521
+ toggleLevel,
522
+ setCursor
523
+ ]));
524
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
525
+ flexDirection: "column",
526
+ flexGrow: 1,
527
+ children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
529
+ paddingX: 1,
530
+ children: searchMode ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
531
+ flexGrow: 1,
532
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
533
+ color: "$primary",
534
+ bold: true,
535
+ children: ["/", " "]
536
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextInput, {
537
+ value: query,
538
+ onChange: (v) => {
539
+ setQuery(v);
540
+ setLogCursor(0);
541
+ setProcCursor(0);
542
+ },
543
+ onSubmit: handleSearchSubmit,
544
+ prompt: "",
545
+ isActive: searchMode
546
+ })]
547
+ }) : query ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
548
+ "filter: ",
549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
550
+ bold: true,
551
+ children: query
552
+ }),
553
+ " (",
554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "backspace" }),
555
+ " clear, ",
556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "/" }),
557
+ " edit)"
558
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "/" }), " search"] })
559
+ }),
560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tabs, {
561
+ value: activeTab,
562
+ onChange: setActiveTab,
563
+ isActive: !searchMode,
564
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
565
+ paddingX: 1,
566
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TabList, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tab, {
567
+ value: "logs",
568
+ children: [
569
+ "Logs (",
570
+ filteredLogs.length.toLocaleString(),
571
+ ")"
572
+ ]
573
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tab, {
574
+ value: "processes",
575
+ children: [
576
+ "Processes (",
577
+ filteredProcesses.length,
578
+ ")"
579
+ ]
580
+ })] })
581
+ })
582
+ }),
583
+ activeTab === "logs" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
584
+ paddingX: 1,
585
+ justifyContent: "space-between",
586
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LevelToggles, {
587
+ levels: logLevels,
588
+ onToggle: toggleLevel
589
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
590
+ effectiveLogCursor + 1,
591
+ "/",
592
+ filteredLogs.length.toLocaleString()
593
+ ] })]
594
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
595
+ flexGrow: 1,
596
+ flexDirection: "column",
597
+ children: filteredLogs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LogListArea, {
598
+ entries: filteredLogs,
599
+ cursor: effectiveLogCursor
600
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
601
+ paddingX: 1,
602
+ justifyContent: "center",
603
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "No logs match the current filter" })
604
+ })
605
+ })] }),
606
+ activeTab === "processes" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
608
+ paddingX: 1,
609
+ justifyContent: "space-between",
610
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
611
+ gap: 1,
612
+ children: [
613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "sort:" }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
615
+ bold: true,
616
+ color: "$primary",
617
+ children: sortCol.toUpperCase()
618
+ }),
619
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
620
+ "(",
621
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "s" }),
622
+ " cycle)"
623
+ ] })
624
+ ]
625
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
626
+ effectiveProcCursor + 1,
627
+ "/",
628
+ filteredProcesses.length
629
+ ] })]
630
+ }),
631
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProcessHeader, { width }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
633
+ paddingX: 1,
634
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Divider, {})
635
+ }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
637
+ flexGrow: 1,
638
+ flexDirection: "column",
639
+ children: filteredProcesses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProcessListArea, {
640
+ processes: filteredProcesses,
641
+ cursor: effectiveProcCursor,
642
+ width
643
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
644
+ paddingX: 1,
645
+ justifyContent: "center",
646
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "No processes match the current filter" })
647
+ })
648
+ })
649
+ ] }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
651
+ paddingX: 1,
652
+ justifyContent: "space-between",
653
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "h/l" }),
655
+ " tab ",
656
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "j/k" }),
657
+ " navigate ",
658
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "d/u" }),
659
+ " page ",
660
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "/" }),
661
+ " search",
662
+ " ",
663
+ activeTab === "logs" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "1-4" }),
665
+ " levels",
666
+ " "
667
+ ] }),
668
+ activeTab === "processes" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "s" }),
670
+ " sort",
671
+ " "
672
+ ] }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "q" }),
674
+ " quit"
675
+ ] })
676
+ })
677
+ ]
678
+ });
679
+ }
680
+ async function main() {
681
+ try {
682
+ var _usingCtx$1 = _usingCtx();
683
+ const term = _usingCtx$1.u(createTerm());
684
+ const { waitUntilExit } = await render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
685
+ meta,
686
+ controls: "h/l tab j/k navigate d/u page / search 1-4 levels s sort q quit",
687
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Explorer, {})
688
+ }), term);
689
+ await waitUntilExit();
690
+ } catch (_) {
691
+ _usingCtx$1.e = _;
692
+ } finally {
693
+ _usingCtx$1.d();
694
+ }
695
+ }
696
+ if (import.meta.main) await main();
697
+ //#endregion
698
+ export { Explorer, main, meta };