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/[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' },
Comment on lines +18 to +31
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file doesn’t appear to be formatted with the repo’s Prettier config (e.g., extra alignment spaces in object literals and long lines that would normally wrap). Running pnpm format (or formatting this file) will prevent noisy diffs and keep formatting consistent.

Suggested change
{ 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' },
{
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',
},

Copilot uses AI. Check for mistakes.
]

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>
)
}
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>
)
}
168 changes: 134 additions & 34 deletions app/playground/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { TerminalApp } from '@/components/terminal-app'
import { Terminal, TerminalCommand, TerminalDiff, TerminalOutput, TerminalSpinner, TerminalBadge, ThemeSwitcher } from '@/components/terminal'
import {
Terminal,
TerminalCommand,
TerminalDiff,
TerminalOutput,
TerminalSpinner,
TerminalBadge,
TerminalMarker,
TerminalLogLine,
ThemeSwitcher,
} from '@/components/terminal'
import { TerminalProgress } from '@/components/terminal-progress'
import { LogDemo } from './log-demo'
import { LogDemo, StructuredLogDemo } from './log-demo'
import { FilterBarDemo } from './filter-demo'
import { PromptDemo } from './prompt-demo'
import { SearchDemo } from './search-demo'
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SearchDemo is imported from ./search-demo, but there is no app/playground/search-demo.tsx in this branch. This will break compilation for the playground page; add the missing file or remove the import/section.

Suggested change
import { SearchDemo } from './search-demo'

Copilot uses AI. Check for mistakes.
import { TreeDemo } from './tree-demo'
import { TreeKeyboardDemo } from './tree-keyboard-demo'

