Text.js 0.1
Text.js 0.1は、DevToolsコンソールに描画するアプリに、React 19リコンサイラー、Yogaレイアウト、ファイルシステムベースのApp Routerを提供します。
Text.js 0.1のタグを付けました。3つのレイヤーがすべて揃った最初のバージョンです。@textjs/core がコンソールにフレームを描画し、@textjs/react がそのフレームにReact 19をレンダリングし、textjs がApp RouterとCLIを追加します。
App Router
ルートはNext.jsのApp Routerの規約に従う、app/ 内のフォルダーです。以下は create-text-app の dashboard テンプレートです。
app/├── layout.tsx├── page.tsx├── not-found.tsx├── deployments/│ └── [id]/│ └── page.tsx└── api/ └── metrics/ └── route.tsレイアウトは配下のすべてのページを包み、画面遷移をまたいで状態を保持します。<Link> は shortcut を受け取り、1つのキーでリンク先に移動でき、activeClassName は現在のルートへのリンクにスタイルを適用します。
import type { ReactNode } from "react";import { Box, KeyHint, Text, useTheme, type Metadata } from "textjs";import Link from "textjs/link";export const metadata: Metadata = { title: { default: "Acme", template: "%s · Acme" } };export default function RootLayout({ children }: { children?: ReactNode }): ReactNode { const theme = useTheme(); return ( <Box className="flex-col w-full h-full"> <Box className="flex-row h-1 px-1 gap-3 bg-surface shrink-0"> <Text bold color={theme.fg} wrap="nowrap"> ▼ Acme </Text> <Link href="/" shortcut="1" className="text-muted no-underline" activeClassName="text-fg bold"> 1 Metrics </Link> <Link href="/deployments/dpl_a1" shortcut="2" className="text-muted no-underline" activeClassName="text-fg bold"> 2 Latest deploy </Link> </Box> <Box className="grow flex-col px-2 pt-1">{children}</Box> <Box className="flex-row h-1 gap-2 pl-6 shrink-0 bg-surface"> <KeyHint keys="1-2" label="navigate" /> <KeyHint keys="tab" label="focus" /> <KeyHint keys="F2" label="devtools" /> </Box> </Box> );}動的セグメントはNext.jsと同じように動作します。[id] はパラメーターになり、[...slug] はパスの残りを取得し、[[...slug]] は親にも一致します。静的セグメントは動的セグメントより優先され、(marketing) のようなルートグループはURLに追加せずにファイルを整理します。
import { Box, Text, useInput } from "textjs";import { notFound, useParams, useRouter } from "textjs/navigation";import { DEPLOYMENTS } from "../../../lib/data";export default function DeploymentPage() { const router = useRouter(); const { id } = useParams<{ id: string }>(); const deployment = DEPLOYMENTS.find((d) => d.id === id); useInput((e) => { if (e.key === "escape") router.back(); }); if (!deployment) notFound(); return ( <Box className="flex-col gap-1"> <Text bold>{deployment.id}</Text> <Text>{deployment.branch}</Text> </Box> );}loading.tsx と error.tsx は、セグメントをそれぞれSuspenseバウンダリーとエラーバウンダリーで包みます。エラーコンポーネントはエラーと reset 関数を受け取ります。
import { Box, Text, useInput, type ErrorComponentProps } from "textjs";export default function DeploymentsError({ error, reset }: ErrorComponentProps) { useInput((e) => { if (e.key === "r") reset(); }); return ( <Box className="flex-col grow items-center justify-center gap-1"> <Text bold>Something went wrong loading deployments.</Text> <Text>{error.message}</Text> </Box> );}ルートハンドラーは app/api に配置し、標準の Response を返します。
export function GET(): Response { return Response.json({ cpu: Math.random(), memory: Math.random(), at: new Date().toISOString() });}ルーターは history.pushState を通じて window.location を同期するため、「戻る」、「進む」、ディープリンクが機能し、metadata.title は document.title に書き込まれます。アプリはコンソールに描画し、ページには入力を収集するフォーカスパッドを配置します。
CLI
text devはReact Fast Refreshを有効にしてViteを起動します。ファイルを保存すると、その場でコンソールが更新されます。text buildはアプリとそのルートハンドラーの本番ビルドを書き出します。text startはビルドとルートハンドラーを配信します。
create-text-app は starter または dashboard テンプレートからプロジェクトのひな形を生成します。
インスペクター
実行中のアプリで Ctrl+` または F2 を押すと、コンソール内に [▼] インスペクターが開きます。一致したルートとそのパラメーター、フレームのサイズ、スパン数、各フェーズの所要時間、dirty gateがスキップしたフレーム数を表示します。W はYogaのレイアウトをワイヤーフレームとして描画します。
0.1のその他の機能
@textjs/react には、PascalCaseのコンポーネント(Box、Text、ScrollBox、Input、Textarea、Select、Tabs、Code、Markdown、Diff、Modal、Spinner)、セル単位で機能するTailwind風の className ユーティリティー、テーマが含まれます。レイアウトはWebAssembly上のYogaで動作します。
全一覧は変更履歴にあります。