{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "terminal",
  "title": "Terminal",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/terminal/Terminal.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface TerminalLine {\n    /** Prompt prefix shown before the command. Omit for output-only lines. */\n    prompt?: string\n    /** Command text shown after the prompt. */\n    command?: string\n    /** Output lines rendered below the command. */\n    output?: string\n    /** Override the line type: 'command' renders prompt+command, 'output' renders plain text. */\n    type?: 'command' | 'output'\n  }\n\n  export interface ResolvedTerminalLine {\n    prompt: string\n    command?: string\n    output?: string\n    type: 'command' | 'output'\n  }\n\n  export interface TerminalProps extends HTMLAttributes<HTMLDivElement> {\n    /** Command history to render. */\n    lines: TerminalLine[]\n    /** Window title shown in the title bar. Default 'bash'. */\n    title?: string\n    /** Prompt character. Default '$'. */\n    promptChar?: string\n    /** Color theme. Default 'dark'. */\n    theme?: 'dark' | 'light'\n    /** Auto-scroll to bottom when new lines arrive. Default true. */\n    autoScroll?: boolean\n    /** Animate lines typing in one-by-one. Default false. */\n    typing?: boolean\n    /** Typing speed in ms per line. Default 120. */\n    typingSpeed?: number\n    /** Max height before scrolling. Default '400px'. */\n    maxHeight?: string\n    /** Custom line renderer — the Svelte counterpart of the Vue `line` slot. */\n    line?: Snippet<[{ line: ResolvedTerminalLine; index: number }]>\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { tick } from 'svelte'\n  import { cn } from '$lib/utils'\n\n  let {\n    lines,\n    title = 'bash',\n    promptChar = '$',\n    theme = 'dark',\n    autoScroll = true,\n    typing = false,\n    typingSpeed = 120,\n    maxHeight = '400px',\n    class: className,\n    line,\n    ref = $bindable(null),\n    ...restProps\n  }: TerminalProps = $props()\n\n  let bodyEl: HTMLDivElement | null = $state(null)\n  // Settled by the effect below on mount and whenever lines change.\n  let visibleCount = $state(0)\n\n  const resolvedLines: ResolvedTerminalLine[] = $derived(\n    lines.map((l) => ({\n      ...l,\n      type: l.type ?? (l.prompt || l.command ? 'command' : 'output'),\n      prompt: l.prompt ?? (l.type === 'output' ? '' : promptChar),\n    })),\n  )\n\n  const shownLines = $derived(resolvedLines.slice(0, visibleCount))\n\n  function scrollToBottom() {\n    if (!autoScroll || !bodyEl) return\n    tick().then(() => {\n      if (bodyEl) bodyEl.scrollTop = bodyEl.scrollHeight\n    })\n  }\n\n  $effect(() => {\n    const len = lines.length\n    if (typing) {\n      // Reset typing animation when lines change.\n      visibleCount = 0\n      let cancelled = false\n      let timer: ReturnType<typeof setTimeout>\n      const tickFn = () => {\n        if (cancelled) return\n        if (visibleCount < len) {\n          visibleCount++\n          scrollToBottom()\n          timer = setTimeout(tickFn, typingSpeed)\n        }\n      }\n      timer = setTimeout(tickFn, typingSpeed)\n      return () => {\n        cancelled = true\n        clearTimeout(timer)\n      }\n    } else {\n      visibleCount = len\n      scrollToBottom()\n    }\n  })\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"terminal\"\n  data-theme={theme}\n  class={cn(\n    'relative overflow-hidden rounded-lg border font-mono text-sm shadow-sm',\n    theme === 'dark' ? 'bg-card text-card-foreground border-border' : 'bg-muted border-border',\n    className,\n  )}\n  {...restProps}\n>\n  <!-- Title bar -->\n  <div class={cn('flex items-center gap-2 border-b px-4 py-2.5', 'border-border bg-muted')}>\n    <div class=\"flex gap-1.5\">\n      <span class=\"bg-destructive size-3 rounded-full\"></span>\n      <span class=\"bg-warning size-3 rounded-full\"></span>\n      <span class=\"bg-success size-3 rounded-full\"></span>\n    </div>\n    <span class={cn('text-muted-foreground ml-2 text-xs')}>{title}</span>\n  </div>\n\n  <!-- Body -->\n  <div bind:this={bodyEl} class=\"overflow-auto p-4 leading-relaxed\" style:max-height={maxHeight}>\n    {#each shownLines as resolvedLine, i (i)}\n      <div data-slot=\"terminal-line\" class=\"break-words whitespace-pre-wrap\">\n        {#if line}\n          {@render line({ line: resolvedLine, index: i })}\n        {:else}\n          {#if resolvedLine.type === 'command'}\n            <div data-slot=\"terminal-command\" class=\"flex flex-wrap items-baseline gap-x-1.5\">\n              <span class={cn('text-success shrink-0 font-semibold')}>{resolvedLine.prompt}</span>\n              <span>{resolvedLine.command}</span>\n            </div>\n          {/if}\n          {#if resolvedLine.output}\n            <div data-slot=\"terminal-output\" class=\"text-muted-foreground\">{resolvedLine.output}</div>\n          {/if}\n        {/if}\n      </div>\n    {/each}\n  </div>\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/terminal/Terminal.svelte"
    },
    {
      "path": "packages/registry-svelte/components/terminal/index.ts",
      "content": "export { default as Terminal, type TerminalLine, type ResolvedTerminalLine, type TerminalProps } from './Terminal.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/terminal/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Terminal/command-line display with a macOS-style title bar, command history, prompt character, dark/light themes, auto-scroll, optional typing animation, and a line snippet for custom formatting.",
  "categories": [
    "display"
  ]
}