perf(packages/core,packages/cli): incremental sync engine#78
Conversation
Skip unchanged pages via mtime + frontmatter hash, parallelize page copy and public asset copy, cache OpenAPI spec parses across dev-mode syncs, skip asset generation when config hash is unchanged, and skip image re-copy when destination is current. Co-Authored-By: Claude <noreply@anthropic.com>
Document the sync pipeline, page transformation, build/dev flows, watcher triggers, and incremental sync optimizations. Co-Authored-By: Claude <noreply@anthropic.com>
Break `architecture.md` into three files: - `architecture.md` — package ecosystem, layers, design decisions - `sync-engine.md` — pipeline, page transformation, entry resolution, incremental sync, OpenAPI sync - `config.md` — config system, output structure, Rspress integration Update `cli.md` with corrected watcher details (fs.watch, not chokidar) and dev lifecycle reflecting OpenAPI cache and Rspress restart behavior. Revert CONTRIBUTING.md to its original state (architecture docs belong in contributing/, not the root file). Co-Authored-By: Claude <noreply@anthropic.com>
Replace the imperative dev command handler with an interactive React/Ink screen component that renders live watcher status, sync results, and keyboard shortcuts (r/c/o/q). - Add DevScreen component with phase-based rendering - Extract WatcherStatus, WatcherCallbacks, WatcherHandle types - Return resolved port from startDevServer for TUI display - Decouple watcher from Log interface via callbacks - Add resync() to WatcherHandle for hotkey-triggered re-sync - Cancel pending debounces on watcher close - Guard all async state updates against unmount - Wrap sync/server init in try/catch for proper error display Co-Authored-By: Claude <noreply@anthropic.com>
- Replace Object.assign with reduce for type-safe specMtimes merge - Add language specifiers to bare code fences in docs Co-Authored-By: Claude <noreply@anthropic.com>
- Use function declaration instead of arrow for guard helper (func-style) - Use Object.assign instead of spread in reduce accumulator (perf) Co-Authored-By: Claude <noreply@anthropic.com>
The contributing docs had broken links to engine/*.md and references/*.md because those directories were not included in the zpress config glob patterns. This caused the Vercel preview build to fail on link validation. Co-Authored-By: Claude <noreply@anthropic.com>
- Fix config.md: defineConfig() is pass-through, validation in loadConfig() - Fix dev.md: add `text` language identifier to lifecycle code fence - Fix openapi.md: tighten cache/config wording in steps 1-2 - Fix pipeline.md: correct .generated/ paths to .zpress/content/.generated/ - Fix pipeline.md: correct public/ copy description - Fix pipeline.md: use `title` instead of `text` in explicit items example Co-Authored-By: Claude <noreply@anthropic.com>
- Fix overview.md: tighten "get their own namespace" wording - Fix dev-screen.tsx: replace `let cancelled` with useRef-backed flag - Fix dev-screen.tsx: honor `quiet` prop instead of hard-coding true - Fix openapi.ts: use immutable spread instead of Object.assign mutation - Fix openapi.ts: evict stale cache entry on dereference failure Co-Authored-By: Claude <noreply@anthropic.com>
…isable The oxlint `no-accumulating-spread` rule flags spreading in reduce for O(n^2) performance. Object.assign mutating the accumulator is the correct pattern here — restored with a targeted lint disable comment. Co-Authored-By: Claude <noreply@anthropic.com>
The dev-screen.tsx component uses JSX but the Rslib bundler was defaulting to the classic React.createElement transform (ignoring tsconfig's jsx: react-jsx). Configure SWC's react transform to use the automatic runtime so jsx/jsxs are imported from react/jsx-runtime. Co-Authored-By: Claude <noreply@anthropic.com>
Rspack's file-based storage layer holds a transaction lock on .temp/ inside the cache directory. Rsbuild's close() resolves before that lock is fully released, causing the new dev() call to panic with "Transaction already in progress". Add a 500ms settle window after close to let the lock release before starting the new instance. Co-Authored-By: Claude <noreply@anthropic.com>
Replace themeConfig.sidebar/nav with per-directory _meta.json and root _nav.json files, enabling Rspress's built-in HMR for sidebar and navigation changes without dev server restarts. - Add filesystem-first _meta.json generation (meta.ts) that derives placement from actual file paths rather than config tree position - Add write-meta.ts to write _meta.json per directory and _nav.json - Remove old multi.ts sidebar builder and its tests - Strip sidebar/nav from themeConfig in UI config - Gate dev server restarts to config changes that actually require a rebuild (title, theme, colors, etc.) via restartRelevantHash - Suppress Rspress dev output (logLevel: silent, progressBar: false) - Keep sidebar.json/nav.json as .generated/ snapshots for debugging Co-Authored-By: Claude <noreply@anthropic.com>
Rspress creates per-directory sidebar scopes by default when only _nav.json is present. Adding a root _meta.json that lists all top-level sections as dir items creates a single unified sidebar keyed by "/" — matching the expected behavior. Also simplifies the sidebar.json snapshot to a single "/" key since standalone sections now share the unified sidebar. Co-Authored-By: Claude <noreply@anthropic.com>
… sections Rspress's _meta.json system only supports unified or per-directory sidebars, not both. This adds a custom Sidebar component that filters the unified "/" sidebar at runtime to isolate standalone sections (Packages, Contributing) into their own scope while keeping the main 5 sections as a shared sidebar. - Sync engine writes .generated/scopes.json with standalone section paths - Config loads scopes and passes via themeConfig.standaloneScopePaths - Custom Sidebar component filters unified sidebar data by current route - Reimplements Rspress's createInitialSidebar collapsed state logic - HMR preserved: _meta.json auto-discovery still runs with addDependency() Co-Authored-By: Claude <noreply@anthropic.com>
Uses the kidd-cli `fullscreen: true` screen option to render the dev TUI in the terminal's alternate screen buffer, preserving scrollback. Co-Authored-By: Claude <noreply@anthropic.com>
- Upgrade @kidd-cli/core from 0.13.0 to 0.20.0 - Replace @inkjs/ui Spinner with kidd's Spinner component - Use Alert, StatusMessage, useHotkey, useFullScreen from new API - Migrate cli() commands config to v0.20.0 shape (order → help.order) - Add dev-screen.stories.tsx with 7 story variants for visual testing Co-Authored-By: Claude <noreply@anthropic.com>
- Register `zpress stories` command using kidd StoriesScreen - Rename dev-screen.stories.tsx → dev.stories.tsx - Add `pnpm stories` script for convenience Co-Authored-By: Claude <noreply@anthropic.com>
Prevents kidd autoload from picking up story files as commands. Co-Authored-By: Claude <noreply@anthropic.com>
…dd stories - Switch all command files to `export default command()`/`screen()` - Use default imports in index.ts for cleaner command map - Remove `zpress stories` CLI command (stories are a dev tool, not user-facing) - Add `pnpm stories` script via `kidd stories` from @kidd-cli/cli - Add @kidd-cli/cli as devDependency for story viewer - Remove unused @inkjs/ui dependency Co-Authored-By: Claude <noreply@anthropic.com>
kidd stories requires a kidd.config.ts to recognize the project. Move dev.stories.tsx back to src/commands/ where the default src/**/*.stories.tsx glob can find it. Co-Authored-By: Claude <noreply@anthropic.com>
Switch CLI bundling from rslib to kidd's native tsdown-based build system. Move non-command files out of the commands directory to avoid kidd's autoload scanner picking them up. - Replace rslib.config.ts with kidd.config.ts entry/commands config - Use static command imports in index.ts (bypass autoloading) - Move dev-screen.tsx to src/screens/ (not a command) - Move dev.stories.tsx to src/stories/ (not a command) - Update bin/exports to index.mjs output format - Split dependencies for proper tsdown externalization - Upgrade @kidd-cli/core to ^0.22.1, @kidd-cli/cli to ^0.11.2 Co-Authored-By: Claude <noreply@anthropic.com>
- Replace ternary expressions with ts-pattern match in LogLine components - Destructure props.width in DevScreenPreview story - Rename clean default import to cleanCmd to avoid named export shadow - Add root `pnpm stories` script proxying to @zpress/cli Co-Authored-By: Claude <noreply@anthropic.com>
… sidebar build crash - Replace broken ASCII banner with ink-big-text + ink-gradient - Fix "q" not exiting by calling process.exit(0) after Ink exit() - Pre-create directories referenced in root _meta.json to prevent Rspress ENOENT crash on empty sections Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 47e5c2a The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 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 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR updates the CLI dev UI and watcher, and enhances the core sync engine and metadata handling. It replaces the ASCII banner with a styled Banner component, ensures quit hotkey fully exits the dev process, converts the watcher to a callback-driven API with conditional restart logic, implements incremental sync (mtime/frontmatter/hash checks, concurrent copy, OpenAPI mtime caching), adds writeMetaFiles to pre-create directories and emit 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/cli/src/lib/watcher.ts (1)
139-143: 🧹 Nitpick | 🔵 TrivialAwkward
reducepattern used to avoid expression statements.The
reducereturningnullis a workaround to satisfy the "no expression statements" rule. While technically compliant, this pattern is harder to read than a simple loop orforEach. Consider using a more idiomatic approach if the linting rules allow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/lib/watcher.ts` around lines 139 - 143, The callback passed to watch (used to create the watcher constant) uses an awkward Array.prototype.reduce returning null to avoid expression statements; replace that reduce with a clearer imperative loop (e.g., forEach or for...of) inside the (_event, filename) => { ... } handler so you can perform side-effecting operations directly and remove the null return workaround, leaving the watcher, watch, and repoRoot usages intact.
🤖 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/concepts/engine/incremental.md`:
- Line 81: Replace the British English "afterwards" with American English
"afterward" in the sentence "Empty parent directories are pruned afterwards."
within the incremental.md content so the line reads "Empty parent directories
are pruned afterward." to maintain consistent American spelling.
In `@contributing/concepts/engine/openapi.md`:
- Line 62: Replace the double hyphen in the link text "Dev Mode -- OpenAPI
Cache" with an em dash for typographic consistency; update the markdown so the
link reads "Dev Mode — OpenAPI Cache" (keep the URL ./dev.md#openapi-cache and
surrounding sentence unchanged) to improve readability.
In `@contributing/concepts/engine/overview.md`:
- Around line 27-30: Update the OpenAPI input node label to reflect that specs
are sourced from configuration rather than a fixed filename: change the SPECS
node's label (currently "openapi.yaml") to something like "config.openapi.spec"
or "OpenAPI spec files (from zpress.config.ts)" and mention/respect the single
source of truth symbol CONFIG (zpress.config.ts) and the runtime boundary
loadConfig(); ensure any text or diagram note references that specs come from
the zpress.config.ts/workspace openapi config and that outputs assume the
.zpress/content/ root and .zpress/content/.generated/ for generated metadata.
In `@packages/cli/kidd.config.ts`:
- Around line 3-6: Summary: enable TypeScript declaration emission by adding
dts: true to the Kidd config. Fix: update the exported config object passed to
defineConfig in kidd.config.ts to include dts: true alongside the existing
commands and entry keys (e.g., export default defineConfig({ commands:
'./src/commands', entry: './src/index.ts', dts: true })), so tsdown will emit
./dist/index.d.ts that matches the package's exports.types entry.
In `@packages/cli/src/lib/watcher.ts`:
- Around line 230-243: The current restartRelevantHash function uses
JSON.stringify on the relevant object which can produce nondeterministic key
ordering and cause spurious hash changes; update restartRelevantHash to produce
a deterministic serialization (e.g., implement a stable stringify that sorts
object keys recursively or use a canonicalization helper) before passing to
createHash('sha256').update(...).digest('hex'), and apply it to the constructed
relevant object (fields: title, description, tagline, icon, theme, sidebar,
socialLinks, footer, home, openapi) so identical content with different
insertion orders yields the same hash.
In `@packages/cli/src/stories/dev.stories.tsx`:
- Around line 43-45: DevScreenPreview accepts an unbounded width so code that
computes width - 2 can produce negative values; guard and normalize the prop at
the top of DevScreenPreview by deriving a safeWidth (e.g., clamp to a minimum of
2 or coerce to a number with a sensible default) and then use safeWidth
everywhere instead of width (this will prevent negative widths passed to Alert
and separators referenced in the function and other places that compute width -
2).
In `@packages/config/schemas/schema.json`:
- Line 3: Regenerate the published schema.json (update the $id) after modifying
the schema so that title under ZpressConfig →
properties.apps.items.properties.title and the analogous locations for packages
and workspaces[].items accept only a plain string (remove or stop referencing
the TitleConfig object-form there); ensure TitleConfig remains reserved for
Section node shapes only and update any $ref entries so those three paths point
to a simple string schema (not the object TitleConfig), then rebuild/export the
schema.json so the new $id matches the regenerated file.
In `@packages/core/src/banner/index.ts`:
- Around line 164-173: Read the file contents into existing as you already do,
but normalize CRLF to LF before doing the marker and equality checks: convert
existing (and newContent for the equality check) to a normalized form (e.g.,
replace "\r\n" with "\n") and then derive firstLine from the normalized string;
then compare firstLine to GENERATED_MARKER and compare the normalized existing
to the normalized newContent. Update the logic around existing, firstLine,
GENERATED_MARKER and newContent to use the normalized strings so Windows CRLF
doesn't break the checks.
In `@packages/core/src/sync/copy.ts`:
- Around line 29-32: The fast-path after calling tryMtimeSkip(page, ctx) should
not return the cached result unless the materialized file actually exists on
disk; update the logic so that after obtaining a non-null cached value you
verify the corresponding materialized file (e.g., the .zpress/content entry or
the page's output path resolved from ctx/page) exists and is readable, and only
then return cached—otherwise proceed with rewrite. Apply the same existence
check to the other fast-path in the same module (the block referenced at lines
~131-163) so both shortcuts validate on-disk presence before skipping
regeneration.
In `@packages/core/src/sync/openapi.ts`:
- Around line 60-64: The reducer for specMtimes mutates the accumulator via
Object.assign(acc, result.specMtimes); change reduce on configResults to collect
entries immutably (e.g., accumulate arrays of [key,value] or use a new object
returned each iteration) and only materialize the final Record<string, number>
once so specMtimes is created without mutating prior state; update the reduce
usage around specMtimes/configResults to return a new object or gather entries
and then build the final specMtimes map after reduction.
---
Outside diff comments:
In `@packages/cli/src/lib/watcher.ts`:
- Around line 139-143: The callback passed to watch (used to create the watcher
constant) uses an awkward Array.prototype.reduce returning null to avoid
expression statements; replace that reduce with a clearer imperative loop (e.g.,
forEach or for...of) inside the (_event, filename) => { ... } handler so you can
perform side-effecting operations directly and remove the null return
workaround, leaving the watcher, watch, and repoRoot usages intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a98b7d92-0963-4ae8-aedb-4bbaac88a941
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yaml
📒 Files selected for processing (52)
.changeset/fix-dev-banner-and-exit.md.changeset/fix-meta-missing-dirs.md.changeset/incremental-sync-engine.mdCONTRIBUTING.mdcontributing/README.mdcontributing/concepts/architecture.mdcontributing/concepts/cli.mdcontributing/concepts/config.mdcontributing/concepts/engine/dev.mdcontributing/concepts/engine/incremental.mdcontributing/concepts/engine/openapi.mdcontributing/concepts/engine/overview.mdcontributing/concepts/engine/pipeline.mdcontributing/guides/getting-started.mdcontributing/references/cli.mdpackage.jsonpackages/cli/kidd.config.tspackages/cli/package.jsonpackages/cli/rslib.config.tspackages/cli/src/commands/build.tspackages/cli/src/commands/check.tspackages/cli/src/commands/clean.tspackages/cli/src/commands/dev.tspackages/cli/src/commands/diff.tspackages/cli/src/commands/draft.tspackages/cli/src/commands/dump.tspackages/cli/src/commands/serve.tspackages/cli/src/commands/setup.tspackages/cli/src/index.tspackages/cli/src/lib/dev-types.tspackages/cli/src/lib/rspress.tspackages/cli/src/lib/watcher.tspackages/cli/src/screens/dev-screen.tsxpackages/cli/src/stories/dev.stories.tsxpackages/cli/tsconfig.jsonpackages/config/schemas/schema.jsonpackages/core/src/banner/index.tspackages/core/src/sync/copy.tspackages/core/src/sync/images.tspackages/core/src/sync/index.tspackages/core/src/sync/openapi.tspackages/core/src/sync/sidebar/index.tspackages/core/src/sync/sidebar/meta.tspackages/core/src/sync/sidebar/multi.test.tspackages/core/src/sync/sidebar/multi.tspackages/core/src/sync/sidebar/write-meta.tspackages/core/src/sync/types.tspackages/ui/src/config.tspackages/ui/src/theme/components/sidebar/sidebar-scope.tsxpackages/ui/src/theme/hooks/use-zpress.tspackages/ui/src/theme/index.tsxzpress.config.ts
💤 Files with no reviewable changes (4)
- packages/cli/rslib.config.ts
- contributing/concepts/cli.md
- packages/core/src/sync/sidebar/multi.test.ts
- packages/core/src/sync/sidebar/multi.ts
…home page HMR - Extract Banner into reusable component at components/banner.tsx - Switch font from chrome to block with zpress purple gradient - Add standalone Banner story - Include actions, features, apps, packages, and workspaces in restart-relevance hash so home page updates on config changes Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/cli/src/lib/watcher.ts (2)
48-58: 🛠️ Refactor suggestion | 🟠 MajorAvoid adding more
let-backed watcher state.The new sync coordinator suppresses the repo’s functional TS rule in five places. Please collapse this into an immutable state transition/helper instead of growing a mutable closure; the current reentrancy path is already hard to audit.
As per coding guidelines "No
let— onlyconst. No reassignment, no mutation."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/lib/watcher.ts` around lines 48 - 58, Replace the multiple let-backed pieces of watcher state (config, syncing, pendingReloadConfig, consecutiveFailures) with a single immutable watcher state and a pure reducer/updater function (e.g., transitionWatcherState or watchStateReducer) that returns new state objects instead of mutating variables; keep callbacks.onStatusChange usage but call it with the new state's status. Locate uses of config, syncing, pendingReloadConfig, and consecutiveFailures and adapt them to read from the single state object and dispatch actions (e.g., { type: 'startSync'|'finishSync'|'fail'|'reloadConfig' }) to the reducer which returns the updated state, ensuring no let or in-place mutation remains in the closure.
125-133:⚠️ Potential issue | 🟠 MajorDon't drop non-markdown sync inputs.
After the root
zpress.config.*special case, every other change is ignored unless the path ends in.md/.mdx. That skips imported config helpers, OpenAPI specs, public assets, and other non-markdown sync inputs, so dev can keep serving stale output until a manual resync or an unrelated markdown save.Also applies to: 152-163
🤖 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/cli/src/screens/dev-screen.tsx`:
- Around line 242-243: Clamp the computed terminal width to a safe minimum
before using it: replace the direct const width = Math.min(columns, 80) with a
clamped value (e.g., const width = Math.max(Math.min(columns, 80), 2)) so width
- 2 cannot go negative; then use this clamped width everywhere you derive layout
sizes (the separator render and the error alert code paths that currently rely
on width/columns) to avoid unstable rendering. Make sure any other calculations
in the same file that read columns or recompute sizes (the blocks around the
current width usage) use this clamped variable rather than raw columns.
- Around line 225-239: The input handler in useInput (dev-screen.tsx) returns
early when phase !== 'ready', which prevents the quit keys from working during
loading/error; change the handler so it first checks for quit keys (input ===
'q' or key.ctrl && input === 'c') and performs the watcherRef.current.close(),
exit(), and process.exit(0) path regardless of phase, and only after that apply
the phase !== 'ready' early-return for other keys/behavior; keep references to
watcherRef.current, exit(), and process.exit as used now and do not alter
isActive logic.
- Around line 143-145: The TUI currently only stores and renders the watcher
status enum (collapsing errors to "● Error") instead of the full WatcherStatus
with its message; update the watcher state to store the entire WatcherStatus
object (including message) and change the callbacks to pass the full status
object—specifically modify the WatcherCallbacks usage in the callbacks object
(onStatusChange: guard(setWatcherStatus) and onSyncComplete) so they call
setWatcherStatus with the whole WatcherStatus, and update the render/path that
shows the tag (the component that reads watcher status) to display
status.message when status.type === 'error' (or otherwise show the message)
rather than only the status label. Ensure types (WatcherStatus/WatcherCallbacks)
align with this change.
---
Outside diff comments:
In `@packages/cli/src/lib/watcher.ts`:
- Around line 48-58: Replace the multiple let-backed pieces of watcher state
(config, syncing, pendingReloadConfig, consecutiveFailures) with a single
immutable watcher state and a pure reducer/updater function (e.g.,
transitionWatcherState or watchStateReducer) that returns new state objects
instead of mutating variables; keep callbacks.onStatusChange usage but call it
with the new state's status. Locate uses of config, syncing,
pendingReloadConfig, and consecutiveFailures and adapt them to read from the
single state object and dispatch actions (e.g., { type:
'startSync'|'finishSync'|'fail'|'reloadConfig' }) to the reducer which returns
the updated state, ensuring no let or in-place mutation remains in the closure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: aaefb94b-f73e-4a65-8456-953f33d0f156
📒 Files selected for processing (6)
.changeset/fix-home-page-hmr.mdpackages/cli/src/components/banner.tsxpackages/cli/src/lib/watcher.tspackages/cli/src/screens/dev-screen.tsxpackages/cli/src/stories/banner.stories.tsxpackages/cli/src/stories/dev.stories.tsx
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix "afterwards" → "afterward" (American English consistency) - Replace double hyphens with em dash in openapi docs - Rename "openapi.yaml" → "OpenAPI specs" in overview flowchart - Normalize CRLF line endings in banner shouldGenerate check - Add output file existence check in mtime skip optimization - Surface watcher error messages in dev TUI status bar - Keep quit hotkey active during loading/error phases - Clamp terminal width to prevent negative separator lengths - Guard story preview against narrow widths 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 `@packages/cli/src/screens/dev-screen.tsx`:
- Around line 280-283: The inline ternary in the watcher error label should be
removed; compute the label before JSX or use a match expression instead of `?:`.
For example, derive a string like `const watcherErrorLabel = watcherStatus._tag
=== 'error' ? \`: ${watcherStatus.message}\` : ''` using an if/else or
ts-pattern `match` (reference symbols: watcherStatus and the Text rendering
block that uses `.with('error', () => (`)) and then render `<Text color="red">●
Error{watcherErrorLabel}</Text>` so no ternary remains inside the JSX
expression.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 364b3c32-cef0-4eb5-83c8-ee83cc26e1e9
📒 Files selected for processing (8)
contributing/concepts/engine/incremental.mdcontributing/concepts/engine/openapi.mdcontributing/concepts/engine/overview.mdpackages/cli/package.jsonpackages/cli/src/screens/dev-screen.tsxpackages/cli/src/stories/dev.stories.tsxpackages/core/src/banner/index.tspackages/core/src/sync/copy.ts
Co-Authored-By: Claude <noreply@anthropic.com>
Summary
ink-big-text+ink-gradient, fix "q" hotkey not exiting_meta.jsonto prevent Rspress ENOENT crash on empty sectionsChanges
@zpress/core: incremental sync engine (minor), sidebar dir creation fix (patch)@zpress/cli: kidd build migration (minor), dev TUI banner + quit fix (patch)Test plan
zpress devand verify styled banner renders correctlyqto confirm the dev server exits cleanlyzpress buildon a project with empty section directoriespnpm checkpasses