Commit Graph
3 Commits
Author SHA1 Message Date
Tommy D. Rossi 691ca1fe26 Test the todo starter through the automation client
`example-app` had a `screenshot.ts` and nothing that asserted behaviour.
`app.test.tsx` mounts `TodoApp` on the GPU test renderer and drives it
with the same locator API, in-process, with no child window:

    const { render, renderer } = createTestRoot({ width: 940, height: 660 })
    render(<TodoApp />)
    const app = await connectTest(renderer)

    await app.getByTestId('view-inbox').click()
    await app.getByTestId('row-t5').hover()
    await app.getByTestId('delete-t5').click()

Five tests: the active view paints every row, delete removes the hovered
row, the composer prepends, a completed task moves between views, and a
task added past the viewport height stays on screen.

**`app.tsx` changes**

`render()` now sits behind an entry-point check, the same one `chat.tsx`
uses, so importing the file does not open a window.

Rows carry `row-<id>`, `toggle-<id>`, `star-<id>` and `delete-<id>`. The
trash button only exists while the row is hovered, so a test must
`hover()` before it can `click()`.

**Why `getPaintedText()` and not `getAllText()`**

Both would work here, since every row is a `<text>`. `getPaintedText()`
is the one that proves the row is actually on screen, which is the whole
point of the fifth test.

Session: ses_fc16226ffffeOn4QTVIbw4K7Nz
2026-08-26 17:31:00 +02:00
Tommy D. Rossi 3d759b21c3 Drop the VirtualList wrapper, and fix the starter after review
Oracle review of 7d45d36 plus a bug the review turned up.

**Remove `VirtualList`**

The windowed wrapper is gone from `@gpuix/react`. Windowing on the React side
is application state: pass `itemCount`, `estimatedItemHeight` and `windowStart`
to the host `<virtual-list>` and render only that slice.

A generic wrapper cannot know when to widen its own window. It moved `range`
only when the native list reported a new visible range, so growing `itemCount`
without a scroll left the extra rows unmounted forever. A filter does exactly
that. The todo example hit it immediately: switching from a 2-row view to a
6-row view painted 2 rows.

    view-done    ok
    view-starred ok
    view-inbox   FAILED: "Ship it" never mounted
    view-today   ok

`VirtualList` was never published, so the pending
`.changeset/windowed-virtual-list.md` now describes the native `itemCount` plus
`windowStart` path instead. README and AGENTS.md carry a worked example and the
rule that no wrapper should come back.

**Review fixes**

`web:build` ran `bun run --cwd ../packages/native build:web`, a path that does
not exist once the folder is copied, while the README claimed nothing depended
on the repository. The script is gone. The browser renderer ships inside
`@gpuix/native`; `web.ts` explains the repository-only build in its failure
message.

`example-app/README.md` claimed `<virtual-list>` builds only visible rows. GPUI
builds only visible native rows, but React still mounts every child. Corrected.

`noImplicitAny: false` is dropped from the Quickstart tsconfig and from
`example-app/tsconfig.json`. The example passes under full `strict`, so the
starter should not hand out weaker checking.

The lockfile finding resolved itself: `examples/package.json` on main now
declares `cbor-x` and `msgpackr`, and `bun install` in a clean checkout of HEAD
produces no diff.

**Verified**

`packages/react` 302 tests pass. `example-app` typechecks under full strict,
lints clean, and all four filter views paint through the automation client.

Session: ses_fc1c928b0ffeeHIfdRpxtE0QaV
2026-08-26 16:46:11 +02:00
Tommy D. Rossi 7d45d3681a Make GPUIX installable: quickstart, todo example app, and a key that typechecks
Installing the published packages and writing an app was not possible by
following the README. Three things blocked it.

**1. `key` failed on every GPUIX element**

    error TS2322: Type '{ key: string; ... }' is not assignable to type 'Props'.
      Property 'key' does not exist on type 'Props'.

Any list built with `.map()` broke on the first `tsc` run. `key` was expected
to arrive through `JSX.IntrinsicAttributes`, but TypeScript 5 ignores that
member for intrinsic elements. React's DOM types work only because
`DetailedHTMLProps` already carries `key`.

`key` now sits on `Props`, next to `ref`, and on `VirtualListShared`, the one
element prop type that does not extend `Props`. Verified with a probe over all
12 intrinsic elements plus `motion.div`, `VirtualList`, Select, Combobox and
Tooltip.

`jsx-dev-runtime.d.ts` also re-exported `jsx` and `jsxs` from
`react/jsx-dev-runtime`, which exports only `jsxDEV`. The declarations now
match `jsx-dev-runtime.js`.

Nothing caught this because Bun never typechecks and the build tsconfig
excludes `src/__tests__`. CI now runs `bun run typecheck` in `example-app`,
whose `app.tsx` maps a keyed list inside a `<virtual-list>`.

**2. A hello world installed 254 MB**

`@gpuix/native` packed all six platform binaries into the main tarball through
a `*.node` glob, on top of the six per-platform packages that
`optionalDependencies` already resolves.

    node_modules                       254M
    |- @gpuix/native                   185M  <- all six, never loaded
    |- @gpuix/native-darwin-arm64       23M  <- the one that loads
    |- @gpuix/react                    544K

The glob is gone. `index.js` still resolves the platform package at runtime.

**3. The README never said how to install**

It opened with the architecture and the mutation protocol. There was no
`bun add` anywhere, and no mention of `jsxImportSource`, which is required:
without it TypeScript uses DOM types and `<virtual-list>`, `<markdown>`,
`<code>` and `style.hover` all fail.

A **Quickstart** now sits between the hero and Examples: install, tsconfig,
entry file, `bun --hot`, `bun build --compile`. Nothing was removed. The
Building section says up front that it is for working on GPUIX itself.

**example-app/**

A todo app to copy, in one file, using the Waku palette from `chat.tsx`.

| Script | What it does |
|---|---|
| `bun run dev` | `bun --hot app.tsx` |
| `bun run build` | standalone binary in `dist/todo` |
| `bun run web:build` | browser renderer, needs nightly Rust |
| `bun run web:dev` | browser bundle served with COOP/COEP |
| `bun run screenshot` | drives the app through the automation client |
| `bun run typecheck` | `tsc --noEmit` |

It shows `<virtual-list>`, a native `<input>`, `motion.div`, tinted `<svg>`,
native `hover` and `active`, and `testId` hooks. `scripts/screenshots.ts`
regenerates `docs/images/todo-app.png` from it.

Verified on all four paths: `bun --hot`, the 77 MB binary, the browser build at
`localhost:4188`, and a clean `typecheck`.

Two things worth remembering, both now in comments:

- the browser build embeds only IBM Plex Sans and Lilex, so `Helvetica` falls
  back to a monospace and the family has to differ per target
- `maxWidth` on a `<virtual-list>` row does nothing; the list lays each row out
  at list width, so the column cap goes one level in

`bun.lock` carries a few entries from unrelated in-flight work in `examples/`.
They are inert and prune on the next install.

Session: ses_fc1c928b0ffeeHIfdRpxtE0QaV
2026-08-26 15:55:10 +02:00