Rewrites all four ASCII tool components to share the same design language and spatial structure as the Calculator & Grapher tool. Layout - New responsive 2/5–3/5 grid (was fixed 2+1 col); matches Calculate - Left panel: text input card + font selector filling remaining height - Right panel: preview as the dominant full-height element - Mobile: tabbed Editor / Preview switcher (same pattern as Calculator) TextInput - Replace shadcn Textarea with native <textarea> - Glass border pattern (border-border/40, focus:border-primary/50) - Monospace font, consistent counter styling FontSelector - Replace Card + shadcn Tabs + Button + Input + Empty with native elements - Glass panel (glass rounded-xl) matching Calculate panel style - Custom tab strip mirrors Calculator mobile tab pattern - Native search input with glass border - Font list items: border-l-2 left accent for selected state, hover:bg-primary/8, rose heart for favorites - Auto-scrolls selected item into view on external changes - Simplified empty state to single italic line FontPreview - Replace Card + Button + Badge + ToggleGroup + Tooltip + Empty - Glass panel with header row (label + font tag + action buttons) - Controls row: native toggle buttons with primary/10 active state - Terminal window: dark #06060e background, macOS-style chrome (rose/amber/emerald dots), font name watermark — the hero element - PNG export captures entire terminal including chrome at 2x - Inline skeleton loader with pulse animation replaces Skeleton import Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
32 lines
1.0 KiB
TypeScript
32 lines
1.0 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export interface TextInputProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
placeholder?: string;
|
|
className?: string;
|
|
}
|
|
|
|
export function TextInput({ value, onChange, placeholder, className }: TextInputProps) {
|
|
return (
|
|
<div className={cn('relative', className)}>
|
|
<textarea
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={placeholder || 'Type something…'}
|
|
rows={4}
|
|
maxLength={100}
|
|
className="w-full bg-transparent resize-none font-mono text-sm outline-none text-foreground placeholder:text-muted-foreground/35 border border-border/40 rounded-lg px-3 py-2.5 focus:border-primary/50 transition-colors"
|
|
spellCheck={false}
|
|
autoComplete="off"
|
|
/>
|
|
<div className="absolute bottom-3 right-3 text-[10px] text-muted-foreground/35 font-mono pointer-events-none tabular-nums">
|
|
{value.length}/100
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|