Code — Terminal Session
blockmarketingTerminal transcript that types itself out line by line on mount, showing prompts, output, and a success summary, and rendering instantly under reduced-motion.
Installation
$pnpm dlx uipkge-ng@latest add code-terminal-session$npx uipkge-ng@latest add code-terminal-session$yarn dlx uipkge-ng@latest add code-terminal-session$bunx uipkge-ng@latest add code-terminal-sessionManifest:
https://uipkge.dev/r/angular/code-terminal-session.jsonInstalls to:src/app/components/blocks/code-terminal-session/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
TerminalLineinterface TerminalLine {
kind: 'prompt' | 'out' | 'ok'
text: string
}npm dependencies
Files installed (2)
src/app/components/blocks/code-terminal-session/code-terminal-session.component.ts4.6 kB
// Terminal transcript that types itself out line by line on mount, showing prompts, // output, and a success summary, and rendering instantly under reduced-motion. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, computed, Input, OnDestroy, OnInit, signal } from '@angular/core' import { LucideAngularModule, Terminal } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' export interface TerminalLine { kind: 'prompt' | 'out' | 'ok' text: string } const lines: TerminalLine[] = [ { kind: 'prompt', text: 'npx @northwind/cli connect snowflake' }, { kind: 'out', text: 'Testing credentials… ok' }, { kind: 'out', text: 'Mirroring schema… 412 tables' }, { kind: 'prompt', text: 'npx @northwind/cli certify revenue_net' }, { kind: 'out', text: 'Reconciling 4 quarters…' }, { kind: 'out', text: 'Q1 ✓ Q2 ✓ Q3 ✓ Q4 ✓' }, { kind: 'ok', text: 'revenue_net certified · 41 consumers notified' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-code-terminal-session, [ui-code-terminal-session]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent], template: ` <section data-slot="code-terminal-session" [class]="rootClass"> <div class="mx-auto max-w-3xl px-6 py-20"> <span ui-badge variant="secondary" class="gap-1.5"> <lucide-icon [img]="Terminal" class="size-3" aria-hidden="true" /> CLI </span> <h2 class="mt-4 text-2xl font-semibold tracking-tight">Connected and certified in two commands</h2> <p class="text-muted-foreground mt-2"> The CLI does everything the UI does, so the first rollout can live in a script. </p> <div ui-card class="mt-6"> <div ui-card-content class="p-0"> <div class="border-border flex items-center gap-1.5 border-b px-4 py-2.5"> <span class="bg-muted-foreground/30 size-2.5 rounded-full" aria-hidden="true"></span> <span class="bg-muted-foreground/30 size-2.5 rounded-full" aria-hidden="true"></span> <span class="bg-muted-foreground/30 size-2.5 rounded-full" aria-hidden="true"></span> <span class="text-muted-foreground ml-2 font-mono text-xs">bash — northwind</span> </div> <!-- min-h holds the final transcript height from the first paint, so the card does not grow line by line and shove the page down. --> <div class="min-h-[13.5rem] space-y-1 p-4 font-mono text-xs leading-relaxed"> @for (line of visibleLines(); track $index) { <p [class]="toneClass(line.kind)"> @if (line.kind === 'prompt') { <span class="text-primary select-none">$ </span> } {{ line.text }} </p> } </div> </div> </div> <button ui-button variant="outline" class="mt-6">Read the CLI reference</button> </div> </section> `, }) export class UiCodeTerminalSessionComponent implements OnInit, OnDestroy { protected readonly Terminal = Terminal @Input() lineDelay = 420 @Input('class') className?: string protected readonly lines = lines // Renders the whole transcript immediately when the viewer prefers reduced // motion; otherwise reveals a line at a time from mount. readonly shown = signal(0) private timer: ReturnType<typeof setInterval> | undefined readonly visibleLines = computed(() => lines.slice(0, this.shown())) ngOnInit(): void { if (typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { this.shown.set(lines.length) return } this.timer = setInterval(() => { const next = this.shown() + 1 this.shown.set(next) if (next >= lines.length) clearInterval(this.timer) }, this.lineDelay) } ngOnDestroy(): void { clearInterval(this.timer) } get rootClass(): string { return cn('bg-background', this.className) } toneClass(kind: string): string { if (kind === 'prompt') return 'text-foreground' if (kind === 'ok') return 'text-success' return 'text-muted-foreground' } }src/app/components/blocks/code-terminal-session/index.ts0.1 kB
export { UiCodeTerminalSessionComponent, type TerminalLine } from './code-terminal-session.component'
Raw manifest:https://uipkge.dev/r/angular/code-terminal-session.json