터미널에서 실행
text run으로 같은 대시보드를 터미널에서 전체 화면으로 실행하세요.
이 장의 내용
- `text run`
- `q`로 종료
- 나만의 CLI
- 차이점
text run은 같은 앱을 터미널에서 실행해요. 레이아웃, 컴포넌트, 키맵, 라우트 핸들러를 공유하고, 출력에는 %c CSS 스팬 대신 트루컬러 ANSI를 사용해요.
text run
스크립트를 추가하고 실행하세요:
"scripts": { "dev": "text dev", "build": "text build",삭제: "start": "text start"추가: "start": "text start",추가: "tui": "text run" },bun run tui # render ./app at /bun run tui -- --path /deployments # start on another routetext run은 app/의 모든 파일을 가져오고, 메모리 내 히스토리로 App Router를 마운트해 전체 화면에 그려요. <Link>, useRouter(), notFound(), redirect()는 그대로 작동하고, 동일 출처의 fetch("/api/…") 호출은 같은 프로세스에서 라우트 핸들러를 실행해요. Ctrl+C는 앱을 종료하고 스크롤 기록을 복원해요. Bun이 필요해요.
q로 종료
터미널 사용자는 q로 종료하기를 기대해요. useApp().exit()는 앱을 언마운트하고 터미널을 복원해요. 브라우저에서는 콘솔 앱을 언마운트해요.
추가: import { useApp } from "@textjs/app";export default function RootLayout({ children }: { children?: ReactNode }) {추가: const app = useApp(); useInput((e) => { if (e.key === "backspace") router.back();추가: if (e.input === "q") app.exit(); });나만의 CLI
단일 컴포넌트에는 renderToTerminal을 사용하세요. App Router 없이 요소 하나를 마운트하고, fetch("/api/…")는 text run에서만 라우트 핸들러에 도달하므로 컴포넌트에 데이터를 전달하세요:
import { renderToTerminal } from "@textjs/app/terminal";import { Table } from "@textjs/app";import { DEPLOYMENTS } from "./lib/deployments";const columns = [{ key: "commit", header: "Commit" }, { key: "project", header: "Project", width: 10 }];const app = await renderToTerminal(<Table columns={columns} rows={DEPLOYMENTS} autoFocus />);await app.waitUntilExit();bun cli.tsx로 실행하세요. DeploymentsPage는 행 데이터를 가져오므로 text run이 필요해요.
차이점
터미널은 더 빠르게 다시 그리며(약 15fps 대신 최대 60fps), 도킹된 콘솔 대신 창 크기에 맞춰 그리드 크기를 정해요. 이미지에는 URL 대신 픽셀 데이터가 필요해요. 이 대시보드를 포함한 나머지는 모두 같은 코드를 사용해요.
사용해 보기
이 장을 마쳤을 때의 Acme Deploys 앱이 DevTools에서 실행 중이에요. 패드를 클릭한 뒤 아래 키를 사용하세요.
콘솔 열기
앱은 DevTools에서 렌더링돼요.먼저 Safari 설정 → 고급에서 ‘웹 개발자용 기능 보기’를 켜세요. 그다음 여기를 클릭하고 Enter를 누르세요.
데스크톱에서 열기
이 앱은 DevTools 콘솔에서 렌더링되므로 데스크톱 브라우저가 필요해요.
퀴즈
질문 2개장을 완료하려면 모든 문제에 답하세요.