Expand All @@ -14,35 +26,27 @@ export default function PlaygroundPage() {
return (
<main className="flex flex-col gap-8 p-6 min-h-screen">
<section className="flex flex-wrap items-center justify-between gap-3">
<h1 className="text-xl font-semibold font-mono text-[var(--term-fg)]">
Playground
</h1>
<h1 className="text-xl font-semibold font-mono text-[var(--term-fg)]">Playground</h1>
<ThemeSwitcher />
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
Terminal App
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">Terminal App</h2>
<div className="h-[480px]">
<TerminalApp className="h-full" />
</div>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalPrompt
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalPrompt</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Interactive command input with history navigation (up / down).
</p>
<PromptDemo />
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalProgress
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalProgress</h2>
<Terminal title="progress-demo.sh">
<TerminalCommand>pnpm install</TerminalCommand>
<TerminalProgress label="Resolving packages..." percent={25} variant="yellow" />
Expand All @@ -53,9 +57,7 @@ export default function PlaygroundPage() {
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalSpinner
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalSpinner</h2>
<Terminal title="spinner-demo.sh">
<TerminalCommand>pnpm run build</TerminalCommand>
<TerminalSpinner text="Compiling components..." />
Expand All @@ -64,7 +66,7 @@ export default function PlaygroundPage() {

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalLog
TerminalLog — string mode
</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Simulated streaming logs with capped history and auto-scroll.
Expand All @@ -74,19 +76,28 @@ export default function PlaygroundPage() {

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
Copy Button
TerminalLog — structured mode
</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Structured entries with level badges, timestamps, and source labels via{' '}
<code>entries</code> prop.
</p>
<StructuredLogDemo />
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">Copy Button</h2>
<Terminal title="copy-demo.sh">
<TerminalCommand>pnpm run build</TerminalCommand>
<TerminalOutput type="success">Compiled successfully in 1.2s</TerminalOutput>
<TerminalOutput type="info">Click the copy icon in the header to copy this output.</TerminalOutput>
<TerminalOutput type="info">
Click the copy icon in the header to copy this output.
</TerminalOutput>
</Terminal>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalDiff
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalDiff</h2>
<Terminal title="diff-demo.sh">
<TerminalCommand>git diff -- src/config.ts</TerminalCommand>
<TerminalOutput type="info">Unified</TerminalOutput>
Expand Down Expand Up @@ -124,9 +135,7 @@ export default function PlaygroundPage() {
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalTree
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalTree</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Expandable tree with custom icon, label, and row render props.
</p>
Expand All @@ -138,16 +147,14 @@ export default function PlaygroundPage() {
Tree Keyboard Navigation
</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Arrow keys to navigate, Enter/Space to toggle, ArrowRight to expand/enter, ArrowLeft to collapse/parent.
Arrow keys to navigate, Enter/Space to toggle, ArrowRight to expand/enter, ArrowLeft to
collapse/parent.
</p>
<TreeKeyboardDemo />
</section>


<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalBadge
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalBadge</h2>
<Terminal title="badge-demo.sh">
<TerminalCommand>pnpm run release</TerminalCommand>
<TerminalOutput type="info">
Expand All @@ -162,9 +169,93 @@ export default function PlaygroundPage() {
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
Typing Animation
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalMarker</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Phase separators for visual boundaries in terminal feeds.
</p>
<Terminal title="deploy-pipeline.sh">
<TerminalCommand>npm run deploy:full</TerminalCommand>
<TerminalMarker label="BUILD" timestamp="10:23:45" variant="info" />
<TerminalOutput type="success">✓ Compiled 42 modules</TerminalOutput>
<TerminalOutput type="normal"> dist/main.js 124 KB</TerminalOutput>
<TerminalMarker label="TEST" timestamp="10:24:01" variant="success" />
<TerminalOutput type="success">✓ 24 tests passed</TerminalOutput>
<TerminalOutput type="normal"> coverage: 94%</TerminalOutput>
<TerminalMarker label="DEPLOY" timestamp="10:24:30" variant="warning" />
<TerminalOutput type="info">→ Deploying to production...</TerminalOutput>
<TerminalOutput type="success">✓ Deployed successfully</TerminalOutput>
<TerminalMarker label="DONE" timestamp="10:24:45" variant="success" />
</Terminal>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalLogLine</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Structured log row primitive with level badge, timestamp, source, and message.
</p>
<Terminal title="app.log">
<TerminalCommand>pnpm run start</TerminalCommand>
<TerminalLogLine
level="info"
timestamp="10:23:44"
source="server"
message="Starting application..."
/>
<TerminalLogLine
level="info"
timestamp="10:23:45"
source="server"
message="Listening on :3000"
/>
<TerminalLogLine
level="debug"
timestamp="10:23:46"
source="worker"
message="Spawned 4 worker threads"
/>
<TerminalLogLine
level="warn"
timestamp="10:23:47"
source="auth"
message="Token expiring in 5 min for user #42"
/>
<TerminalLogLine
level="error"
timestamp="10:23:48"
source="db"
message="Connection refused: ECONNREFUSED 127.0.0.1:5432"
/>
<TerminalLogLine
level="info"
timestamp="10:23:48"
source="db"
message="Retrying connection (attempt 1/3)..."
/>
<TerminalLogLine
level="success"
timestamp="10:23:49"
source="db"
message="Connected to postgres in 84ms"
/>
<TerminalLogLine
level="success"
timestamp="10:23:50"
source="build"
message="Compiled 42 modules in 1.2s"
/>
</Terminal>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalSearch</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
In-feed search with next / prev navigation (Enter / Shift+Enter) and match highlighting.
</p>
<SearchDemo />
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">Typing Animation</h2>
<Terminal title="deploy-log.sh">
<TerminalCommand>npm run deploy</TerminalCommand>
<TerminalOutput type="info" animate delay={28}>
Expand All @@ -175,6 +266,15 @@ export default function PlaygroundPage() {
</TerminalOutput>
</Terminal>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalFilterBar</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Controlled filter bar — level toggles, text search, and source toggles. Pair with{' '}
<code>filterEntries()</code> to apply.
</p>
<FilterBarDemo />
</section>
</main>
)
}
Loading
Loading