feat(runtime): re-export Ink-aligned named prop/data types (BoxProps, …) (#70)

vue-tui withheld Ink's named prop types (BoxProps, TextProps, StaticProps,
TransformProps, NewlineProps) and the WindowSize/CursorPosition data shapes
under a blanket "avoid React-shaped type names" rule. That rule over-reached:
a <Box> has props in Vue exactly as in React, so those names carry no
React-vs-Vue content — there's no reason to rename them. Re-export them under
Ink's names so a consumer can name a component's props the same way as in Ink.

- Derive each XProps from the component's runtime `props` object via Vue's
  `ExtractPublicPropTypes`, so the public type can never drift from the real
  props. Pin `required: true as const` on Static.items / Transform.transform:
  a standalone `const` widens `true`→`boolean`, which would otherwise drop them
  from the required keys — in both the exported type AND the component's own
  `setup(props)` typing.
- Add `WindowSize { columns, rows }` and `CursorPosition { x, y }`, anchored to
  their real usage in useTerminalSize / useCursor. (The composables still return
  reactive refs of these — the data shape matches Ink; the ref wrapper is the
  framework difference.)
- Keep the genuinely-divergent names as-is: DOMElement→TuiNode (real
  DOM-emulation vs host-node difference), RenderOptions/Instance→MountOptions/
  TuiApp (downstream of createApp()), App/Stdin/Stdout/StderrProps = N/A. Rewrite
  the ink-divergences doc to record what's now aligned vs still divergent.
- Add a tsc-checked type-level test (public-types.test-d.ts) asserting the
  exported shapes; it is excluded from vitest's runtime run by naming.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-30 19:18:19 +08:00
committed by GitHub
parent 688da13872
commit 6edc51b925
10 changed files with 267 additions and 130 deletions
@@ -0,0 +1,38 @@
// Type-level guarantees for the public *named* type surface.
//
// vue-tui tracks Ink, and Ink re-exports its component prop types and a couple of
// framework-neutral data shapes under stable names. These names (BoxProps, TextProps,
// …, WindowSize, CursorPosition) have nothing to do with React vs Vue — a <Box> has
// props in Vue exactly as in React — so vue-tui re-exports them too, letting consumers
// name a component's props the same way they would in Ink. See
// `.agents/docs/ink-divergences.md` ("Named type / prop re-exports").
//
// These assertions are erased at runtime; the real gate is `tsc --noEmit` (the package's
// `check:type` script). This file is named `*.test-d.ts` on purpose so vitest does NOT
// pick it up as a runtime test (its include is `*.test.ts`), while tsc still checks it.
import { expectTypeOf } from "vite-plus/test";
import type {
BoxProps,
TextProps,
StaticProps,
TransformProps,
NewlineProps,
WindowSize,
CursorPosition,
} from "@vue-tui/runtime";
// Prop types carry their component's real, declared props.
expectTypeOf<BoxProps["flexDirection"]>().toEqualTypeOf<
"row" | "row-reverse" | "column" | "column-reverse" | undefined
>();
expectTypeOf<BoxProps["gap"]>().toEqualTypeOf<number | undefined>();
expectTypeOf<TextProps["bold"]>().toEqualTypeOf<boolean | undefined>();
expectTypeOf<StaticProps["items"]>().toEqualTypeOf<unknown[]>();
expectTypeOf<TransformProps["transform"]>().toEqualTypeOf<
(line: string, lineIndex: number) => string
>();
expectTypeOf<NewlineProps["count"]>().toEqualTypeOf<number | undefined>();
// Framework-neutral data shapes, mirrored from Ink exactly.
expectTypeOf<WindowSize>().toEqualTypeOf<{ readonly columns: number; readonly rows: number }>();
expectTypeOf<CursorPosition>().toEqualTypeOf<{ x: number; y: number }>();