mirror of
https://github.com/remorses/gpuix.git
synced 2026-10-29 03:04:52 +08:00
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
69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
/**
|
|
* Bundle app.tsx for the browser and serve it.
|
|
*
|
|
* The Wasm renderer uses shared memory, so every response needs the two
|
|
* cross-origin isolation headers. A production host must send the same ones.
|
|
*
|
|
* `@gpuix/native` resolves to its `browser` entry during a browser build. That
|
|
* entry imports the Wasm file, so Bun copies it next to the bundle. Nothing
|
|
* here has to know where the package lives.
|
|
*/
|
|
|
|
import { existsSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const ROOT = path.dirname(fileURLToPath(import.meta.url))
|
|
const OUT = path.join(ROOT, 'web-dist')
|
|
|
|
const bundle = await Bun.build({
|
|
entrypoints: [path.join(ROOT, 'app.tsx')],
|
|
outdir: OUT,
|
|
target: 'browser',
|
|
format: 'esm',
|
|
naming: 'app.js',
|
|
throw: false,
|
|
})
|
|
if (!bundle.success) {
|
|
for (const message of bundle.logs) console.error(message)
|
|
// Inside the GPUIX repository `packages/native/wasm/` is gitignored, so the
|
|
// browser renderer has to be built once. A copied app never hits this: the
|
|
// published `@gpuix/native` ships the Wasm files.
|
|
console.error(
|
|
'\nweb: if the Wasm renderer is missing and this is the GPUIX repository,' +
|
|
' run `bun run build:web` in packages/native'
|
|
)
|
|
process.exit(1)
|
|
}
|
|
|
|
const ISOLATION = {
|
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
|
}
|
|
|
|
function contentType(pathname: string): string {
|
|
if (pathname.endsWith('.wasm')) return 'application/wasm'
|
|
if (pathname.endsWith('.js') || pathname.endsWith('.mjs')) return 'text/javascript'
|
|
return 'text/html'
|
|
}
|
|
|
|
const server = Bun.serve({
|
|
port: Number(process.env.PORT || 4173),
|
|
fetch(request) {
|
|
const { pathname } = new URL(request.url)
|
|
if (pathname === '/favicon.ico') {
|
|
return new Response(null, { status: 204, headers: ISOLATION })
|
|
}
|
|
const file =
|
|
pathname === '/' ? path.join(ROOT, 'index.html') : path.join(OUT, pathname.slice(1))
|
|
if (!file.startsWith(ROOT) || !existsSync(file)) {
|
|
return new Response('Not found', { status: 404 })
|
|
}
|
|
return new Response(Bun.file(file), {
|
|
headers: { ...ISOLATION, 'Content-Type': contentType(pathname) },
|
|
})
|
|
},
|
|
})
|
|
|
|
console.log(`web: http://localhost:${server.port}`)
|