Skip to content
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/[email protected]' },
{ 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 [email protected]"
/>
<TerminalLogLine
level="debug"
timestamp="10:23:44"
source="npm"
message="HIT [email protected]"
/>
<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/[email protected]' },
{ 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