mirror of
https://github.com/anomalyco/opentui.git
synced 2026-09-19 01:26:03 +08:00
4.3 KiB
4.3 KiB
OpenTUI Agent Guide
Engineering
- Reuse existing seams; do not duplicate policy, ownership, or state across TypeScript, Zig, and framework layers.
- For bug fixes, first add a focused regression test of observable behavior or invariants. If automation is impossible, record the evidence and remaining manual verification; do not substitute contract sketches or guesses.
- Make native ownership explicit; clean up handles, callbacks, buffers, and listeners on every exit path. Bound input-driven work and test lifecycle failures.
- Do not interchange byte lengths, code points, graphemes, and terminal display-cell widths.
oxfmtis the formatting source of truth (semi: false,printWidth: 120); avoid unrelated formatting churn.
Tooling And Runtimes
- Use Bun for dependency management and development commands:
bun install,bun run <script>,bun test, andbun <file>. - Run package scripts from the package directory unless the script is defined at the repository root.
- Shared runtime code must preserve the supported Bun and Node paths. Keep runtime-specific behavior behind existing
platform/runtime/build seams; do not introduce Bun-only APIs into shared modules. Node checks use the minimum
version enforced by
scripts/node26.mjs.
Verification
- Run the narrowest relevant test first, then the affected package suite.
- Ordinary TypeScript source changes do not require the root build. Use the affected package's
test,typecheck,build, or validation scripts as applicable. - Run
bun run buildfrom the repository root after native or cross-package build/output changes, or when tests report a missing/stale native artifact. It does not build web or examples; use their package scripts. - For native changes, run
bun run test:nativefrompackages/core. Filter withbun run test:native -Dtest-filter="test name"while iterating. - For runtime, FFI, build, or export changes, run the relevant Node and packed-distribution scripts from that package
(such as
test:js:nodeortest:dist) when present. - Use root
bun run fmt:checkandbun run lintfor final static checks when relevant.
Prefer tests and TestRenderer for automated debugging. For interactive behavior, load the terminal-control skill and
use its terminal tool to run, drive, and inspect the app; repository examples bind backtick to
renderer.console.toggle() for captured console.log output. Ask for a user-run reproduction only when the required
terminal or platform is unavailable locally.
Portable FFI
- Portable symbol signatures must stay within the
node:ffi/bun:ffiintersection. Use explicit widths such asu32/u64, not backend-only ABI names such asusize,napi_env, ornapi_value; representi64/u64asbigint, native booleans as0/1, and shared pointers asnumber | bigint. - Default to
bufferfor transient, non-nullTypedArrayparameters and pass the view directly. Do not callptr(). - For a raw
ArrayBuffer, create oneUint8Arrayview over it, keep that view, and declarebuffer. Abuffercall costs less than aptrcall.bufferrejects an argument that is not a view with aTypeError.ptrsends a bad argument to native code as an address. - Use
ptronly when a parameter can be null, accepts a numeric native address, or is a callback. Pass transient owner objects directly toptrparameters. Do not pre-resolve them. - Bun 1.3 does not accept
DataViewdirectly forbufferorptrparameters. Pass an equivalent typed array such asnew Uint8Array(view.buffer, view.byteOffset, view.byteLength). - On Bun 1.4+, use
bufferwhen you pass aDataViewdirectly to FFI. - Use
ptr(view)only when native code stores the address beyond the call. Before resolving it, accessview.bufferto move any inline typed-array storage into a stableArrayBuffer, then keep the view alive for the complete native lifetime. The order is required:const owner = view.buffer; const address = ptr(view). Callingptr(view)first and accessingview.bufferlater can move the storage and invalidateaddress. - Model C-string inputs as pointer parameters and pass owned, NUL-terminated byte buffers directly; string returns are
not portable. Create callbacks through the loaded library/platform facade, not
new JSCallback(...), and assume only same-thread callbacks.