Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions app/playground/filter-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import { useMemo, useState } from 'react'
import {
Terminal,
TerminalCommand,
TerminalLog,
type LogEntry,
} from '@/components/terminal'
import {
TerminalFilterBar,
emptyFilterState,
filterEntries,
type FilterBarState,
} from '@/components/terminal-filter-bar'

const ALL_ENTRIES: LogEntry[] = [
{ id: '1', level: 'info', timestamp: '10:23:44', source: 'server', message: 'Application starting up...' },
{ id: '2', level: 'info', timestamp: '10:23:45', source: 'server', message: 'Listening on :3000' },
{ id: '3', level: 'debug', timestamp: '10:23:45', source: 'cache', message: 'HIT packages/react@19.1.0' },
{ id: '4', level: 'warn', timestamp: '10:23:46', source: 'cache', message: 'MISS @openknots/terminal-ui — fetching from registry' },
{ id: '5', level: 'info', timestamp: '10:23:47', source: 'build', message: 'Compiling 42 modules...' },
{ id: '6', level: 'debug', timestamp: '10:23:47', source: 'build', message: 'Resolved tsconfig paths in 4ms' },
{ id: '7', level: 'success', timestamp: '10:23:48', source: 'build', message: 'Compiled in 1.2s — 0 errors' },
{ id: '8', level: 'info', timestamp: '10:23:49', source: 'test', message: 'Running 24 unit tests...' },
{ id: '9', level: 'error', timestamp: '10:23:50', source: 'test', message: 'FAIL src/utils.test.ts — 1 snapshot mismatch' },
{ id: '10', level: 'info', timestamp: '10:23:50', source: 'test', message: 'Retrying with --updateSnapshot...' },
{ id: '11', level: 'success', timestamp: '10:23:51', source: 'test', message: '24 / 24 tests passed — coverage 94%' },
{ id: '12', level: 'info', timestamp: '10:23:52', source: 'deploy', message: 'Uploading artifacts to CDN...' },
{ id: '13', level: 'warn', timestamp: '10:23:52', source: 'deploy', message: 'Edge cache warm-up taking longer than 5s' },
{ id: '14', level: 'success', timestamp: '10:23:53', source: 'deploy', message: 'Published to production — https://example.app' },
]

const SOURCES = [...new Set(ALL_ENTRIES.map((e) => e.source!))]

export function FilterBarDemo() {
const [filter, setFilter] = useState<FilterBarState>(emptyFilterState)

const visible = useMemo(() => filterEntries(ALL_ENTRIES, filter), [filter])

return (
<div className="flex flex-col gap-2">
<TerminalFilterBar
state={filter}
onChange={setFilter}
sources={SOURCES}
/>
<Terminal title="pipeline.log">
<TerminalCommand>pnpm run ci</TerminalCommand>
<TerminalLog entries={visible} maxLines={20} />
</Terminal>
<p className="font-mono text-xs text-(--term-fg-dim)">
{visible.length} / {ALL_ENTRIES.length} entries shown
</p>
</div>
)
}
201 changes: 201 additions & 0 deletions app/playground/group-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
'use client'

import { useState } from 'react'
import { Terminal, TerminalCommand, TerminalGroup, TerminalLogLine } from '@/components/terminal'

// ── Demo ──────────────────────────────────────────────────────────────────────

