Text.js 0.1
Text.js 0.1 bringt einen React 19-Reconciler, Yoga-Layout und einen dateisystembasierten App Router für Apps, die in der DevTools-Konsole rendern.
Text.js 0.1 ist getaggt. Es ist die erste Version mit allen drei Schichten: @textjs/core zeichnet Frames in die Konsole, @textjs/react rendert React 19 in diese Frames, und textjs ergänzt einen App Router und eine CLI.
Der App Router
Routen sind Ordner in app/ und folgen den Konventionen des Next.js App Router. Das ist die dashboard-Vorlage aus create-text-app:
app/├── layout.tsx├── page.tsx├── not-found.tsx├── deployments/│ └── [id]/│ └── page.tsx└── api/ └── metrics/ └── route.tsEin Layout umschließt jede untergeordnete Seite und behält seinen Zustand beim Navigieren bei. <Link> nimmt einen shortcut entgegen, sodass ein einzelner Tastendruck dem Link folgt, und activeClassName gestaltet den Link zur aktuellen Route:
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> );}Dynamische Segmente funktionieren genauso wie in Next.js. [id] wird zu einem Parameter, [...slug] erfasst den Rest des Pfads und [[...slug]] erfasst auch das übergeordnete Segment. Statische Segmente haben Vorrang vor dynamischen, und Routengruppen wie (marketing) organisieren Dateien, ohne die URL zu erweitern.
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 und error.tsx umschließen ein Segment mit einer Suspense-Boundary und einer Error-Boundary. Die Fehlerkomponente erhält den Fehler und eine reset-Funktion:
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> );}Route-Handler liegen in app/api und geben eine standardmäßige Response zurück:
export function GET(): Response { return Response.json({ cpu: Math.random(), memory: Math.random(), at: new Date().toISOString() });}Der Router hält window.location über history.pushState synchron, sodass Zurück, Vorwärts und Deep-Links funktionieren, und schreibt metadata.title in document.title. Die App zeichnet in der Konsole, und die Seite enthält ein Fokus-Pad, das Eingaben erfasst.
Die CLI
text devstartet Vite mit React Fast Refresh. Das Speichern einer Datei aktualisiert die Konsole an Ort und Stelle.text builderstellt einen Produktions-Build der App und ihrer Route-Handler.text startstellt den Build und die Route-Handler bereit.
create-text-app erstellt ein Projektgerüst aus der Vorlage starter oder dashboard.
Der Inspektor
Drücke in einer laufenden App Ctrl+` oder F2, um den [▼]-Inspektor in der Konsole zu öffnen. Er zeigt die passende Route und ihre Parameter, die Größe des Frames, die Anzahl der Spans und die Phasenlaufzeiten sowie die Anzahl der Frames, die das Dirty Gate übersprungen hat. W zeichnet das Yoga-Layout als Drahtgittermodell.
Außerdem in 0.1
@textjs/react enthält PascalCase-Komponenten (Box, Text, ScrollBox, Input, Textarea, Select, Tabs, Code, Markdown, Diff, Modal, Spinner), className-Hilfsklassen im Tailwind-Stil, die in Zellen arbeiten, und Themes. Das Layout läuft auf Yoga in WebAssembly.
Die vollständige Liste steht im Änderungsprotokoll.