@silvery/examples 0.17.3 → 0.17.5

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