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
142 changes: 105 additions & 37 deletions app/playground/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
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 { PromptDemo } from './prompt-demo'
Expand All @@ -14,35 +24,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,40 +55,34 @@ 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..." />
</Terminal>
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
TerminalLog
</h2>
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">TerminalLog</h2>
<p className="text-sm text-[var(--term-fg-dim)] font-mono">
Simulated streaming logs with capped history and auto-scroll.
</p>
<LogDemo />
</section>

<section className="flex flex-col gap-2">
<h2 className="text-lg font-semibold font-mono text-[var(--term-fg)]">
Copy Button
</h2>
<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 +120,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 +132,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 +154,85 @@ 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)]">Typing Animation</h2>
<Terminal title="deploy-log.sh">
<TerminalCommand>npm run deploy</TerminalCommand>
<TerminalOutput type="info" animate delay={28}>
Expand Down
124 changes: 124 additions & 0 deletions components/terminal-log-line.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use client'

import { ReactNode } from 'react'

export interface TerminalLogLineProps {
/** Primary log message content. */
message: ReactNode
/** Severity level β€” controls the label color (default: 'info'). */
level?: 'debug' | 'info' | 'warn' | 'error' | 'success'
/** Optional timestamp string (e.g. "10:23:45" or ISO). */
timestamp?: string
/** Optional source / subsystem label (e.g. "server", "db"). */
source?: string
/** Additional classes for layout overrides. */
className?: string
}

/**
* Per-level color tokens β€” border, background tint, and text colors.
*
* `badge` β€” the [LEVEL] indicator pill (border + bg tint, consistent with TerminalBadge)
* `message` β€” foreground for the message body (dimmed for debug, red for error)
*/
const levelClasses: Record<
NonNullable<TerminalLogLineProps['level']>,
{ badge: string; message: string }
> = {
debug: {
badge: 'border-(--glass-border) text-(--term-fg-dim) bg-[rgba(255,255,255,0.03)]',
message: 'text-(--term-fg-dim)',
},
info: {
badge:
'border-[var(--term-blue)]/40 text-(--term-blue) bg-[color-mix(in_oklab,var(--term-blue)_10%,transparent)]',
message: 'text-(--term-fg)',
Comment on lines +24 to +35
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.

TerminalLogLine uses Tailwind class syntax like text-(--term-fg-dim) / border-(--glass-border), but the rest of this codebase uses arbitrary values (e.g. text-[var(--term-fg-dim)], border-[var(--glass-border)]). Unless the project is intentionally relying on a Tailwind feature/plugin for the parentheses form, these classes likely won't generate styles and the badge/text colors will be wrong. Please switch these to the bracket form for consistency and to ensure Tailwind picks them up.

Copilot uses AI. Check for mistakes.
},
warn: {
badge:
'border-[var(--term-yellow)]/40 text-(--term-yellow) bg-[color-mix(in_oklab,var(--term-yellow)_10%,transparent)]',
message: 'text-(--term-fg)',
},
error: {
badge:
'border-[var(--term-red)]/40 text-(--term-red) bg-[color-mix(in_oklab,var(--term-red)_10%,transparent)]',
message: 'text-(--term-red)',
},
success: {
badge:
'border-[var(--term-green)]/40 text-(--term-green) bg-[color-mix(in_oklab,var(--term-green)_10%,transparent)]',
message: 'text-(--term-fg)',
},
}

/**
* 3-character uppercase level labels.
* All entries are exactly 3 chars so the badge pill stays a consistent width
* across levels in a monospace font.
*/
const levelLabel: Record<NonNullable<TerminalLogLineProps['level']>, string> = {
debug: 'DBG',
info: 'INF',
warn: 'WRN',
error: 'ERR',
success: 'OK',
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.

The comment says all level labels are exactly 3 characters, but success is currently 'OK' (2 chars). Either pad/change the success label to 3 chars or update the comment/width calculation so the documentation matches the rendered output.

Suggested change
success: 'OK',
success: 'SUC',

Copilot uses AI. Check for mistakes.
}

/**
* A single structured log row primitive for terminal-style feeds.
*
* Renders a monospace row with an optional timestamp, a compact level badge,
* an optional source label, and the log message. All color tokens are derived
* from CSS custom properties, making the component fully theme-aware.
*
* Layout (left β†’ right):
* ```
* [timestamp] [LVL] source message
* ```
*
* @param message - Log message body (string or any React node)
* @param level - Severity (debug | info | warn | error | success); default 'info'
* @param timestamp - Optional timestamp string shown in dim foreground
* @param source - Optional subsystem / service label shown after the badge
* @param className - Additional CSS classes
*
* @example
* ```tsx
* <TerminalLogLine level="info" timestamp="10:23:45" source="server" message="Listening on :3000" />
* <TerminalLogLine level="warn" timestamp="10:23:47" source="auth" message="Token expiring in 5 min" />
* <TerminalLogLine level="error" timestamp="10:23:48" source="db" message="Connection refused" />
* <TerminalLogLine level="success" timestamp="10:23:49" source="build" message="Compiled 42 modules" />
* <TerminalLogLine level="debug" timestamp="10:23:50" source="worker" message="Spawned 4 threads" />
* ```
*/
export function TerminalLogLine({
message,
level = 'info',
timestamp,
source,
className = '',
}: TerminalLogLineProps) {
const cls = levelClasses[level]

return (
<div
className={`flex min-w-0 items-baseline gap-2 py-0.5 font-mono text-xs leading-5 ${className}`.trim()}
>
{/* Timestamp */}
{timestamp && <span className="shrink-0 tabular-nums text-(--term-fg-dim)">{timestamp}</span>}

{/* Level badge β€” fixed width keeps columns aligned in monospace feeds */}
<span
className={`shrink-0 inline-flex w-[calc(3ch+0.5rem)] justify-center rounded border py-px font-mono text-[10px] font-semibold leading-none tracking-wider ${cls.badge}`}
>
{levelLabel[level]}
</span>

{/* Source */}
{source && <span className="shrink-0 text-(--term-fg-dim)">{source}</span>}

{/* Message */}
<span className={`min-w-0 wrap-break-word ${cls.message}`}>{message}</span>
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.

wrap-break-word isn’t a standard Tailwind utility and doesn’t appear to be defined anywhere in the repo, so long messages may not wrap and can overflow. Consider using Tailwind’s break-words (and/or break-all depending on desired behavior) to ensure the message column wraps correctly.

Suggested change
<span className={`min-w-0 wrap-break-word ${cls.message}`}>{message}</span>
<span className={`min-w-0 break-words ${cls.message}`}>{message}</span>

Copilot uses AI. Check for mistakes.
</div>
)
}
Loading
Loading