Text.js 0.1
Text.js 0.1 为在 DevTools 控制台中渲染的应用带来 React 19 协调器、Yoga 布局和基于文件系统的 App Router。
Text.js 0.1 已打标签。这是首个包含全部三层的版本:@textjs/core 将帧绘制到控制台,@textjs/react 将 React 19 渲染到这些帧中,textjs 则添加 App Router 和 CLI。
App Router
路由是 app/ 中的文件夹,遵循 Next.js App Router 的约定。这是 create-text-app 的 dashboard 模板:
app/├── layout.tsx├── page.tsx├── not-found.tsx├── deployments/│ └── [id]/│ └── page.tsx└── api/ └── metrics/ └── route.ts布局包裹其下的每个页面,并在导航时保留状态。<Link> 接受 shortcut,按一个键即可跳转,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启动 Vite 并启用 React Fast Refresh。保存文件会原地更新控制台。text build输出应用及其路由处理程序的生产构建。text start提供构建产物和路由处理程序的服务。
create-text-app 根据 starter 或 dashboard 模板生成项目骨架。
检查器
在运行中的应用里按 Ctrl+` 或 F2,即可在控制台内打开 [▼] 检查器。检查器显示匹配的路由及其参数、帧尺寸、span 数量、各阶段耗时,以及脏检查门控跳过的帧数。W 将 Yoga 布局绘制为线框图。
0.1 的其他内容
@textjs/react 提供 PascalCase 命名的组件(Box、Text、ScrollBox、Input、Textarea、Select、Tabs、Code、Markdown、Diff、Modal、Spinner)、以单元格为单位的 Tailwind 风格 className 工具类和主题。布局由 WebAssembly 中的 Yoga 处理。
完整列表见更新日志。