8aea881ffa
* feat(components): scaffold @vue-tui/components with spinner preset data New private package (0.0.0) for high-level components composed from runtime primitives. Ships the dots/line preset data + a pure resolveSpinner() with edge-guards (empty frames / unknown type → dots; interval threads both modes), fully unit-tested incl. a width-safety guard (string-width === 1 per frame). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(components): add the Spinner component Spinner is a <Text> + useAnimation pure composition: `type` selects an inline preset (dots/line), `frames`/`interval` is the escape hatch, and it always animates (no interactivity gate — there is no public signal; matches Ink). Renders a visible glyph non-interactively. Typed props via ExtractPublicPropTypes. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(components): Spinner color + label `color` tints the glyph only (label stays default, matching ora/@inkjs/ui); `label` renders after the glyph with a separating space (interpolated so Vue whitespace-condense keeps it). Two <Text> spans share an outer <Text> context so they render inline on one line rather than stacking vertically. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(components): add @vue-tui/components to the CI task graph Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): record Spinner decisions Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): refresh package status and clarify ink-spinner parity note The design-principles status blockquote said the package was 'planned' with no code yet; this branch ships Spinner, so mark it active. Also reword the spinner Behavior note to name the third-party ink-spinner explicitly and soften it to an unverified, un-run-checked observation. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): list @vue-tui/components + <Spinner> Add the new package to the hero line + Packages table, and <Spinner> to the Components table. Marked "New; early". Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): give @vue-tui/components its own section Move <Spinner> out of the runtime Components table into a separate "High-level Components" section so the package's API surface stays decoupled from the runtime primitives. Add a ToC entry. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(readme): drop the "New; early" status tag for @vue-tui/components Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
44 lines
2.5 KiB
Markdown
44 lines
2.5 KiB
Markdown
# Spinner — decision record
|
|
|
|
> Decisions specific to `@vue-tui/components`' `Spinner`. Shared conventions live in
|
|
> [components-design-principles.md](../components-design-principles.md). Tracking: #218.
|
|
|
|
A pure composition of `<Text>` + `useAnimation` — no new runtime export needed.
|
|
|
|
## Style selection & the escape hatch
|
|
|
|
- `type` selects an **inlined preset**; only **`dots`** (default) and **`line`** ship.
|
|
- **Inclusion bar for a preset:** universal default + functional fallback only; every frame
|
|
must be **width-safe (exactly 1 column, verified with `string-width`)**; non-novelty. `dots`
|
|
is the universal default; `line` is the only pure-ASCII fallback (braille needs a braille font).
|
|
Everything else — including the full `cli-spinners` set — is reachable via the escape hatch.
|
|
- **Escape hatch:** `frames: string[]` + `interval?: number` override `type`. A `cli-spinners`
|
|
entry (`{ interval, frames }`) can be spread in verbatim. Empty `frames` and an unknown `type`
|
|
both fall back to `dots`; `interval` overrides in either mode.
|
|
- **No `cli-spinners` dependency.** This is a _set-membership_ decision under the inclusion bar,
|
|
recorded here — **not** an Ink divergence (`ink-spinner`/`cli-spinners` are third-party npm, not
|
|
Ink-core v7.0.4, which has no Spinner; there is nothing to diverge from).
|
|
|
|
## Behavior
|
|
|
|
- **Always animates.** It does NOT gate on app interactivity — there is no public signal for
|
|
`interactive` (it lives only on the internal `AppContext`), and reaching for it would break the
|
|
pure-composition rule. This mirrors the third-party `ink-spinner`, which likewise just animates
|
|
(unverified parity — not run-checked); the runtime governs
|
|
non-interactive output one layer down. (If a static-when-non-interactive affordance is ever
|
|
wanted, it needs the runtime to expose interactivity publicly first.)
|
|
- Switching `type` changes the preset interval; `useAnimation` resets `frame` to 0 on a live
|
|
interval change — acceptable for a spinner.
|
|
|
|
## API shape
|
|
|
|
- `color` tints the **glyph only**; the `label` stays default-colored (matches ora / @inkjs/ui).
|
|
- `label` is a **`string` prop** (type-friendly + the common Vue idiom for simple text). If rich
|
|
label content is ever needed, add a same-purpose **default slot** later — non-breaking.
|
|
|
|
## Non-goals
|
|
|
|
- **succeed / fail / pending** terminal states (`✔`/`✖`) are NOT Spinner's job — they belong to a
|
|
future `TaskList` / `StatusMessage` (render = f(state) in a separate component).
|
|
- **Screen-reader** handling: deferred (niche; no surveyed spinner implements it).
|