feat(core): React/Ink render support and UI components#91
Conversation
🦋 Changeset detectedLatest commit: 7c36e8e The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Move logger, spinner, and prompts off base Context into a `logger()` middleware providing a unified `ctx.log` API. Extract diagnostics into a `report()` middleware with `ctx.report`. - New `@kidd-cli/core/logger` export with `logger()` middleware - New `@kidd-cli/core/report` export with `report()` middleware - Context no longer ships logger/spinner/prompts by default - All CLI commands and examples updated to `ctx.log.*` API - Test utilities updated with `mockLog()` helper - Rename formatTally → formatSummary Co-Authored-By: Claude <noreply@anthropic.com>
…ents
Move logger, spinner, and prompts off base Context into a `logger()` middleware
that provides a unified `ctx.log` API. Extract diagnostics into a `report()`
middleware with `ctx.report`. Add `render` as an alternative to `handler` on
commands for React/Ink-based TUI support. Export Ink component wrappers from
`@kidd-cli/core/ui`.
Breaking changes:
- `ctx.logger` removed — use `logger()` middleware, access via `ctx.log`
- `ctx.spinner` removed — use `ctx.log.spinner('msg')` (creates + starts)
- `ctx.prompts` removed — prompts are flat on `ctx.log`
- `CliLogger` type removed — replaced by `Log` interface
- `TallyInput` renamed to `SummaryInput`
- `formatTally` renamed to `formatSummary`
New exports:
- `@kidd-cli/core/logger` — logger() middleware factory
- `@kidd-cli/core/report` — report() middleware factory
- `@kidd-cli/core/ui` — Ink component wrappers (Spinner, Select, etc.)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Remove middleware changeset (already on feat/middleware-refactor). Add render/UI-specific changeset. Co-Authored-By: Claude <noreply@anthropic.com>
7b64b10 to
03cb0b1
Compare
📝 WalkthroughWalkthroughThis PR introduces a screen-based TUI command pattern to Kidd CLI, enabling React/Ink components as command renderers. It adds a comprehensive UI module structure with component re-exports from Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Add missing exports: measureElement, useIsScreenReaderEnabled, kittyFlags, kittyModifiers, and all remaining prop types. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The bundler's scan-commands mirrored the runtime autoloader's extension list but was missing .tsx and .jsx. This caused kidd build/dev to skip React/Ink command files during static autoloader generation. Co-Authored-By: Claude <noreply@anthropic.com>
pnpm strict mode prevents @inkjs/ui (nested in core's devDeps) from resolving react in consuming packages. Public hoisting ensures a single shared copy is accessible across the workspace. Co-Authored-By: Claude <noreply@anthropic.com>
Introduce `screen()` as the dedicated API for building terminal UI
commands with React/Ink, replacing the previous `command({ render })`
pattern. Screens receive parsed args as component props; runtime
context (config, meta, store) is available via `useConfig()`,
`useMeta()`, and `useStore()` hooks through a `KiddProvider`.
- Add `screen.tsx` with `screen()` factory and exit mode config
- Add `provider.tsx` with `KiddProvider` and context hooks
- Remove `RenderFn`/`RenderProps` from public command types
- Add internal `ScreenRenderFn` type for runtime plumbing
- Clean up runtime types to use proper type imports
- Update kitchen-sink examples to use `screen()` from `@kidd-cli/core/ui`
Co-Authored-By: Claude <noreply@anthropic.com>
Remove direct @inkjs/ui and ink imports in kitchen-sink examples. All UI components should come through the unified @kidd-cli/core/ui surface. Co-Authored-By: Claude <noreply@anthropic.com>
The current API expects `z.object({})` or `Record<string, YargsArgDef>`
for positionals, not arrays. Update preview, show, and category
commands to use zod schemas.
Co-Authored-By: Claude <noreply@anthropic.com>
Replace stale `auth({ http, resolvers })` pattern with separate
`auth({ strategies })` and `http({ headers: createAuthHeaders() })`
middleware. Update README to match.
Co-Authored-By: Claude <noreply@anthropic.com>
Merge base branch imports (Log, Prompts, Spinner types and RuntimeOptions fields) with render branch additions (ScreenRenderFn, http middleware in auth example). Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Take main's base-context approach for log/prompts/spinner while keeping ScreenRenderFn import from this branch. Update docs and icons context to match. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@contributing/standards/typescript/components.md`:
- Around line 15-23: The fenced directory-listing code blocks in
contributing/standards/typescript/components.md lack language identifiers;
update each triple-backtick fence enclosing the directory examples (the blocks
showing commands/ with entries like deploy.ts, status.tsx, dashboard/index.tsx
and _components/StatusTable.tsx) to use a plaintext/text language tag (e.g.,
change ``` to ```text) so markdown lint rules pass and rendering is consistent;
apply the same change to all similar blocks mentioned in the review (the other
directory-listing examples in the file).
- Around line 167-179: The render function in the command example uses await
inside a synchronous function; make the render method async so you can await the
dynamic import. Specifically, change the render(props: RenderProps) function to
async, then await import('ink') when importing render, call render(<StatusView
{...props} />) to get { waitUntilExit }, and return await waitUntilExit() (or
return waitUntilExit()) so the async flow is correct for the command export
default command(...) block.
In `@examples/kitchen-sink/package.json`:
- Around line 15-21: package.json declares runtime "react": ">=18.0.0" while
devDependency uses "@types/react": "^19.0.0", causing React 19 type features
(e.g., async components) to be allowed by TypeScript but unsupported at runtime;
update the devDependency by changing "@types/react" from "^19.0.0" to "^18.0.0"
(or instead bump "react" to v19 if you intend to use React 19) so the types
match the runtime and prevent false-positive type acceptance.
In `@examples/kitchen-sink/src/commands/greet.ts`:
- Around line 19-23: Replace the if/else in greet.ts with a ts-pattern match on
ctx.args.shout: import match from 'ts-pattern', then replace the conditional
around ctx.args.shout so it calls match(ctx.args.shout).with(true, () =>
ctx.log.raw(greeting.toUpperCase())).with(false, () =>
ctx.log.raw(greeting)).run() (or use .otherwise if preferred); keep references
to greeting and ctx.log.raw exactly as used now.
In `@packages/core/src/ui/screen.tsx`:
- Around line 152-157: The two-branch typeof check in resolveValue should be
replaced with ts-pattern matching: import { match, P } from 'ts-pattern' and use
match(value).with(P.when(v => typeof v === 'function'), v => (v as () =>
T)()).otherwise(v => v as T | undefined) so the function resolves callables and
returns plain values via pattern arms; update resolveValue to use match (keeping
the Resolvable<T> typing) and remove the typeof conditional.
In `@PLAN.md`:
- Around line 606-628: The fenced code blocks containing directory listings (the
blocks beginning with "src/middleware/logger/", "src/middleware/report/",
"src/ui/ (Phase 3)", and
"contributing/standards/typescript/components.md (Phase 4)") must include a
language identifier; update each opening fence from ``` to ```text (or
```plaintext) so the listings comply with the documentation standard and render
correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: ab0d5d65-3299-4faf-b7eb-517b4d19d20b
⛔ Files ignored due to path filters (2)
.changeset/render-support-ui-components.mdis excluded by!.changeset/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yaml
📒 Files selected for processing (40)
PLAN.mdcontributing/standards/typescript/components.mdexamples/advanced/src/commands/deploy/preview.tsexamples/authenticated-service/README.mdexamples/authenticated-service/cli/src/index.tsexamples/icons/commands/category.tsexamples/icons/commands/show.tsexamples/kitchen-sink/kidd.config.tsexamples/kitchen-sink/package.jsonexamples/kitchen-sink/src/commands/dashboard.tsxexamples/kitchen-sink/src/commands/deploy.tsxexamples/kitchen-sink/src/commands/greet.tsexamples/kitchen-sink/src/index.tsexamples/kitchen-sink/tsconfig.jsonpackages/bundler/src/autoloader/scan-commands.tspackages/cli/src/commands/add/command.tspackages/cli/src/commands/add/middleware.tspackages/cli/src/commands/init.tspackages/core/package.jsonpackages/core/src/autoload.tspackages/core/src/cli.tspackages/core/src/context/types.tspackages/core/src/middleware/icons/icons.tspackages/core/src/runtime/register.tspackages/core/src/runtime/runtime.tspackages/core/src/runtime/types.tspackages/core/src/types/command.tspackages/core/src/types/index.tspackages/core/src/ui/confirm.tsxpackages/core/src/ui/index.tspackages/core/src/ui/multi-select.tsxpackages/core/src/ui/password-input.tsxpackages/core/src/ui/provider.tsxpackages/core/src/ui/screen.tsxpackages/core/src/ui/select.tsxpackages/core/src/ui/spinner.tsxpackages/core/src/ui/text-input.tsxpackages/core/tsconfig.jsonpackages/core/tsdown.config.tstsconfig.base.json
- Remove stale @kidd-cli/core/logger import from kitchen-sink (export no longer exists; log/prompts/spinner are on base context) - Rewrite components.md for screen() API (replaces old render/RenderProps docs, adds hooks/exit behavior/import conventions) - Add text lang identifiers to markdown code blocks - Use ts-pattern in greet.ts example - Bump react peer dep to >=19.0.0 to match @types/react@^19 Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@contributing/standards/typescript/components.md`:
- Around line 171-187: The example is using z.object(...) but never imports z
from 'zod', causing a ReferenceError; fix it by adding an import for z (e.g.,
import { z } from 'zod') at the top of the file alongside the existing imports
so the options: z.object(...) in the export default screen(...) call resolves
correctly; ensure the import is added before the StatusView/screen usage.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: cc30eca6-9321-4138-b7fa-8953811cb970
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yaml
📒 Files selected for processing (4)
contributing/standards/typescript/components.mdexamples/kitchen-sink/package.jsonexamples/kitchen-sink/src/commands/greet.tsexamples/kitchen-sink/src/index.ts
TypeScript 6.0.2 has peer dependency conflicts with @typescript-eslint, tsdown, and eslint-plugin-functional. Downgrade to ^5.9.3 and add explicit type annotations for isolatedDeclarations compatibility. Co-Authored-By: Claude <noreply@anthropic.com>
Add screen.test.ts covering the screen() factory (tagged output, render property, option/metadata preservation, Resolvable resolution) and render function behavior (ink integration, KiddProvider wrapping, auto/manual exit modes). Add render execution tests to runtime.test.ts covering render invocation, context prop passing, middleware bypass, and error handling. Delete stale PLAN.md. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/ui/screen.test.ts`:
- Around line 196-215: The test enables fake timers with vi.useFakeTimers() but
doesn't guarantee restoration on failure; wrap the fake timer setup and the test
body in a try/finally so vi.useRealTimers() is always called; locate the test
case using symbols vi.useFakeTimers/useRealTimers, mockedInkRender, screen and
the renderPromise variable and move the vi.useRealTimers() call into a finally
block that runs after awaiting renderPromise so timers are restored even if
assertions (e.g., expect(unmount).toHaveBeenCalledOnce()) throw.
- Around line 7-12: Update the Ink mock to use Vitest's dynamic-import form:
replace vi.mock('ink', ...) with vi.mock(import('ink'), ...) while keeping the
same mock implementation for render (returning unmount and waitUntilExit mocks).
Ensure the mock still defines render as vi.fn(() => ({ unmount: vi.fn(),
waitUntilExit: vi.fn().mockResolvedValue(undefined) })) so tests referencing
render, unmount, and waitUntilExit behave unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 53159fe7-265e-496e-af1b-f419c5485c2e
📒 Files selected for processing (2)
packages/core/src/runtime/runtime.test.tspackages/core/src/ui/screen.test.ts
Summary
Stacked on #92 (middleware refactor).
renderas an alternative tohandleron command definitions for React/Ink TUI support.tsx/.jsxto autoloader valid extensionsBox,Text,useApp,useInput, etc.) and@inkjs/uicomponent wrappers from@kidd-cli/core/uiRenderFnandRenderPropstypescommand.renderis defined, it is called instead ofhandlerdeploy.tsx) and persistent TUI (dashboard.tsx)contributing/standards/typescript/components.mdfor React/Ink conventionsTest plan
pnpm checkpasses (typecheck + lint + format)pnpm testpasses (711 core tests, all CLI tests, 66 integration tests)cd examples/kitchen-sink && pnpm dev -- deploycd examples/kitchen-sink && pnpm dev -- dashboard