@silvery/examples 0.5.6 → 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.
- package/dist/UPNG-Cy7ViL8f.mjs +5074 -0
- package/dist/__vite-browser-external-2447137e-BML7CYau.mjs +4 -0
- package/dist/_banner-DLPxCqVy.mjs +44 -0
- package/dist/ansi-CCE2pVS0.mjs +16397 -0
- package/dist/apng-HhhBjRGt.mjs +68 -0
- package/dist/apng-mwUQbTTF.mjs +3 -0
- package/dist/apps/aichat/index.mjs +1299 -0
- package/dist/apps/app-todo.mjs +139 -0
- package/dist/apps/async-data.mjs +204 -0
- package/dist/apps/cli-wizard.mjs +339 -0
- package/dist/apps/clipboard.mjs +198 -0
- package/dist/apps/components.mjs +864 -0
- package/dist/apps/data-explorer.mjs +483 -0
- package/dist/apps/dev-tools.mjs +397 -0
- package/dist/apps/explorer.mjs +698 -0
- package/dist/apps/gallery.mjs +766 -0
- package/dist/apps/inline-bench.mjs +115 -0
- package/dist/apps/kanban.mjs +280 -0
- package/dist/apps/layout-ref.mjs +187 -0
- package/dist/apps/outline.mjs +203 -0
- package/dist/apps/paste-demo.mjs +189 -0
- package/dist/apps/scroll.mjs +86 -0
- package/dist/apps/search-filter.mjs +287 -0
- package/dist/apps/selection.mjs +355 -0
- package/dist/apps/spatial-focus-demo.mjs +388 -0
- package/dist/apps/task-list.mjs +258 -0
- package/dist/apps/terminal-caps-demo.mjs +315 -0
- package/dist/apps/terminal.mjs +872 -0
- package/dist/apps/text-selection-demo.mjs +254 -0
- package/dist/apps/textarea.mjs +178 -0
- package/dist/apps/theme.mjs +661 -0
- package/dist/apps/transform.mjs +215 -0
- package/dist/apps/virtual-10k.mjs +422 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backends-Bahh9mKN.mjs +1179 -0
- package/dist/backends-CCtCDQ94.mjs +3 -0
- package/dist/{cli.mjs → bin/cli.mjs} +21 -25
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/components/counter.mjs +48 -0
- package/dist/components/hello.mjs +31 -0
- package/dist/components/progress-bar.mjs +59 -0
- package/dist/components/select-list.mjs +85 -0
- package/dist/components/spinner.mjs +57 -0
- package/dist/components/text-input.mjs +62 -0
- package/dist/components/virtual-list.mjs +51 -0
- package/dist/flexily-zero-adapter-UB-ra8fR.mjs +3374 -0
- package/dist/gif-BZaqPPVX.mjs +3 -0
- package/dist/gif-BtnXuxLF.mjs +71 -0
- package/dist/gifenc-CLRW41dk.mjs +728 -0
- package/dist/jsx-runtime-dMs_8fNu.mjs +241 -0
- package/dist/key-mapping-5oYQdAQE.mjs +3 -0
- package/dist/key-mapping-D4LR1go6.mjs +130 -0
- package/dist/layout/dashboard.mjs +1204 -0
- package/dist/layout/live-resize.mjs +303 -0
- package/dist/layout/overflow.mjs +70 -0
- package/dist/layout/text-layout.mjs +335 -0
- package/dist/node-NuJ94BWl.mjs +1083 -0
- package/dist/plugins-D1KtkT4a.mjs +3057 -0
- package/dist/resvg-js-C_8Wps1F.mjs +201 -0
- package/dist/src-BTEVGpd9.mjs +23538 -0
- package/dist/src-CUUOuRH6.mjs +5322 -0
- package/dist/src-CzfRafCQ.mjs +814 -0
- package/dist/usingCtx-CsEf0xO3.mjs +57 -0
- package/dist/yoga-adapter-BVtQ5OJR.mjs +237 -0
- package/package.json +19 -14
- package/_banner.tsx +0 -60
- package/apps/aichat/components.tsx +0 -469
- package/apps/aichat/index.tsx +0 -220
- package/apps/aichat/script.ts +0 -460
- package/apps/aichat/state.ts +0 -325
- package/apps/aichat/types.ts +0 -19
- package/apps/app-todo.tsx +0 -201
- package/apps/async-data.tsx +0 -196
- package/apps/cli-wizard.tsx +0 -332
- package/apps/clipboard.tsx +0 -183
- package/apps/components.tsx +0 -658
- package/apps/data-explorer.tsx +0 -490
- package/apps/dev-tools.tsx +0 -395
- package/apps/explorer.tsx +0 -731
- package/apps/gallery.tsx +0 -653
- package/apps/inline-bench.tsx +0 -138
- package/apps/kanban.tsx +0 -265
- package/apps/layout-ref.tsx +0 -173
- package/apps/outline.tsx +0 -160
- package/apps/panes/index.tsx +0 -203
- package/apps/paste-demo.tsx +0 -185
- package/apps/scroll.tsx +0 -77
- package/apps/search-filter.tsx +0 -240
- package/apps/selection.tsx +0 -342
- package/apps/spatial-focus-demo.tsx +0 -368
- package/apps/task-list.tsx +0 -271
- package/apps/terminal-caps-demo.tsx +0 -334
- package/apps/terminal.tsx +0 -800
- package/apps/text-selection-demo.tsx +0 -189
- package/apps/textarea.tsx +0 -155
- package/apps/theme.tsx +0 -515
- package/apps/transform.tsx +0 -229
- package/apps/virtual-10k.tsx +0 -405
- package/apps/vterm-demo/index.tsx +0 -216
- package/components/counter.tsx +0 -45
- package/components/hello.tsx +0 -34
- package/components/progress-bar.tsx +0 -48
- package/components/select-list.tsx +0 -50
- package/components/spinner.tsx +0 -40
- package/components/text-input.tsx +0 -57
- package/components/virtual-list.tsx +0 -52
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs.map +0 -1
- package/layout/dashboard.tsx +0 -953
- package/layout/live-resize.tsx +0 -282
- package/layout/overflow.tsx +0 -51
- package/layout/text-layout.tsx +0 -283
package/apps/dev-tools.tsx
DELETED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dev Tools — Log Viewer Example
|
|
3
|
-
*
|
|
4
|
-
* A live log viewer demonstrating:
|
|
5
|
-
* - ListView for efficient rendering of thousands of log entries
|
|
6
|
-
* - Keyboard shortcuts to add log entries at different severity levels
|
|
7
|
-
* - Color-coded severity levels (DEBUG, INFO, WARN, ERROR)
|
|
8
|
-
* - j/k navigation through log history
|
|
9
|
-
* - Auto-scroll to latest entry
|
|
10
|
-
*
|
|
11
|
-
* Usage: bun run examples/apps/dev-tools.tsx
|
|
12
|
-
*
|
|
13
|
-
* Controls:
|
|
14
|
-
* j/k or Up/Down - Navigate through log entries
|
|
15
|
-
* g/G - Jump to first/last entry
|
|
16
|
-
* d - Add DEBUG entry
|
|
17
|
-
* i - Add INFO entry
|
|
18
|
-
* w - Add WARN entry
|
|
19
|
-
* e - Add ERROR entry
|
|
20
|
-
* c - Clear all logs
|
|
21
|
-
* q or Esc - Quit
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import React, { useState, useCallback, useMemo } from "react"
|
|
25
|
-
import {
|
|
26
|
-
render,
|
|
27
|
-
Box,
|
|
28
|
-
Text,
|
|
29
|
-
ListView,
|
|
30
|
-
Divider,
|
|
31
|
-
useBoxRect,
|
|
32
|
-
useInput,
|
|
33
|
-
useApp,
|
|
34
|
-
createTerm,
|
|
35
|
-
Strong,
|
|
36
|
-
Kbd,
|
|
37
|
-
Muted,
|
|
38
|
-
type Key,
|
|
39
|
-
} from "silvery"
|
|
40
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
41
|
-
|
|
42
|
-
export const meta: ExampleMeta = {
|
|
43
|
-
name: "Dev Tools",
|
|
44
|
-
description: "Log viewer with severity levels, ListView, and keyboard-driven log injection",
|
|
45
|
-
features: ["ListView", "useInput()", "useBoxRect()", "keyboard navigation"],
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ============================================================================
|
|
49
|
-
// Types
|
|
50
|
-
// ============================================================================
|
|
51
|
-
|
|
52
|
-
type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"
|
|
53
|
-
|
|
54
|
-
interface LogEntry {
|
|
55
|
-
id: number
|
|
56
|
-
timestamp: Date
|
|
57
|
-
level: LogLevel
|
|
58
|
-
source: string
|
|
59
|
-
message: string
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ============================================================================
|
|
63
|
-
// Data Generation
|
|
64
|
-
// ============================================================================
|
|
65
|
-
|
|
66
|
-
const SOURCES = ["http", "db", "auth", "cache", "worker", "api", "scheduler", "queue", "metrics", "ws"]
|
|
67
|
-
|
|
68
|
-
const LOG_TEMPLATES: Record<LogLevel, string[]> = {
|
|
69
|
-
DEBUG: [
|
|
70
|
-
"Cache miss for key user:session:{{id}}",
|
|
71
|
-
"Query plan: sequential scan on events ({{n}} rows)",
|
|
72
|
-
"WebSocket frame received: {{n}} bytes",
|
|
73
|
-
"GC pause: {{n}}ms (minor collection)",
|
|
74
|
-
"Connection pool stats: {{n}} active, {{n}} idle",
|
|
75
|
-
"Route matched: GET /api/v2/resources/{{id}}",
|
|
76
|
-
],
|
|
77
|
-
INFO: [
|
|
78
|
-
"Request completed: 200 OK ({{n}}ms)",
|
|
79
|
-
"User {{id}} authenticated via OAuth",
|
|
80
|
-
"Background job processed: email_dispatch #{{id}}",
|
|
81
|
-
"Server listening on port {{n}}",
|
|
82
|
-
"Database migration applied: v{{n}}",
|
|
83
|
-
"Health check passed (latency: {{n}}ms)",
|
|
84
|
-
],
|
|
85
|
-
WARN: [
|
|
86
|
-
"Slow query detected: {{n}}ms (threshold: 200ms)",
|
|
87
|
-
"Rate limit approaching: {{n}}/1000 requests",
|
|
88
|
-
"Memory usage: {{n}}% of allocated heap",
|
|
89
|
-
"Retry attempt {{n}}/3 for external API call",
|
|
90
|
-
"Certificate expires in {{n}} days",
|
|
91
|
-
"Connection pool near capacity: {{n}}/100",
|
|
92
|
-
],
|
|
93
|
-
ERROR: [
|
|
94
|
-
"Unhandled exception in request handler: TypeError",
|
|
95
|
-
"Database connection refused: ECONNREFUSED",
|
|
96
|
-
"Authentication failed for user {{id}}: invalid token",
|
|
97
|
-
"Timeout after {{n}}ms waiting for upstream service",
|
|
98
|
-
"Disk usage critical: {{n}}% on /var/data",
|
|
99
|
-
"Failed to process message from queue: malformed payload",
|
|
100
|
-
],
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let nextLogId = 1
|
|
104
|
-
|
|
105
|
-
function seededRandom(seed: number): () => number {
|
|
106
|
-
let s = seed
|
|
107
|
-
return () => {
|
|
108
|
-
s = (s * 1664525 + 1013904223) & 0x7fffffff
|
|
109
|
-
return s / 0x7fffffff
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function generateMessage(level: LogLevel, rng: () => number): string {
|
|
114
|
-
const templates = LOG_TEMPLATES[level]
|
|
115
|
-
const template = templates[Math.floor(rng() * templates.length)]!
|
|
116
|
-
return template
|
|
117
|
-
.replace(/\{\{id\}\}/g, () => String(Math.floor(rng() * 99999)))
|
|
118
|
-
.replace(/\{\{n\}\}/g, () => String(Math.floor(rng() * 999)))
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function createLogEntry(level: LogLevel, rng: () => number): LogEntry {
|
|
122
|
-
return {
|
|
123
|
-
id: nextLogId++,
|
|
124
|
-
timestamp: new Date(),
|
|
125
|
-
level,
|
|
126
|
-
source: SOURCES[Math.floor(rng() * SOURCES.length)]!,
|
|
127
|
-
message: generateMessage(level, rng),
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function generateInitialLogs(count: number): LogEntry[] {
|
|
132
|
-
const rng = seededRandom(42)
|
|
133
|
-
const levels: LogLevel[] = ["DEBUG", "INFO", "INFO", "INFO", "WARN", "ERROR"]
|
|
134
|
-
const entries: LogEntry[] = []
|
|
135
|
-
const now = Date.now()
|
|
136
|
-
|
|
137
|
-
for (let i = 0; i < count; i++) {
|
|
138
|
-
const level = levels[Math.floor(rng() * levels.length)]!
|
|
139
|
-
const entry = createLogEntry(level, rng)
|
|
140
|
-
// Spread timestamps over the last hour
|
|
141
|
-
entry.timestamp = new Date(now - (count - i) * 1200)
|
|
142
|
-
entries.push(entry)
|
|
143
|
-
}
|
|
144
|
-
return entries
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// Constants
|
|
149
|
-
// ============================================================================
|
|
150
|
-
|
|
151
|
-
const LEVEL_COLORS: Record<LogLevel, string> = {
|
|
152
|
-
DEBUG: "$muted",
|
|
153
|
-
INFO: "$primary",
|
|
154
|
-
WARN: "$warning",
|
|
155
|
-
ERROR: "$error",
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const LEVEL_BADGES: Record<LogLevel, string> = {
|
|
159
|
-
DEBUG: "DBG",
|
|
160
|
-
INFO: "INF",
|
|
161
|
-
WARN: "WRN",
|
|
162
|
-
ERROR: "ERR",
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ============================================================================
|
|
166
|
-
// Components
|
|
167
|
-
// ============================================================================
|
|
168
|
-
|
|
169
|
-
function formatTime(date: Date): string {
|
|
170
|
-
return date.toLocaleTimeString("en-US", {
|
|
171
|
-
hour: "2-digit",
|
|
172
|
-
minute: "2-digit",
|
|
173
|
-
second: "2-digit",
|
|
174
|
-
hour12: false,
|
|
175
|
-
})
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function LogRow({ entry, isSelected }: { entry: LogEntry; isSelected: boolean }) {
|
|
179
|
-
const badge = LEVEL_BADGES[entry.level]
|
|
180
|
-
const color = LEVEL_COLORS[entry.level]
|
|
181
|
-
|
|
182
|
-
// When selected, use $primary-fg for all text to ensure contrast against $primary bg.
|
|
183
|
-
// When not selected, use level-specific colors for visual distinction.
|
|
184
|
-
if (isSelected) {
|
|
185
|
-
return (
|
|
186
|
-
<Box paddingX={1} backgroundColor="$primary">
|
|
187
|
-
<Text color="$primary-fg">{formatTime(entry.timestamp)} </Text>
|
|
188
|
-
<Text color="$primary-fg" bold>
|
|
189
|
-
{badge}
|
|
190
|
-
</Text>
|
|
191
|
-
<Text color="$primary-fg"> [{entry.source.padEnd(9)}] </Text>
|
|
192
|
-
<Text color="$primary-fg" bold>
|
|
193
|
-
{entry.message}
|
|
194
|
-
</Text>
|
|
195
|
-
</Box>
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<Box paddingX={1}>
|
|
201
|
-
<Muted>{formatTime(entry.timestamp)} </Muted>
|
|
202
|
-
<Strong color={color}>{badge}</Strong>
|
|
203
|
-
<Muted> [{entry.source.padEnd(9)}] </Muted>
|
|
204
|
-
<Text>{entry.message}</Text>
|
|
205
|
-
</Box>
|
|
206
|
-
)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function LevelCounts({ entries }: { entries: LogEntry[] }) {
|
|
210
|
-
const counts = useMemo(() => {
|
|
211
|
-
const c = { DEBUG: 0, INFO: 0, WARN: 0, ERROR: 0 }
|
|
212
|
-
for (const e of entries) c[e.level]++
|
|
213
|
-
return c
|
|
214
|
-
}, [entries])
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<Box gap={2}>
|
|
218
|
-
<Strong color="$muted">
|
|
219
|
-
{LEVEL_BADGES.DEBUG}:{counts.DEBUG}
|
|
220
|
-
</Strong>
|
|
221
|
-
<Strong color="$primary">
|
|
222
|
-
{LEVEL_BADGES.INFO}:{counts.INFO}
|
|
223
|
-
</Strong>
|
|
224
|
-
<Strong color="$warning">
|
|
225
|
-
{LEVEL_BADGES.WARN}:{counts.WARN}
|
|
226
|
-
</Strong>
|
|
227
|
-
<Strong color="$error">
|
|
228
|
-
{LEVEL_BADGES.ERROR}:{counts.ERROR}
|
|
229
|
-
</Strong>
|
|
230
|
-
</Box>
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** Inner component that reads the flex container's height via useBoxRect */
|
|
235
|
-
function LogListArea({ entries, cursor }: { entries: LogEntry[]; cursor: number }) {
|
|
236
|
-
const { height } = useBoxRect()
|
|
237
|
-
|
|
238
|
-
return (
|
|
239
|
-
<ListView
|
|
240
|
-
items={entries}
|
|
241
|
-
height={height}
|
|
242
|
-
estimateHeight={1}
|
|
243
|
-
scrollTo={cursor}
|
|
244
|
-
overscan={5}
|
|
245
|
-
renderItem={(entry, index) => <LogRow key={entry.id} entry={entry} isSelected={index === cursor} />}
|
|
246
|
-
/>
|
|
247
|
-
)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ============================================================================
|
|
251
|
-
// Main App
|
|
252
|
-
// ============================================================================
|
|
253
|
-
|
|
254
|
-
const INITIAL_COUNT = 200
|
|
255
|
-
const rng = seededRandom(12345)
|
|
256
|
-
|
|
257
|
-
export function DevTools() {
|
|
258
|
-
const { exit } = useApp()
|
|
259
|
-
const [entries, setEntries] = useState<LogEntry[]>(() => generateInitialLogs(INITIAL_COUNT))
|
|
260
|
-
const [cursor, setCursor] = useState(INITIAL_COUNT - 1)
|
|
261
|
-
const [autoScroll, setAutoScroll] = useState(true)
|
|
262
|
-
|
|
263
|
-
const addEntry = useCallback(
|
|
264
|
-
(level: LogLevel) => {
|
|
265
|
-
const entry = createLogEntry(level, rng)
|
|
266
|
-
setEntries((prev) => [...prev, entry])
|
|
267
|
-
if (autoScroll) {
|
|
268
|
-
setCursor((prev) => prev + 1)
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
[autoScroll],
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
useInput(
|
|
275
|
-
useCallback(
|
|
276
|
-
(input: string, key: Key) => {
|
|
277
|
-
// Quit
|
|
278
|
-
if (input === "q" || key.escape) {
|
|
279
|
-
exit()
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Navigation
|
|
284
|
-
if (input === "j" || key.downArrow) {
|
|
285
|
-
setCursor((c) => Math.min(entries.length - 1, c + 1))
|
|
286
|
-
setAutoScroll(false)
|
|
287
|
-
return
|
|
288
|
-
}
|
|
289
|
-
if (input === "k" || key.upArrow) {
|
|
290
|
-
setCursor((c) => Math.max(0, c - 1))
|
|
291
|
-
setAutoScroll(false)
|
|
292
|
-
return
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Jump to start/end
|
|
296
|
-
if (input === "g" || key.home) {
|
|
297
|
-
setCursor(0)
|
|
298
|
-
setAutoScroll(false)
|
|
299
|
-
return
|
|
300
|
-
}
|
|
301
|
-
if (input === "G" || key.end) {
|
|
302
|
-
setCursor(entries.length - 1)
|
|
303
|
-
setAutoScroll(true)
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Add log entries
|
|
308
|
-
if (input === "d") {
|
|
309
|
-
addEntry("DEBUG")
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
if (input === "i") {
|
|
313
|
-
addEntry("INFO")
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
if (input === "w") {
|
|
317
|
-
addEntry("WARN")
|
|
318
|
-
return
|
|
319
|
-
}
|
|
320
|
-
if (input === "e") {
|
|
321
|
-
addEntry("ERROR")
|
|
322
|
-
return
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Clear
|
|
326
|
-
if (input === "c") {
|
|
327
|
-
setEntries([])
|
|
328
|
-
setCursor(0)
|
|
329
|
-
setAutoScroll(true)
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
[entries.length, exit, addEntry],
|
|
334
|
-
),
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
return (
|
|
338
|
-
<Box flexDirection="column" flexGrow={1} padding={1}>
|
|
339
|
-
{/* Header */}
|
|
340
|
-
<Box justifyContent="space-between" backgroundColor="$surfacebg">
|
|
341
|
-
<Box gap={2}>
|
|
342
|
-
<Text bold color="$primary">
|
|
343
|
-
{"▸"} Log Viewer
|
|
344
|
-
</Text>
|
|
345
|
-
<LevelCounts entries={entries} />
|
|
346
|
-
</Box>
|
|
347
|
-
<Box gap={1}>
|
|
348
|
-
{autoScroll && (
|
|
349
|
-
<Text backgroundColor="$success" color="$success-fg" bold>
|
|
350
|
-
{" LIVE "}
|
|
351
|
-
</Text>
|
|
352
|
-
)}
|
|
353
|
-
<Strong color="$primary">{cursor + 1}</Strong>
|
|
354
|
-
<Muted>/ {entries.length}</Muted>
|
|
355
|
-
</Box>
|
|
356
|
-
</Box>
|
|
357
|
-
|
|
358
|
-
{/* Column headers */}
|
|
359
|
-
<Box paddingX={1}>
|
|
360
|
-
<Muted>{"Time "} </Muted>
|
|
361
|
-
<Muted>{"Lvl"} </Muted>
|
|
362
|
-
<Muted>{"[Source ]"} </Muted>
|
|
363
|
-
<Muted>Message</Muted>
|
|
364
|
-
</Box>
|
|
365
|
-
|
|
366
|
-
<Box paddingX={1}>
|
|
367
|
-
<Divider />
|
|
368
|
-
</Box>
|
|
369
|
-
|
|
370
|
-
{/* Log list in a flex-grow container */}
|
|
371
|
-
<Box flexGrow={1} flexDirection="column">
|
|
372
|
-
<LogListArea entries={entries} cursor={cursor} />
|
|
373
|
-
</Box>
|
|
374
|
-
</Box>
|
|
375
|
-
)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ============================================================================
|
|
379
|
-
// Main
|
|
380
|
-
// ============================================================================
|
|
381
|
-
|
|
382
|
-
async function main() {
|
|
383
|
-
using term = createTerm()
|
|
384
|
-
const { waitUntilExit } = await render(
|
|
385
|
-
<ExampleBanner meta={meta} controls="j/k navigate g/G start/end d/i/w/e add log c clear Esc/q quit">
|
|
386
|
-
<DevTools />
|
|
387
|
-
</ExampleBanner>,
|
|
388
|
-
term,
|
|
389
|
-
)
|
|
390
|
-
await waitUntilExit()
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (import.meta.main) {
|
|
394
|
-
main().catch(console.error)
|
|
395
|
-
}
|