* chore: remove Ink-parity design docs (.agents/docs), keep the code Removes the Ink-parity loop documentation — ink-parity-loop.md, ink-parity.md, and the parity-ledger.md — and drops the now-dangling 'Current docs:' list from AGENTS.md's Context Engineering section (the convention itself is kept). All the merged parity CODE fixes remain on main; only the documentation/ledger artifacts are cleared, to re-orchestrate the documentation with a different approach. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: add focused Ink intentional-divergences design doc Replaces the removed sprawling parity docs with one focused doc that records ONLY where vue-tui deliberately differs from Ink (API surface, additive features, unavoidable Vue-vs-React semantics, N/A React concepts, framework idioms) — leaving placeholders for the maintainer to supplement. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
5.8 KiB
vue-tui — Intentional Divergences from Ink
vue-tui started as a Vue 3 port of Ink, and it still tracks Ink closely — the aim is behavioral parity except where a difference is deliberate. But it is no longer just a port: it has grown its own design decisions, additive features, and Vue-native choices. This document records the places vue-tui intentionally differs from Ink — by design, not as a gap to fix. A difference that is not listed here is treated as a bug (or simply unverified), not a design choice.
Reference baseline: Ink v7.0.4 (commit
40b3a7578811fd616341ca4e31cc7748aeeff12f). When bumping the target Ink version, re-validate every entry below against the new source.
How to read this
Each entry states what Ink does, what vue-tui does, and why the divergence is deliberate. Divergences fall into a few kinds:
- API surface — public API renamed/reshaped to fit Vue idioms.
- Additive — vue-tui supports something Ink doesn't (a strict superset).
- Framework semantics — a consequence of Vue ≠ React that cannot be papered over.
- N/A — a React-only concept with no Vue equivalent.
Public API surface
Entry point — createApp() instead of render()
- Ink:
render(<App/>). - vue-tui:
createApp(App).mount(options). - Why: mirrors Vue's own
createAppmental model — a Vue developer expects an app object they mount, not a one-shot render call.
App composable — useExit() instead of useApp()
- Ink:
useApp()returns the full AppContext (exit,waitUntilRenderFlush, stdin/stdout/stderr, …). - vue-tui:
useExit()returns only theexitfunction; the rest is reached through dedicated composables (useStdin,useStdout,useStderr, …). - Why: intentionally minimal, single-purpose composables.
waitUntilRenderFlushis deliberately not exposed.
Exported text-measurement helpers
- Ink: does not export its internal
measure-textmodule. - vue-tui: exports
measureText/measureTextNaturalfrom the public index. - Why: a deliberately public utility surface for consumers who need to size text.
No named type / prop re-exports
- Ink: re-exports
BoxProps,TextProps,StaticProps,TransformProps,NewlineProps,WindowSize,CursorPosition,DOMElement,RenderOptions,Instance,App/Stdin/Stdout/StderrProps. - vue-tui: does not re-export those names; exposes its own type surface
(
TuiApp,MountOptions, …). - Why: avoid leaking React-shaped type names; present a Vue-native type surface.
Additive features (vue-tui is a strict superset)
Accessibility props — AriaRole / AriaState
- Ink: no equivalent.
- vue-tui:
<Box>/<Text>acceptAriaRole/AriaStateprops that feed the screen-reader linearization. - Why: additive accessibility surface; does not change visual (non-SR) rendering.
Multiple <Static> regions
- Ink: keeps a single
staticNode; only one<Static>is honored. - vue-tui:
findStatics(root)renders every<Static>in the tree. - Why: strictly more capable — a tree with two
<Static>regions both render. Maintainer decision (2026-05-30): KEEP.
Ctrl+C exits under the kitty protocol too
- Ink: wires
exitOnCtrlConly to the legacy\x03path, so a kitty-protocol Ctrl+C (\x1b[99;5u) does not exit. - vue-tui:
useInputexits oninput === 'c' && key.ctrl, gated onexitOnCtrlC(defaulttrue), so Ctrl+C exits under both legacy and kitty protocols. - Why:
exitOnCtrlCreliably exiting under all protocols is the intended behavior; opt out withexitOnCtrlC: false. Maintainer decision (2026-05-30): KEEP.
Framework-semantic divergences (Vue ≠ React)
Removing flexDirection / flexWrap resets to the default
- Ink:
applyFlexStyleshas noundefinedbranch forflexDirection/flexWrap, so an explicitflexDirection={undefined}leaves the yoga node's stale value in place. (For an omitted prop, Ink's<Box>re-applies itsrow/nowrapdefault before the style spread.) - vue-tui: resets
flexDirection/flexWrapto the Box default (row/nowrap) when the prop is removed across renders. - Why: Vue cannot distinguish an omitted prop from an explicit
undefined— both collapse to the prop's default — so vue-tui must pick one behavior. It matches Ink's common case (omitted →row/nowrap) and the Vue-idiomatic expectation (drop the override → get the default). The residual difference (explicit={undefined}→ vue-tui resets, Ink keeps stale) is an unavoidable Vue-vs-React semantic. Maintainer decision (2026-05-30): KEEP.
Other Vue-vs-React semantics (placeholder)
- (maintainer: candidates to document —
v-if/nullrendering as comment host nodes; reactivity-driven re-render timing vs React's render cycle; keyed reconciliation order. Add the ones that are genuinely by-design.)
Not applicable in Vue
React concurrent mode
- Ink: built on React; Suspense /
useTransitionare React features. - vue-tui: no equivalent — N/A, not a gap.
Framework idioms (noted, not behavioral divergences)
Surface conventions, listed so they aren't mistaken for gaps:
- Vue composables (
useFocus,useInput, …) instead of React hooks. <script setup>SFCs /defineComponentinstead of function components.- kebab-case filenames;
.tsover.tsxwhere there's no JSX. shallowRefby default for reactive state.
Maintainer additions
Space for divergences to add or refine. For each, capture: what Ink does, what vue-tui does, and why it's deliberate (the trade-off, not just the what).