/**
* Playground demo for TerminalGroup.
*
* Shows several groups in a single terminal window:
* - A success group (collapsed by default) — install step
* - A warn group (open) — test step with a warning
* - An error group (open) — build step with failures
* - An info group (open) — controlled open/close via external button
*/
export function GroupDemo() {
// Controlled group example
const [deployOpen, setDeployOpen] = useState(true)

return (
<Terminal title="ci-pipeline.log">
<TerminalCommand>pnpm run ci</TerminalCommand>

{/* ── Install step: collapsed by default ── */}
<div className="px-3 pt-2">
<TerminalGroup
title="install"
summary="42 packages resolved from cache"
countLabel="0.8s"
variant="success"
defaultOpen={false}
>
<TerminalLogLine
level="debug"
timestamp="10:23:44"
source="npm"
message="Resolving dependency tree…"
/>
<TerminalLogLine
level="debug"
timestamp="10:23:44"
source="npm"
message="HIT react@19.2.0"
/>
<TerminalLogLine
level="debug"
timestamp="10:23:44"
source="npm"
message="HIT typescript@5.8.2"
/>
<TerminalLogLine
level="success"
timestamp="10:23:44"
source="npm"
message="42 packages installed from cache"
/>
</TerminalGroup>
</div>

{/* ── Lint step: info, collapsed by default ── */}
<div className="px-3 pt-1">
<TerminalGroup
title="lint"
summary="ruff check — no issues"
countLabel="0.2s"
variant="info"
defaultOpen={false}
>
<TerminalLogLine
level="info"
timestamp="10:23:45"
source="ruff"
message="Checking 38 files…"
/>
<TerminalLogLine
level="success"
timestamp="10:23:45"
source="ruff"
message="All checks passed"
/>
</TerminalGroup>
</div>

{/* ── Test step: warn, open ── */}
<div className="px-3 pt-1">
<TerminalGroup
title="test"
summary="23/24 passed · 1 snapshot mismatch"
countLabel="4.1s"
variant="warn"
defaultOpen
>
<TerminalLogLine
level="info"
timestamp="10:23:46"
source="jest"
message="Running 24 test suites…"
/>
<TerminalLogLine
level="success"
timestamp="10:23:49"
source="jest"
message="23 tests passed"
/>
<TerminalLogLine
level="warn"
timestamp="10:23:49"
source="jest"
message="FAIL src/utils.test.ts — 1 snapshot out of date"
/>
<TerminalLogLine
level="info"
timestamp="10:23:49"
source="jest"
message="Run with --updateSnapshot to fix"
/>
</TerminalGroup>
</div>

{/* ── Build step: error, open ── */}
<div className="px-3 pt-1">
<TerminalGroup
title="build"
summary="TypeScript compilation failed"
countLabel="3 errors"
variant="error"
defaultOpen
>
<TerminalLogLine
level="info"
timestamp="10:23:50"
source="tsc"
message="Compiling 42 modules…"
/>
<TerminalLogLine
level="error"
timestamp="10:23:51"
source="tsc"
message="src/api/handler.ts:14 — Property 'data' does not exist on type 'Response'"
/>
<TerminalLogLine
level="error"
timestamp="10:23:51"
source="tsc"
message="src/api/handler.ts:22 — Argument of type 'string' is not assignable to 'number'"
/>
<TerminalLogLine
level="error"
timestamp="10:23:51"
source="tsc"
message="src/utils/retry.ts:8 — Cannot find module '../types' or its type declarations"
/>
</TerminalGroup>
</div>

{/* ── Deploy step: controlled ── */}
<div className="px-3 pt-1 pb-3">
<TerminalGroup
title="deploy"
summary="Production rollout"
countLabel="2.3s"
variant="info"
open={deployOpen}
onOpenChange={setDeployOpen}
>
<TerminalLogLine
level="info"
timestamp="10:23:52"
source="k8s"
message="Rolling update → deployment/api-server"
/>
<TerminalLogLine
level="info"
timestamp="10:23:53"
source="k8s"
message="4/4 pods healthy"
/>
<TerminalLogLine
level="success"
timestamp="10:23:54"
source="k8s"
message="Deployment complete. Live at https://api.example.com"
/>
</TerminalGroup>

{/* External controlled toggle */}
<div className="mt-2 pl-1">
<button
type="button"
onClick={() => setDeployOpen((v) => !v)}
className="font-mono text-xs text-(--term-fg-dim) underline underline-offset-2
hover:text-(--term-fg) transition-colors"
>
{deployOpen ? '▲ collapse deploy (controlled)' : '▼ expand deploy (controlled)'}
</button>
</div>
</div>
</Terminal>
)
}
61 changes: 60 additions & 1 deletion app/playground/log-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client'

import { useEffect, useState } from 'react'
import { Terminal, TerminalCommand, TerminalLog } from '@/components/terminal'
import { Terminal, TerminalCommand, TerminalLog, type LogEntry } from '@/components/terminal'

// ── String-mode demo (unchanged) ─────────────────────────────────────────────

const STREAM_LINES = [
'[info] Connecting to build worker...',
Expand Down Expand Up @@ -38,3 +40,60 @@ export function LogDemo() {
</Terminal>
)
}

// ── Structured-mode demo ─────────────────────────────────────────────────────

const STREAM_ENTRIES: LogEntry[] = [
{ level: 'info', timestamp: '10:23:45', source: 'server', message: 'Worker connected' },
{ level: 'debug', timestamp: '10:23:46', source: 'cache', message: 'HIT packages/react@19.1.0' },
{ level: 'warn', timestamp: '10:23:47', source: 'cache', message: 'MISS @openknots/terminal-ui' },
{ level: 'info', timestamp: '10:23:48', source: 'build', message: 'Compiling 42 modules...' },
{ level: 'success', timestamp: '10:23:49', source: 'build', message: 'Compiled in 1.2s' },
{ level: 'info', timestamp: '10:23:50', source: 'test', message: 'Running 24 unit tests...' },
{
level: 'error',
timestamp: '10:23:51',
source: 'test',
message: 'FAIL src/utils.test.ts — 1 snapshot mismatch',
},
{
level: 'info',
timestamp: '10:23:52',
source: 'test',
message: 'Retrying with --updateSnapshot...',
},
{ level: 'success', timestamp: '10:23:53', source: 'test', message: '24 / 24 tests passed' },
{ level: 'success', timestamp: '10:23:54', source: 'deploy', message: 'Published to production' },
]

export function StructuredLogDemo() {
const [entries, setEntries] = useState<LogEntry[]>([
{
id: 'boot-0',
level: 'info',
timestamp: '10:23:44',
source: 'server',
message: 'Starting pipeline...',
},
])

useEffect(() => {
const timer = window.setInterval(() => {
setEntries((current) => {
const next = STREAM_ENTRIES[current.length % STREAM_ENTRIES.length]
return [...current, { ...next, id: String(current.length) }]
})
}, 900)

return () => {
window.clearInterval(timer)
}
}, [])

return (
<Terminal title="pipeline.log">
<TerminalCommand>pnpm run ci</TerminalCommand>
<TerminalLog entries={entries} maxLines={8} autoScroll />
</Terminal>
)
}
Loading
Loading