← 部落格

Text.js 0.1

Text.js 0.1 為在 DevTools 主控台渲染的應用程式帶來 React 19 reconciler、Yoga 版面配置與檔案系統 App Router。

Adam

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 則設定指向目前路由的連結樣式:

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>  );}

動態區段的運作方式與 Next.js 相同。[id] 會成為參數,[...slug] 會擷取路徑的其餘部分,而 [[...slug]] 也會匹配父層。靜態區段的優先順序高於動態區段,而像 (marketing) 這樣的路由群組可用來組織檔案,不會增加網址的層級。

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 與 error.tsx 分別以 Suspense 邊界和錯誤邊界包住區段。錯誤元件會收到錯誤與一個 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>  );}

路由處理常式位於 app/api,並回傳標準的 Response:

app/api/metrics/route.tsts
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,即可開啟主控台內的 [▼] 檢查器。檢查器會顯示匹配的路由及其參數、影格尺寸、span 數量與各階段耗時,以及 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 執行。

完整清單請見更新紀錄。