Skip to content

Write the frontend as TypeScript LitElements - #468

Merged
panarch merged 20 commits into
mainfrom
typescript-lit-element
Oct 27, 2026
Merged

panarch merged 20 commits into
mainfrom
typescript-lit-element

Conversation

@panarch

@panarch panarch commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Every frontend module is now TypeScript and every component a LitElement. Nothing is meant to look or behave differently; what changes is how the frontend renders, builds, is served, and is tested.

  • Components render through Lit's reactive update. DOM work that reads the rendered result runs after the update, and Action Hints and keyboard contexts are declared in templates and read from what Lit last drew.
  • cargo build compiles the frontend with tsc (strict, noImplicitOverride, noImplicitReturns), the app and the service worker separately, and stops on a type error. Building needs Node.js 24 or newer and npm ci in frontend/, which installs lit, tslib, and typescript; the Rust checks and the release workflow run npm ci --omit=dev before Cargo.
  • The server serves each module at its .js path with a .js.map source map that carries the TypeScript source; .ts URLs are not served. The shell starts from /assets/app.js. The service worker is compiled from service-worker.ts and keeps its URL.
  • Lit, tslib, and Lit's decorator modules are served through the import map and precached. Components declare reactive properties and registration with standard decorators, whose downleveled form adds about 0.4 MB to the uncompressed modules.
  • Each custom event that carries a detail declares its detail type and DOM event map entry once, in the module a counted ownership rule picks, and a contract test checks the rule. The workspace tab intent sends a mode from the navigation's mode list instead of the raw dataset string.
  • npm run typecheck and npm run lint (Biome) are new and run in Frontend Tests. npm run test:unit runs the unit tests from tsc's output.
  • The frontend architecture and review documents describe the TypeScript module layout, Lit's update model, and the event ownership rule.

The frontend can hold TypeScript modules: cargo build strips their types with
Node.js and the server serves the result at the source path, beside Lit
embedded from frontend/node_modules. The pagination and its page buttons are
the first LitElement components, and a reattached pagination no longer reports
one click twice. CI checks types with tsc, formats and lints the .ts files with
Biome, and runs on Node.js 24.
The app update, update result, Codex runtime restart and update, Codex reset
credit, Claude runtime restart, and archived Task delete dialogs render through
Lit. Opening and closing wait for the dialog's first render, so an owner can
call them right after mounting it. Contracts that scanned only .js modules now
scan the .ts ones too.
Every module in frontend/components/ renders through Lit, with file-status
and chunked-fetch, which they use whole; other modules they call stay
JavaScript behind a same-stem .d.ts. A component renders on the spot when it
connects and when a public method changes it, since its JavaScript owners
read its DOM right after.
Task Detail's Review, Git, and GitHub surfaces are TypeScript LitElements
that hand files, diffs, and snapshots to their children as template
properties. The shared viewers stop rendering in place on connect; only
the three Detail layouts still flush for their JavaScript owner.
The Settings and Notes workspaces, their navigators and pages, and the
shared workspace brand are TypeScript LitElements. The two workspaces
still draw in place for the JavaScript Task Workspace that owns them.
The Task conversation, its entry model, and the components its entries
mount are TypeScript LitElements. The list draws in place, entries
included, for the JavaScript Task layout and the keyboard modes that read
it right after a change.
The Composer with its attachments, context usage, voice input, and turn
options, and New Task's Task Create and directory field, are TypeScript
LitElements that draw in place for their JavaScript owners. Model menu
options now survive a redraw, so an open Action Hint session keeps its keys.
The Task navigator, the Active list with its Sections and rows, the
Archived list, and the Task switcher are TypeScript LitElements. The
status chip is now one component every Task surface uses, and it registers
its spinner once.
The Detail layout, the Task screen with its session and history, the
Section screen with its shortcuts and fork dialog, the summary with Task
Info, the Git and GitHub menus, and the terminal are TypeScript
LitElements. The Task screen's conversation pane stays once drawn, and
what stands in its place draws beside it.
The App Shell, the workspace with its tab buttons and update Task dialog,
the Tasks layout, New Task, the recovery screens, the image preview, and
the build mismatch alert are TypeScript LitElements. Workspace methods
that nothing called, or that called a missing method, are gone.
Settings and appearance, the API, routes and navigation history, the Tasks
model and stream, Codex and Task-store status, live updates and watch,
foreground recovery, and the PWA update lifecycle are TypeScript. Their
hand-written declarations are gone, and server response types live in the
API module.
Each component declares its Action Hint targets, Scroll surfaces, and
keyboard contexts on the elements its template draws, and reaches its own
elements through drawn refs instead of selectors. The keyboard navigation
controller and its badge, Scroll, and shortcut help parts are TypeScript.
Action Hint, Scroll, and keyboard-context declarations are read as the
last draw left them instead of forcing each owner to draw first. Opening
the Task switcher, continuing into a popover, and revalidating after a
DOM change wait one frame, by which Lit has drawn the change.
The shell's entry module is TypeScript like the modules it loads. The
string icon and HTML escaping helpers nothing used are gone, and the
icon fixture contract now reads the inlineIcon calls production makes.
Components no longer draw when they connect, and those that only render
their state keep it in reactive properties instead of drawing by hand.
Immediate draws stay where the same call uses what it drew and in the
routed layouts that hand their children a route right after drawing.
Node's type stripping limited the frontend to `.ts` import paths and erasable
syntax, behind a separate script. tsc now compiles each module, the service
worker included, to the `.js` path it is served at with a source map beside it,
stops the server build on a type error, and writes the build unit tests run
from. Keyboard navigation imports Action Hints through its public entry again,
which the boundary contract checks now that imports end in `.js`.
A reactive field was spread over `static properties`, a `declare` line, and a
constructor assignment. Components now declare it as a decorated `accessor`
field and register with `@customElement`, lifecycle members carry `override`,
and each tag has an `HTMLElementTagNameMap` entry. tsc imports the decorator
helpers from `tslib`, which the server serves beside Lit's decorator modules.
Listeners restated each custom event's detail in a cast or parameter, so a
sender could change it unseen. Each event that carries a detail now declares its
type and DOM event map entry once, in the module a counted rule picks, with an
-event module for the four that several modules both send and hear. A contract
test checks the rule, and listeners no longer cast.
tsc now reports a function that returns a value on some paths and falls off
the end on others. The App Shell's bootstrap was the only one, and both of its
callers ignore the result, so it no longer returns it.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://typescript-lit-element-caffold.taehoon-moon.workers.dev (commit 601cc17)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://71911d78-caffold.taehoon-moon.workers.dev 601cc17 2026-10-27T08:00:20.698Z Visit the dashboard ↗

@panarch panarch self-assigned this Oct 27, 2026
@panarch
panarch merged commit ee15b13 into main Oct 27, 2026
10 checks passed
@panarch
panarch deleted the typescript-lit-element branch October 27, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant