← Blog

Text.js 0.1

Text.js 0.1 apporte un réconciliateur React 19, un layout Yoga et un App Router basé sur les fichiers aux applications qui s’affichent dans la console DevTools.

Adam

Le tag Text.js 0.1 est publié. C’est la première version qui réunit les trois couches : @textjs/core dessine des frames dans la console, @textjs/react effectue le rendu de React 19 dans ces frames, et textjs ajoute un App Router et une CLI.

L’App Router

Les routes sont des dossiers dans app/, selon les conventions de l’App Router de Next.js. Voici le modèle dashboard de create-text-app :

app/├── layout.tsx├── page.tsx├── not-found.tsx├── deployments/│   └── [id]/│       └── page.tsx└── api/    └── metrics/        └── route.ts

Un layout enveloppe toutes les pages qu’il contient et conserve son état au fil de la navigation. <Link> accepte un shortcut, ce qui permet de suivre le lien avec une seule touche, et activeClassName définit le style du lien vers la route actuelle :

app/layout.tsxtsx
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>  );}

Les segments dynamiques fonctionnent comme dans Next.js. [id] devient un paramètre, [...slug] capture le reste du chemin et [[...slug]] correspond aussi au parent. Les segments statiques ont priorité sur les segments dynamiques, et les groupes de routes comme (marketing) organisent les fichiers sans rien ajouter à l’URL.

app/deployments/[id]/page.tsxtsx
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 et error.tsx enveloppent un segment dans une limite Suspense et une limite d’erreur. Le composant d’erreur reçoit l’erreur et une fonction reset :

app/deployments/error.tsxtsx
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>  );}

Les gestionnaires de route se trouvent dans app/api et renvoient une Response standard :

app/api/metrics/route.tsts
export function GET(): Response {  return Response.json({ cpu: Math.random(), memory: Math.random(), at: new Date().toISOString() });}

Le routeur synchronise window.location via history.pushState, pour que les boutons « Précédent » et « Suivant » ainsi que les liens profonds fonctionnent, et écrit metadata.title dans document.title. L’application dessine dans la console, et la page héberge une zone de focus qui recueille les saisies.

La CLI

  • text dev démarre Vite avec React Fast Refresh. L’enregistrement d’un fichier met à jour la console sur place.
  • text build génère un build de production de l’application et de ses gestionnaires de route.
  • text start sert le build et les gestionnaires de route.

create-text-app génère la structure d’un projet à partir du modèle starter ou dashboard.

L’inspecteur

Appuyez sur Ctrl+` ou F2 dans une application en cours d’exécution pour ouvrir l’inspecteur [▼] dans la console. Il affiche la route correspondante et ses paramètres, la taille de la frame, le nombre de spans et les durées des phases, ainsi que le nombre de frames ignorées par le filtre de modifications. W dessine le layout Yoga sous forme filaire.

Également dans la version 0.1

@textjs/react fournit des composants en PascalCase (Box, Text, ScrollBox, Input, Textarea, Select, Tabs, Code, Markdown, Diff, Modal, Spinner), des utilitaires className de style Tailwind qui fonctionnent en cellules, et des thèmes. Le layout repose sur Yoga en WebAssembly.

La liste complète se trouve dans le journal des modifications.