feat(create-rezi): add starship command console template#191
feat(create-rezi): add starship command console template#191RtlZeroMemory merged 2 commits intomainfrom
Conversation
📝 WalkthroughWalkthroughAdds a new "starship" starter template to create-rezi: a full demo app including types, state management, seed data, keybindings, theme utilities, six UI screens, a shell, an application bootstrap, tests, and template configuration/docs. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Input as Input Handler
participant Resolver as Command Resolver
participant Dispatcher as Dispatch
participant Reducer as State Reducer
participant Router as Router
participant Screen as Screen Renderer
participant Shell as Shell UI
participant Display as Terminal
User->>Input: press key
Input->>Resolver: normalize key & resolveStarshipCommand(key, route)
Resolver-->>Input: StarshipCommand | undefined
Input->>Dispatcher: dispatch(action / apply-command)
Dispatcher->>Reducer: reduceStarshipState(state, action)
Reducer-->>Dispatcher: new state
Dispatcher->>Router: navigate/update current route
Router->>Screen: render(context, deps) with new state
Screen->>Shell: renderShell(body)
Shell->>Display: update terminal UI
Display-->>User: refreshed UI
sequenceDiagram
participant Bootstrap as App Bootstrap
participant State as State Container
participant Ticker as Tick Scheduler
participant Generator as Simulation
participant Dispatcher as Dispatch
participant Renderer as Screen Renderer
participant Display as Terminal
Bootstrap->>State: createInitialState(now)
Bootstrap->>Ticker: start interval (TICK_MS)
loop every tick
Ticker->>Generator: generate telemetry/comms/toast(tick)
Generator-->>Dispatcher: dispatch tick actions
Dispatcher->>State: reduceStarshipState(...)
State-->>Renderer: new state
Renderer->>Display: render updated UI
end
Estimated Code Review Effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2a5627285
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const channel = channels[(tick / 2) % channels.length] ?? "fleet"; | ||
| const priority = priorities[tick % priorities.length] ?? "routine"; | ||
| const sender = senders[tick % senders.length] ?? "Fleet Command"; | ||
| const content = `${contents[(tick * 3) % contents.length] ?? "Status update"} · t${tick}`; |
There was a problem hiding this comment.
Base comms message variants on emission index
This generator only runs when tick % 4 === 0, but the channel/priority/sender/content selectors are all indexed directly by tick; as a result, generated traffic is heavily degenerate (priority is always routine, content is always the first template, and channels only alternate between fleet/internal). In normal runtime this silently removes most channel and severity variation from the comms simulation, so deck behaviors that rely on diverse traffic never get exercised.
Useful? React with 👍 / 👎.
| title: "Command Deck", | ||
| style: styles.panelStyle, | ||
| }, | ||
| [BridgeCommandDeck({ key: `bridge-command-${state.tick}`, state, dispatch: deps.dispatch })], |
There was a problem hiding this comment.
Keep bridge command deck key stable across ticks
Using state.tick in this component key forces BridgeCommandDeck to unmount/remount every simulation tick (800ms), which resets its hook state and restarts its internal 1s useInterval before it can fire. In practice the local uptime/scan sweep loop never progresses and the deck does extra mount churn each frame; this key should be stable so widget-local animations and timers can persist.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 20
🧹 Nitpick comments (17)
packages/create-rezi/templates/starship/tsconfig.json (1)
2-12: AddoutDirto prevent emitted JavaScript files in source directory.The
buildscript runstsc --pretty false, which emits JavaScript without anoutDirdirective, causing.jsfiles to be written alongside.tssources. The template should specify"outDir": "dist"to keep sources clean and align with the project's.gitignorepattern fordist/.🔧 Proposed addition
"compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", + "outDir": "dist", "strict": true,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/tsconfig.json` around lines 2 - 12, The tsconfig.json compilerOptions is missing an outDir which causes tsc to emit .js files next to .ts sources; update the "compilerOptions" block in packages/create-rezi/templates/starship/tsconfig.json to include "outDir": "dist" (so the build (tsc --pretty false) writes outputs to dist/), ensuring this aligns with the project's .gitignore and prevents emitted JS from cluttering source files.packages/create-rezi/src/scaffold.ts (1)
121-124:"starship" → "starship"identity alias is redundant.
TEMPLATE_ALIASESis already seeded with every key → key identity mapping at lines 103–105 (TEMPLATE_DEFINITIONS.map((t) => [t.key, t.key])). No other template re-sets its own identity alias in the manual block — removing line 121 is the consistent approach.♻️ Proposed fix
-TEMPLATE_ALIASES.set("starship", "starship"); TEMPLATE_ALIASES.set("ship", "starship"); TEMPLATE_ALIASES.set("bridge", "starship"); TEMPLATE_ALIASES.set("command", "starship");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/src/scaffold.ts` around lines 121 - 124, Remove the redundant identity alias by deleting the explicit TEMPLATE_ALIASES.set("starship", "starship") entry; TEMPLATE_ALIASES is already seeded with identity mappings from TEMPLATE_DEFINITIONS (via TEMPLATE_DEFINITIONS.map((t) => [t.key, t.key])), so keep the other aliases (TEMPLATE_ALIASES.set("ship", "starship"), TEMPLATE_ALIASES.set("bridge", "starship"), TEMPLATE_ALIASES.set("command", "starship")) but remove the duplicate TEMPLATE_ALIASES.set("starship", "starship") line.packages/create-rezi/templates/starship/src/helpers/state.ts (2)
19-28: Consider exportingSubsystemTreeNodeandSystemHealthtypes.These types are used as return types of the exported
subsystemTree()andsystemHealth()functions. Without exporting them, consumers cannot reference these types by name (they can only infer them). If any screen or test needs to annotate a variable with these types, they'd be unable to.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/helpers/state.ts` around lines 19 - 28, Export the two helper types so callers can reference them: make SubsystemTreeNode and SystemHealth exported types (they are the return shapes used by subsystemTree() and systemHealth()), e.g. change their declarations to exported type aliases or exported interfaces; update any local references to use the exported names so consumers (screens/tests) can import and annotate variables with SubsystemTreeNode and SystemHealth.
48-50:freezeArrayappears unused.
freezeArrayis defined but not called anywhere in this file.🧹 Remove dead code
-function freezeArray<T>(input: readonly T[]): readonly T[] { - return Object.freeze([...input]); -} -🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/helpers/state.ts` around lines 48 - 50, The function freezeArray is defined but never used; remove the dead code by deleting the freezeArray<T>(input: readonly T[]): readonly T[] { return Object.freeze([...input]); } declaration (and any related unused imports or exports) and run type-check/build to ensure no remaining references; if the original intent was to reuse it elsewhere, alternatively replace local usages to call freezeArray (rather than an inline freeze) and export it—choose one fix so freezeArray is either removed or actually referenced.packages/create-rezi/templates/starship/src/helpers/simulation.ts (1)
91-103:clampInt,fract, andnoiseare duplicated instate.ts.These three utility functions are identically defined in both
simulation.tsandstate.ts. Consider extracting them into a shared internal utility (e.g.,helpers/math.ts) to avoid the duplication.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/helpers/simulation.ts` around lines 91 - 103, The functions clampInt, fract, and noise are duplicated in both simulation.ts and state.ts; extract them into a shared helper module (e.g., create a new helpers/math.ts exporting clampInt, fract, and noise), remove the duplicate definitions from simulation.ts and state.ts, and import the shared implementations in both files (use the exported function names clampInt, fract, noise) so both modules reuse the same utilities.packages/create-rezi/templates/starship/src/screens/engineering.ts (1)
24-33: Consider memoizing derived structures rebuilt every render.
buildSubsystemChildren(...)andheatmapDataare recomputed each render (and this screen likely re-renders on ticks). Memoizing them (based onprops.state.subsystems) would reduce per-tick allocations.As per coding guidelines, “Use
ctx.useMemo()for expensive computations in widget render functions”.Also applies to: 90-104
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/engineering.ts` around lines 24 - 33, The buildSubsystemChildren map and heatmapData are rebuilt every render; wrap their computation with ctx.useMemo() keyed on props.state.subsystems (and any other inputs like props.state.heatmapSources) so they are recomputed only when inputs change; locate the buildSubsystemChildren function and the code that computes heatmapData in the engineering screen and replace direct calls/inline computation with ctx.useMemo(() => /* compute */ , [props.state.subsystems, ...]) ensuring the memo returns the Map and heatmap array respectively.packages/create-rezi/templates/starship/src/screens/settings.ts (1)
6-13: Consider aligning slider bounds with the validation range (optional).Validation errors if threshold is outside 20–95 (Line 10-12), but the slider allows 0–100 (Line 81-83). If you want to prevent invalid states rather than demonstrate validation, tighten
min/maxto20/95.Also applies to: 79-87
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/settings.ts` around lines 6 - 13, The validation in function validationError checks state.alertThreshold must be between 20 and 95 but the UI slider allows 0–100, so update the slider's bounds to match the validation: change its min to 20 and max to 95 (and optionally adjust any default/initial value to fall in that range) so state.alertThreshold cannot be set to an invalid value; ensure references to state.alertThreshold and the slider component are consistent with the 20–95 range.packages/create-rezi/templates/starship/src/screens/shell.ts (1)
21-37: Command palette route shortcuts: consider adding 3–6 for parity with global nav keys.
routeCommandItems()only assigns shortcuts for Bridge/Engineering (Line 28-32), but the template supports 1–6 route switching. Adding shortcuts for the remaining routes makes the command palette match the rest of the UX.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/shell.ts` around lines 21 - 37, routeCommandItems currently only assigns shortcuts for route.id === "bridge" and "engineering", so update the function to assign numeric shortcuts for all supported route ids (1–6) to match global nav keys; inside routeCommandItems (and the object spread building each CommandItem) replace the two-branch shortcut logic with a mapping from route.id to shortcut (e.g., map route ids to "1","2","3","4","5","6") or a switch that returns { shortcut: "<n>" } for each known route id, leaving routes without a mapping unchanged.packages/create-rezi/templates/starship/src/__tests__/keybindings.test.ts (1)
5-30: Add normalization edge-cases to matchtrim().toLowerCase()behavior.Since the resolver normalizes input, consider adding a couple assertions like:
" Q "→"quit""CTRL+P"→"toggle-command-palette"""/" "→undefinedThat will lock in the input-normalization contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/__tests__/keybindings.test.ts` around lines 5 - 30, Update the keybinding tests to cover input-normalization edge cases for resolveStarshipCommand: add assertions that inputs with surrounding whitespace and mixed case are normalized (e.g., resolveStarshipCommand(" Q ") === "quit" and resolveStarshipCommand("CTRL+P") === "toggle-command-palette"), and that empty or all-whitespace strings return undefined (e.g., resolveStarshipCommand("") and resolveStarshipCommand(" ") === undefined); place these assertions alongside the existing tests so resolveStarshipCommand's trim().toLowerCase() contract is explicitly validated.packages/create-rezi/templates/starship/src/screens/crew.ts (1)
57-61: Loading state currently renders the skeleton and the full table.Right now
asyncCrew.loading(Line 252) only adds a “Loading Crew Manifest” panel;ui.masterDetail(...)is still always rendered. If the intention is to “replace” the table during loading, consider gating the master/detail block behind the same condition and dropping the redundantui.text("")fallback.Also applies to: 251-261
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/crew.ts` around lines 57 - 61, The loading state currently renders both the skeleton and the full table; update the render logic so when asyncCrew.loading is true the master/detail table is not rendered. Specifically, in the component that uses asyncCrew (the useAsync call and the asyncCrew variable), guard the ui.masterDetail(...) block behind a check like if (!asyncCrew.loading) { ui.masterDetail(...) } (or render the master/detail only when asyncCrew.loading is false) and remove the redundant ui.text("") fallback so the loading skeleton replaces—not duplicates—the table. Ensure you reference asyncCrew.loading and the ui.masterDetail(...) call when making the change.packages/create-rezi/templates/starship/src/screens/cargo.ts (1)
44-47: Scatter y-values can exceed the declared axis max (100).With
y: item.priority * 20 + (index % 7)(Line 45),priority=5yields up to 106, but y-axis max is 100 (Line 132). Consider clamping or adjusting the axis max to avoid clipped points.Also applies to: 131-133
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/cargo.ts` around lines 44 - 47, The scatter y calculation (y: item.priority * 20 + (index % 7)) can produce values >100 while the chart y-axis max is fixed at 100; update the data mapping that builds y (the expression using item.priority and index) to clamp values to the axis max (e.g., replace the y expression with a Math.min(..., 100) clamp) or compute a dynamic axis max from the dataset and set the chart’s y-axis max to that computed value so points never get clipped; adjust the code that sets the chart y-domain/max to use the new dynamic value if you choose that route.packages/create-rezi/templates/starship/src/main.ts (2)
83-105:stopAppdisposes even ifapp.stop()throws — verify this is safe.Line 103 calls
app.dispose()unconditionally after catching any error fromapp.stop(). Ifstop()partially completed,dispose()on a half-stopped app may throw or leak. Consider guarding:try { await app.stop(); } catch { /* ignore */ } try { app.dispose(); } catch { /* ignore */ }That said, since the process exits immediately after on Line 104, this is low-risk in practice.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/main.ts` around lines 83 - 105, The stopApp function currently calls app.dispose() unconditionally after awaiting app.stop(), which can throw if stop() partially completed; update stopApp so that app.dispose() is invoked inside its own try/catch (separate from the app.stop() try/catch) to swallow or log any dispose errors and avoid crashing before exit; keep existing clearing of tickTimer/toastTimer and setting stopping, and ensure exit(code) still runs after the guarded dispose so the process terminates reliably (references: stopApp, app.stop, app.dispose, tickTimer, toastTimer, stopping, exit).
107-316: Consider a lookup table for simple command → action mappings.
applyCommandis ~210 lines of sequentialif/returnblocks. Many branches are trivial one-liner dispatches (e.g.,cycle-theme→dispatch({ type: "cycle-theme" })). Extracting those into aRecord<string, StarshipAction>map and falling through to adispatch(map[command])would cut the function roughly in half while keeping the complex branches (navigation,comms-acknowledge, channel cycling) as explicitifblocks.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/main.ts` around lines 107 - 316, applyCommand is lengthy with many trivial if-branches; extract simple command→action mappings into a lookup (e.g., const simpleActions: Record<string, StarshipAction>) and replace repeated one-line dispatch calls with a single lookup + dispatch path inside applyCommand; keep complex branches (navigation commands using navigate, deck offsets using navigateDeckOffset, comms-acknowledge using app.update + filteredMessages + reduceStarshipState, and channel cycling logic) as explicit if-blocks so they remain unchanged, and ensure keys like "cycle-theme", "toggle-help", "toggle-command-palette", "toggle-pause", "toggle-autopilot", "toggle-red-alert", "set-alert-green"/"yellow"/"red", "engineering-boost", "engineering-diagnostics", "crew-new-assignment"/"crew-edit-selected", "cargo-sort-*" and "settings-reset" map to the correct action objects used by dispatch.packages/create-rezi/templates/starship/src/screens/bridge.ts (3)
101-116: Hardcoded chart colors inlineSeries.
"#7ec8ff"and"#7cf0c1"are hardcoded. Same concern as indrawShipSchematic— these should ideally derive from theme tokens so chart colors update with theme changes.As per coding guidelines: "NEVER hardcode RGB values — let the theme handle colors via tokens".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/bridge.ts` around lines 101 - 116, The chart colors are hardcoded in the lineSeries constant (colors "#7ec8ff" and "#7cf0c1"); replace these hex literals with theme token lookups so colors react to theme changes—for example obtain reactor/shield color tokens from the theme/context used by this module (the same approach used in drawShipSchematic) and use those token values in the objects inside lineSeries; ensure you still provide a sensible fallback if a token is missing and keep the dependency array on ctx.useMemo in sync with any theme token values you read.
91-91: Misleading variable namescanId.
scanId(fromctx.id("scan")) is used as theidfor the red alert button (Line 275), not for the scan button (which usesctx.id("scan-button")at Line 257). Consider renaming toalertIdorredAlertIdto avoid confusion during maintenance.Also applies to: 274-279
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/bridge.ts` at line 91, The variable scanId (assigned via ctx.id("scan")) is misleading because it is used as the id for the red alert button; rename scanId to redAlertId (or alertId), change the ctx.id invocation to ctx.id("red-alert") (or similar distinct string) and update every usage where the red alert button consumes that id so that the scan button still uses ctx.id("scan-button") unchanged; ensure all references to scanId in this file (including the red alert button creation and any handlers) are updated to the new symbol name.
32-87: Hardcoded RGB hex values in canvas drawing.The
drawShipSchematicfunction contains 11+ hardcoded hex color strings (e.g.,"#0e1626","#35608a","#89d0ff","#6ff0cf", etc.). The coding guidelines state: NEVER hardcode RGB values — let the theme handle colors via tokens.Even for a canvas context where theme integration may be less straightforward, consider passing theme-derived colors into
drawShipSchematicvia theoptionsparameter so the canvas respects theme switches (day/night/etc.). At minimum, extract these into named constants at the top of the file so they're easy to swap.As per coding guidelines: "NEVER hardcode RGB values — let the theme handle colors via tokens".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/bridge.ts` around lines 32 - 87, drawShipSchematic currently uses many hardcoded hex color strings which violates the "NEVER hardcode RGB values" guideline; fix by making the canvas colors theme-aware: add a colors bag to the drawShipSchematic signature (e.g., extend options with a colors: Readonly<{ background, border, hull, reactorFill, reactorStroke, shield, sweep, warp, starA, starB, text }>) and replace each literal like "#0e1626", "#35608a", "#89d0ff", "#6ff0cf", "#afffe8", "#7eb9ff", "#4fd3f7", "#80f5ff", "#d5f3ff", "#8ec7ff", "#b6d9ff" with the corresponding token from that colors object; alternatively, if changing the signature is not feasible, extract those literals into named top-level constants (backgroundColor, borderColor, hullColor, reactorFillColor, reactorStrokeColor, shieldColor, sweepColor, warpColor, starColorA, starColorB, textColor) and use those symbols in drawShipSchematic so theme tokens can be mapped in one place.packages/create-rezi/templates/starship/src/screens/comms.ts (1)
52-60:when()fallback to emptyui.text("")— minor but could useshow()instead.The
when(...) ?? ui.text("")pattern at Line 53 renders an invisible empty text node when the condition is false. If the framework'sshow()helper handles the falsy case by returning nothing (no node), it would be slightly cleaner. Not a bug — just a readability note.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/screens/comms.ts` around lines 52 - 60, Replace the fallback empty node pattern "when(...) ?? ui.text(\"\")" with the framework's show() helper so no invisible text node is created; locate the ui.column call and the when(...) that checks state.activeChannel === "emergency" and wrap the conditional callout in show(...) (or use show(() => state.activeChannel === "emergency" ? ui.callout(...) : undefined)) so the callout is rendered when true and nothing is rendered when false.
🤖 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/create-rezi/templates/starship/package.json`:
- Around line 18-23: The package.json lists "@types/node": "^22.13.1" which
targets Node 22 while "engines.node" is ">=18"; update the dependency to the
conservative minimum major (e.g., change "@types/node" to "^18") so TypeScript
typings match the declared engine floor and review other template package.json
entries for the same mismatch; locate the "@types/node" entry in the
dependencies/devDependencies section to modify it and run install/typecheck to
verify.
In `@packages/create-rezi/templates/starship/src/__tests__/reducer.test.ts`:
- Around line 76-82: Replace the forbidden non-null assertions in the test that
uses createInitialState, reduceStarshipState and sortedCargo: instead of using
sorted[0]! and sorted[1]!, use safe access (optional chaining or explicit
guards) to get names after the existing assert.ok(sorted.length > 5) check —
e.g. obtain names via sorted[0]?.name and sorted[1]?.name with a sensible
fallback or assert the two entries exist before comparing; update the
localeCompare call to handle possibly undefined names so the linter no longer
requires non-null assertions.
In `@packages/create-rezi/templates/starship/src/__tests__/render.test.ts`:
- Around line 1-13: The import declarations are unsorted causing the CI failure;
reorder and group them to match the project's organizeImports rules (external
packages first, then local modules, and alphabetically within groups) and/or run
the repo's autofix (e.g., eslint --fix or your editor's "organize imports") to
apply the change; specifically adjust the block importing createTestRenderer,
createInitialState, STARSHIP_ROUTES, and the render* imports
(renderBridgeScreen, renderCargoScreen, renderCommsScreen, renderCrewScreen,
renderEngineeringScreen, renderSettingsScreen) and ensure type imports
(RouteRenderContext, RouterApi, RouteDeps, RouteId, StarshipState) are grouped
correctly.
In `@packages/create-rezi/templates/starship/src/helpers/keybindings.ts`:
- Around line 118-131: In resolveStarshipCommand, when a routeId is provided the
fallback chain can pull keys from other routes via COMMAND_BY_KEY; change the
logic so after checking ROUTE_KEY_OVERRIDES[routeId] you only fall back to
GLOBAL_COMMAND_BY_KEY[normalized] (remove the COMMAND_BY_KEY fallback) so active
routes only use their overrides plus globals; reference resolveStarshipCommand,
ROUTE_KEY_OVERRIDES, GLOBAL_COMMAND_BY_KEY and COMMAND_BY_KEY to locate and
update the conditional return.
In `@packages/create-rezi/templates/starship/src/helpers/state.ts`:
- Around line 300-308: Replace forbidden non-null assertions in clampSizes by
using safe access/defaults: in function clampSizes use (normalized[0] ?? 0) and
(normalized[1] ?? 0) instead of normalized[0]! and normalized[1]! (update
variable `total` and the numerator when computing `first` accordingly) so the
logic and guards remain the same without using `!` (references: clampSizes,
normalized, total, first, second).
In `@packages/create-rezi/templates/starship/src/main.ts`:
- Around line 304-316: The "settings-save" branch inside applyCommand dispatches
the toast but is missing a trailing return and thus falls through; update the
branch where command === "settings-save" (the block that dispatches the toast
with id `save-${Date.now()}` and message "Settings snapshot saved") to append a
return; statement after the dispatch so it mirrors the other branches and
prevents fall-through/double-execution.
- Line 20: The linter flags the single-assignment `let app!` declaration
(variable app created with createNodeApp<StarshipState>) — suppress the rule by
adding an ESLint directive immediately above that line (e.g. `//
eslint-disable-next-line prefer-const`) so the `let app!` pattern is allowed
while retaining the existing closure-based initialization; ensure the comment
directly precedes the `let app!: ReturnType<typeof
createNodeApp<StarshipState>>;` declaration.
- Around line 3-7: Re-order the import statements using your editor's "Organize
Imports" (or manually) so they satisfy the linter: sort the module specifiers
alphabetically and keep the type-only import separate (import type { RouteDeps,
RouteId, StarshipAction, StarshipState } from "./types.js") as the final import;
ensure the value imports (resolveStarshipCommand, createInitialState,
filteredMessages, reduceStarshipState, createStarshipRoutes, STARSHIP_ROUTES,
themeSpec) are ordered by their module paths (e.g. "./helpers/keybindings.js",
"./helpers/state.js", "./screens/index.js", "./theme.js") to match the lint
rule.
- Around line 367-375: The escape handler currently mutates state via
bindingMap.escape using app.update(...) with object spreads which bypasses the
reducer and freezeState; change it to read the current state with app.getState()
and then call dispatch(...) with the appropriate action(s) (e.g., the existing
toggle-help, toggle-command-palette, toggle-hail-dialog, toggle-reset-dialog
action types handled by reduceStarshipState) instead of calling app.update
directly so the reducer and freezeState run and any reducer side-effects are
applied.
In `@packages/create-rezi/templates/starship/src/screens/bridge.ts`:
- Line 306: The key for BridgeCommandDeck is unstable because it's tied to
state.tick, causing the component to be destroyed and recreated every 800ms and
losing local state (scanSweep, scanBoost, uptime, lastReactorRef) and restarting
animation hooks (useSpring/useTransition/useSequence/useStagger); change the key
to a stable value (e.g., a constant string like "bridge-command" or a stable
identifier) instead of using `state.tick` so the BridgeCommandDeck instance
persists across ticks and retains its local state and animation progress.
In `@packages/create-rezi/templates/starship/src/screens/cargo.ts`:
- Around line 43-47: The scatterPoints mapping currently hardcodes hex colors;
update it to use theme tokens by deriving colors from
themeSpec(state.themeName).theme.colors (or a shared palette helper) and map
categories to those tokens (e.g., ordnance, medical, default) so scatterPoints
(the x/y/color mapping for cargo.slice(0,160)) uses theme-based colors instead
of "#ff6b77", "#7ff0b2", "#7cc6ff"; ensure you import/resolve themeSpec or the
palette helper and reference the specific color tokens when building the color
property.
In `@packages/create-rezi/templates/starship/src/screens/comms.ts`:
- Around line 177-195: The modal actions put the primary "Transmit" button
before the "Cancel" button; reorder the buttons in the actions array so the
cancel button (id "comms-hail-cancel", onPress -> deps.dispatch({ type:
"toggle-hail-dialog" })) comes first and the primary submit/send button (id
"comms-hail-send", onPress -> deps.dispatch({ type: "send-hail", target:
state.hailTarget..., message: state.hailMessage... })) comes last to match the
guideline ui.actions([cancelBtn, submitBtn]).
In `@packages/create-rezi/templates/starship/src/screens/crew.ts`:
- Around line 77-80: The page calculation can become 0 causing a negative start
and surprising slice results; change the clamp for page to ensure it's at least
1 and at most totalPages by computing page using both Math.max and Math.min
(i.e., clamp props.state.crewPage between 1 and totalPages) before deriving
start and pageData so start = (page - 1) * props.state.crewPageSize never goes
negative; update the variables page, start, and pageData usage around
totalPages, props.state.crewPage, props.state.crewPageSize, and sorted
accordingly.
In `@packages/create-rezi/templates/starship/src/screens/engineering.ts`:
- Around line 34-70: drawReactor currently hardcodes hex colors throughout
(ctx.clear, ctx.strokeRect, ctx.arc, ctx.fillCircle, ctx.circle, ctx.line,
ctx.text); change drawReactor to use theme colors instead by either accepting a
colors object param or pulling themeSpec(...).theme.colors inside the function
(e.g., replace "#131728", "#476088", "#8acaff", "#ffb681", "#66efd0", "#bffef0",
"#79d8ff", "#8ef5c2", "#b7ccff" with theme tokens). Update the call sites to
pass the palette (or ensure theme access is available) and map the token names
to the appropriate uses (background, border, arcPrimary/arcSecondary, coreFill,
coreStroke, linePrimary/lineAlternate, text) so no hardcoded RGB values remain.
- Around line 121-142: The subsystem tree (ui.tree) is incorrectly wired to
props.state.selectedCrewId and has a no-op onSelect; remove the conditional
selected prop that spreads ...(props.state.selectedCrewId === null ? {} : {
selected: props.state.selectedCrewId }) and delete the onSelect: () => {}
handler from the ui.tree props in the engineering screen so the tree no longer
advertises a subsystem selection tied to a crew ID (there is no subsystem
selection state/action like "toggle-subsystem" to handle it).
In `@packages/create-rezi/templates/starship/src/screens/settings.ts`:
- Around line 150-162: The toast id can collide because onPress currently uses
`settings-saved-${state.tick}`; change the id generation in the onPress handler
to use a monotonic timestamp (e.g., `state.nowMs`) or combine it with tick
(e.g., `${state.nowMs}-${state.tick}`) so each dispatched toast in deps.dispatch
(type "add-toast") has a unique id; update the toast object creation inside the
onPress function to use the new id scheme.
- Around line 179-203: The three theme buttons (ids "theme-day", "theme-night",
"theme-alert") currently all call deps.dispatch({ type: "cycle-theme" }) so they
only cycle instead of selecting the named theme; update each ui.button.onPress
to dispatch a new explicit action like deps.dispatch({ type: "set-theme", theme:
"day" }) / "night" / "alert", and add handling for "set-theme" in the reducer
that sets state.themeName to the provided theme (or, if you prefer, implement a
payload convention consistent with existing actions); ensure the intent checks
still use state.themeName to highlight the active button.
In `@packages/create-rezi/templates/starship/src/screens/shell.ts`:
- Around line 110-156: The help modal currently hard-codes returnFocusTo to
"deck-sidebar-bridge", causing focus to always jump to the bridge item; update
the help modal instantiation (the returnFocusTo property on the help modal /
dialog) to use the dynamic currentRoute variable instead, i.e. set returnFocusTo
to `deck-sidebar-${currentRoute}` so focus is restored to the currently selected
sidebar item (use the existing currentRoute in scope where the help modal is
created).
In `@packages/create-rezi/templates/starship/src/theme.ts`:
- Around line 1-10: The imports in theme.ts are incorrectly ordered causing CI
failures; move the external package import from "@rezi-ui/core" above the local
relative import "./types.js" so that darkTheme, draculaTheme, extendTheme,
nordTheme and the type imports (BadgeVariant, TextStyle, ThemeDefinition) are
imported before AlertLevel and ThemeName; update the import block to list the
`@rezi-ui/core` import first, then the ./types.js import to satisfy the import
sorting rule.
---
Nitpick comments:
In `@packages/create-rezi/src/scaffold.ts`:
- Around line 121-124: Remove the redundant identity alias by deleting the
explicit TEMPLATE_ALIASES.set("starship", "starship") entry; TEMPLATE_ALIASES is
already seeded with identity mappings from TEMPLATE_DEFINITIONS (via
TEMPLATE_DEFINITIONS.map((t) => [t.key, t.key])), so keep the other aliases
(TEMPLATE_ALIASES.set("ship", "starship"), TEMPLATE_ALIASES.set("bridge",
"starship"), TEMPLATE_ALIASES.set("command", "starship")) but remove the
duplicate TEMPLATE_ALIASES.set("starship", "starship") line.
In `@packages/create-rezi/templates/starship/src/__tests__/keybindings.test.ts`:
- Around line 5-30: Update the keybinding tests to cover input-normalization
edge cases for resolveStarshipCommand: add assertions that inputs with
surrounding whitespace and mixed case are normalized (e.g.,
resolveStarshipCommand(" Q ") === "quit" and resolveStarshipCommand("CTRL+P")
=== "toggle-command-palette"), and that empty or all-whitespace strings return
undefined (e.g., resolveStarshipCommand("") and resolveStarshipCommand(" ")
=== undefined); place these assertions alongside the existing tests so
resolveStarshipCommand's trim().toLowerCase() contract is explicitly validated.
In `@packages/create-rezi/templates/starship/src/helpers/simulation.ts`:
- Around line 91-103: The functions clampInt, fract, and noise are duplicated in
both simulation.ts and state.ts; extract them into a shared helper module (e.g.,
create a new helpers/math.ts exporting clampInt, fract, and noise), remove the
duplicate definitions from simulation.ts and state.ts, and import the shared
implementations in both files (use the exported function names clampInt, fract,
noise) so both modules reuse the same utilities.
In `@packages/create-rezi/templates/starship/src/helpers/state.ts`:
- Around line 19-28: Export the two helper types so callers can reference them:
make SubsystemTreeNode and SystemHealth exported types (they are the return
shapes used by subsystemTree() and systemHealth()), e.g. change their
declarations to exported type aliases or exported interfaces; update any local
references to use the exported names so consumers (screens/tests) can import and
annotate variables with SubsystemTreeNode and SystemHealth.
- Around line 48-50: The function freezeArray is defined but never used; remove
the dead code by deleting the freezeArray<T>(input: readonly T[]): readonly T[]
{ return Object.freeze([...input]); } declaration (and any related unused
imports or exports) and run type-check/build to ensure no remaining references;
if the original intent was to reuse it elsewhere, alternatively replace local
usages to call freezeArray (rather than an inline freeze) and export it—choose
one fix so freezeArray is either removed or actually referenced.
In `@packages/create-rezi/templates/starship/src/main.ts`:
- Around line 83-105: The stopApp function currently calls app.dispose()
unconditionally after awaiting app.stop(), which can throw if stop() partially
completed; update stopApp so that app.dispose() is invoked inside its own
try/catch (separate from the app.stop() try/catch) to swallow or log any dispose
errors and avoid crashing before exit; keep existing clearing of
tickTimer/toastTimer and setting stopping, and ensure exit(code) still runs
after the guarded dispose so the process terminates reliably (references:
stopApp, app.stop, app.dispose, tickTimer, toastTimer, stopping, exit).
- Around line 107-316: applyCommand is lengthy with many trivial if-branches;
extract simple command→action mappings into a lookup (e.g., const simpleActions:
Record<string, StarshipAction>) and replace repeated one-line dispatch calls
with a single lookup + dispatch path inside applyCommand; keep complex branches
(navigation commands using navigate, deck offsets using navigateDeckOffset,
comms-acknowledge using app.update + filteredMessages + reduceStarshipState, and
channel cycling logic) as explicit if-blocks so they remain unchanged, and
ensure keys like "cycle-theme", "toggle-help", "toggle-command-palette",
"toggle-pause", "toggle-autopilot", "toggle-red-alert",
"set-alert-green"/"yellow"/"red", "engineering-boost",
"engineering-diagnostics", "crew-new-assignment"/"crew-edit-selected",
"cargo-sort-*" and "settings-reset" map to the correct action objects used by
dispatch.
In `@packages/create-rezi/templates/starship/src/screens/bridge.ts`:
- Around line 101-116: The chart colors are hardcoded in the lineSeries constant
(colors "#7ec8ff" and "#7cf0c1"); replace these hex literals with theme token
lookups so colors react to theme changes—for example obtain reactor/shield color
tokens from the theme/context used by this module (the same approach used in
drawShipSchematic) and use those token values in the objects inside lineSeries;
ensure you still provide a sensible fallback if a token is missing and keep the
dependency array on ctx.useMemo in sync with any theme token values you read.
- Line 91: The variable scanId (assigned via ctx.id("scan")) is misleading
because it is used as the id for the red alert button; rename scanId to
redAlertId (or alertId), change the ctx.id invocation to ctx.id("red-alert") (or
similar distinct string) and update every usage where the red alert button
consumes that id so that the scan button still uses ctx.id("scan-button")
unchanged; ensure all references to scanId in this file (including the red alert
button creation and any handlers) are updated to the new symbol name.
- Around line 32-87: drawShipSchematic currently uses many hardcoded hex color
strings which violates the "NEVER hardcode RGB values" guideline; fix by making
the canvas colors theme-aware: add a colors bag to the drawShipSchematic
signature (e.g., extend options with a colors: Readonly<{ background, border,
hull, reactorFill, reactorStroke, shield, sweep, warp, starA, starB, text }>)
and replace each literal like "#0e1626", "#35608a", "#89d0ff", "#6ff0cf",
"#afffe8", "#7eb9ff", "#4fd3f7", "#80f5ff", "#d5f3ff", "#8ec7ff", "#b6d9ff" with
the corresponding token from that colors object; alternatively, if changing the
signature is not feasible, extract those literals into named top-level constants
(backgroundColor, borderColor, hullColor, reactorFillColor, reactorStrokeColor,
shieldColor, sweepColor, warpColor, starColorA, starColorB, textColor) and use
those symbols in drawShipSchematic so theme tokens can be mapped in one place.
In `@packages/create-rezi/templates/starship/src/screens/cargo.ts`:
- Around line 44-47: The scatter y calculation (y: item.priority * 20 + (index %
7)) can produce values >100 while the chart y-axis max is fixed at 100; update
the data mapping that builds y (the expression using item.priority and index) to
clamp values to the axis max (e.g., replace the y expression with a
Math.min(..., 100) clamp) or compute a dynamic axis max from the dataset and set
the chart’s y-axis max to that computed value so points never get clipped;
adjust the code that sets the chart y-domain/max to use the new dynamic value if
you choose that route.
In `@packages/create-rezi/templates/starship/src/screens/comms.ts`:
- Around line 52-60: Replace the fallback empty node pattern "when(...) ??
ui.text(\"\")" with the framework's show() helper so no invisible text node is
created; locate the ui.column call and the when(...) that checks
state.activeChannel === "emergency" and wrap the conditional callout in
show(...) (or use show(() => state.activeChannel === "emergency" ?
ui.callout(...) : undefined)) so the callout is rendered when true and nothing
is rendered when false.
In `@packages/create-rezi/templates/starship/src/screens/crew.ts`:
- Around line 57-61: The loading state currently renders both the skeleton and
the full table; update the render logic so when asyncCrew.loading is true the
master/detail table is not rendered. Specifically, in the component that uses
asyncCrew (the useAsync call and the asyncCrew variable), guard the
ui.masterDetail(...) block behind a check like if (!asyncCrew.loading) {
ui.masterDetail(...) } (or render the master/detail only when asyncCrew.loading
is false) and remove the redundant ui.text("") fallback so the loading skeleton
replaces—not duplicates—the table. Ensure you reference asyncCrew.loading and
the ui.masterDetail(...) call when making the change.
In `@packages/create-rezi/templates/starship/src/screens/engineering.ts`:
- Around line 24-33: The buildSubsystemChildren map and heatmapData are rebuilt
every render; wrap their computation with ctx.useMemo() keyed on
props.state.subsystems (and any other inputs like props.state.heatmapSources) so
they are recomputed only when inputs change; locate the buildSubsystemChildren
function and the code that computes heatmapData in the engineering screen and
replace direct calls/inline computation with ctx.useMemo(() => /* compute */ ,
[props.state.subsystems, ...]) ensuring the memo returns the Map and heatmap
array respectively.
In `@packages/create-rezi/templates/starship/src/screens/settings.ts`:
- Around line 6-13: The validation in function validationError checks
state.alertThreshold must be between 20 and 95 but the UI slider allows 0–100,
so update the slider's bounds to match the validation: change its min to 20 and
max to 95 (and optionally adjust any default/initial value to fall in that
range) so state.alertThreshold cannot be set to an invalid value; ensure
references to state.alertThreshold and the slider component are consistent with
the 20–95 range.
In `@packages/create-rezi/templates/starship/src/screens/shell.ts`:
- Around line 21-37: routeCommandItems currently only assigns shortcuts for
route.id === "bridge" and "engineering", so update the function to assign
numeric shortcuts for all supported route ids (1–6) to match global nav keys;
inside routeCommandItems (and the object spread building each CommandItem)
replace the two-branch shortcut logic with a mapping from route.id to shortcut
(e.g., map route ids to "1","2","3","4","5","6") or a switch that returns {
shortcut: "<n>" } for each known route id, leaving routes without a mapping
unchanged.
In `@packages/create-rezi/templates/starship/tsconfig.json`:
- Around line 2-12: The tsconfig.json compilerOptions is missing an outDir which
causes tsc to emit .js files next to .ts sources; update the "compilerOptions"
block in packages/create-rezi/templates/starship/tsconfig.json to include
"outDir": "dist" (so the build (tsc --pretty false) writes outputs to dist/),
ensuring this aligns with the project's .gitignore and prevents emitted JS from
cluttering source files.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
packages/create-rezi/src/__tests__/scaffold.test.tspackages/create-rezi/src/index.tspackages/create-rezi/src/scaffold.tspackages/create-rezi/templates/starship/.gitignorepackages/create-rezi/templates/starship/README.mdpackages/create-rezi/templates/starship/package.jsonpackages/create-rezi/templates/starship/src/__tests__/keybindings.test.tspackages/create-rezi/templates/starship/src/__tests__/reducer.test.tspackages/create-rezi/templates/starship/src/__tests__/render.test.tspackages/create-rezi/templates/starship/src/helpers/formatters.tspackages/create-rezi/templates/starship/src/helpers/keybindings.tspackages/create-rezi/templates/starship/src/helpers/simulation.tspackages/create-rezi/templates/starship/src/helpers/state.tspackages/create-rezi/templates/starship/src/main.tspackages/create-rezi/templates/starship/src/screens/bridge.tspackages/create-rezi/templates/starship/src/screens/cargo.tspackages/create-rezi/templates/starship/src/screens/comms.tspackages/create-rezi/templates/starship/src/screens/crew.tspackages/create-rezi/templates/starship/src/screens/engineering.tspackages/create-rezi/templates/starship/src/screens/index.tspackages/create-rezi/templates/starship/src/screens/settings.tspackages/create-rezi/templates/starship/src/screens/shell.tspackages/create-rezi/templates/starship/src/theme.tspackages/create-rezi/templates/starship/src/types.tspackages/create-rezi/templates/starship/tsconfig.json
| import assert from "node:assert/strict"; | ||
| import test from "node:test"; | ||
| import type { RouteRenderContext, RouterApi } from "@rezi-ui/core"; | ||
| import { createTestRenderer } from "@rezi-ui/core/testing"; | ||
| import { createInitialState } from "../helpers/state.js"; | ||
| import { STARSHIP_ROUTES } from "../screens/index.js"; | ||
| import { renderBridgeScreen } from "../screens/bridge.js"; | ||
| import { renderCargoScreen } from "../screens/cargo.js"; | ||
| import { renderCommsScreen } from "../screens/comms.js"; | ||
| import { renderCrewScreen } from "../screens/crew.js"; | ||
| import { renderEngineeringScreen } from "../screens/engineering.js"; | ||
| import { renderSettingsScreen } from "../screens/settings.js"; | ||
| import type { RouteDeps, RouteId, StarshipState } from "../types.js"; |
There was a problem hiding this comment.
CI failure: organize imports in this file.
Pipeline reports Import statements could be sorted by organizeImports (Lines 4-11). Please run the repo’s “organize imports” autofix / lint-fix so CI passes.
🧰 Tools
🪛 GitHub Actions: ci
[error] 4-11: Import statements could be sorted by organizeImports.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/__tests__/render.test.ts` around
lines 1 - 13, The import declarations are unsorted causing the CI failure;
reorder and group them to match the project's organizeImports rules (external
packages first, then local modules, and alphabetically within groups) and/or run
the repo's autofix (e.g., eslint --fix or your editor's "organize imports") to
apply the change; specifically adjust the block importing createTestRenderer,
createInitialState, STARSHIP_ROUTES, and the render* imports
(renderBridgeScreen, renderCargoScreen, renderCommsScreen, renderCrewScreen,
renderEngineeringScreen, renderSettingsScreen) and ensure type imports
(RouteRenderContext, RouterApi, RouteDeps, RouteId, StarshipState) are grouped
correctly.
| ui.panel("Theme Preview", [ | ||
| ui.grid( | ||
| { | ||
| columns: 3, | ||
| gap: 1, | ||
| }, | ||
| ui.button({ | ||
| id: "theme-day", | ||
| label: "Day Shift", | ||
| intent: state.themeName === "day" ? "primary" : "secondary", | ||
| onPress: () => deps.dispatch({ type: "cycle-theme" }), | ||
| }), | ||
| ui.button({ | ||
| id: "theme-night", | ||
| label: "Night Shift", | ||
| intent: state.themeName === "night" ? "primary" : "secondary", | ||
| onPress: () => deps.dispatch({ type: "cycle-theme" }), | ||
| }), | ||
| ui.button({ | ||
| id: "theme-alert", | ||
| label: "Red Alert", | ||
| intent: state.themeName === "alert" ? "primary" : "secondary", | ||
| onPress: () => deps.dispatch({ type: "cycle-theme" }), | ||
| }), | ||
| ), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for theme-related action types in the reducer
rg -n "cycle-theme|set-theme|set-theme-name" packages/create-rezi/templates/starship/src -t ts -B 2 -A 2Repository: RtlZeroMemory/Rezi
Length of output: 5551
Theme Preview buttons don't select their named theme—all three just cycle.
Lines 189, 195, and 201 each dispatch { type: "cycle-theme" }, ignoring which button was clicked. The button labels and conditional intent styling (showing the active theme as "primary") imply direct selection, but the behavior contradicts this. Either add a set-theme action to the reducer with an explicit target, or rework the grid to clarify it's a cycle control, not theme selection buttons.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/settings.ts` around lines
179 - 203, The three theme buttons (ids "theme-day", "theme-night",
"theme-alert") currently all call deps.dispatch({ type: "cycle-theme" }) so they
only cycle instead of selecting the named theme; update each ui.button.onPress
to dispatch a new explicit action like deps.dispatch({ type: "set-theme", theme:
"day" }) / "night" / "alert", and add handling for "set-theme" in the reducer
that sets state.themeName to the provided theme (or, if you prefer, implement a
payload convention consistent with existing actions); ensure the intent checks
still use state.themeName to highlight the active button.
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/create-rezi/src/scaffold.ts (1)
5-123:⚠️ Potential issue | 🟡 MinorUpdate
CHANGELOG.mdwith an entry for the new starship template. Other templates (dashboard, stress-test, animation-lab) are documented in the changelog; add a brief note about the starship template addition to keep releases traceable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/src/scaffold.ts` around lines 5 - 123, Add a brief changelog entry documenting the new "starship" template: update CHANGELOG.md to include a short release note referencing the TemplateDefinition with key "starship" (from TEMPLATE_DEFINITIONS) — mention the addition of the Starship Command Console template, its purpose/one-line summary, and link or note that users can scaffold it via the "starship" template key (and aliases managed in TEMPLATE_ALIASES) so releases remain traceable.
🧹 Nitpick comments (1)
packages/create-rezi/templates/starship/src/main.ts (1)
252-281: Preferdispatchoverapp.update()for comms actions.These branches bypass the reducer-first pattern. You can read state via
app.getState()and thendispatch, keeping all updates consistent with the reducer flow.♻️ Suggested refactor
if (command === "comms-acknowledge") { - app.update((previous) => { - const candidate = filteredMessages(previous).find((message) => !message.acknowledged); - if (!candidate) return previous; - return reduceStarshipState(previous, { - type: "acknowledge-message", - messageId: candidate.id, - }); - }); + const state = app.getState(); + const candidate = filteredMessages(state).find((message) => !message.acknowledged); + if (!candidate) return; + dispatch({ type: "acknowledge-message", messageId: candidate.id }); return; } if (command === "comms-next-channel" || command === "comms-prev-channel") { - app.update((previous) => { - const channels: readonly StarshipState["activeChannel"][] = [ - "fleet", - "local", - "emergency", - "internal", - ]; - const index = channels.indexOf(previous.activeChannel); - const safe = index < 0 ? 0 : index; - const nextIndex = - command === "comms-next-channel" - ? (safe + 1) % channels.length - : (safe - 1 + channels.length) % channels.length; - const channel = channels[nextIndex] ?? "fleet"; - return reduceStarshipState(previous, { type: "switch-channel", channel }); - }); + const state = app.getState(); + const channels: readonly StarshipState["activeChannel"][] = [ + "fleet", + "local", + "emergency", + "internal", + ]; + const index = channels.indexOf(state.activeChannel); + const safe = index < 0 ? 0 : index; + const nextIndex = + command === "comms-next-channel" + ? (safe + 1) % channels.length + : (safe - 1 + channels.length) % channels.length; + const channel = channels[nextIndex] ?? "fleet"; + dispatch({ type: "switch-channel", channel }); return; }As per coding guidelines: “Prefer
useReducerpattern (reducer + dispatch) over rawapp.update().”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/create-rezi/templates/starship/src/main.ts` around lines 252 - 281, Replace the direct app.update(...) usage in the comms branches with a reducer-style dispatch: call app.getState() to read current state, compute the candidate/message or next channel using filteredMessages and the channels array, and then call app.dispatch(...) with the corresponding action object instead of calling reduceStarshipState inside app.update. Specifically, for the "comms-acknowledge" branch use app.getState(), find candidate via filteredMessages(previous).find(...), return early if none, otherwise app.dispatch({ type: "acknowledge-message", messageId: candidate.id }); and for "comms-next-channel"/"comms-prev-channel" compute nextIndex using previous.activeChannel and the channels array then call app.dispatch({ type: "switch-channel", channel }). Ensure you remove the app.update(...) blocks and reference the same action types used by reduceStarshipState.
🤖 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/create-rezi/templates/starship/src/helpers/state.ts`:
- Around line 554-583: Add a new reducer branch handling action.type ===
"set-cargo-bay" that updates the matching cargo item's bay and returns the
frozen updated state (following the pattern used in set-cargo-priority): map
state.cargo, replace the item with Object.freeze({ ...item, bay: action.bay })
when item.id === action.cargoId, wrap the resulting cargo array with
Object.freeze and return freezeState({ ...state, cargo }); also update the
StarshipAction union in types.ts to include the set-cargo-bay action shape
(cargoId and bay) so the action is typed across the codebase.
In `@packages/create-rezi/templates/starship/src/screens/bridge.ts`:
- Around line 47-87: The bridge screen is using hardcoded hex colors (e.g.,
"#0e1626", "#35608a", "#89d0ff", etc.) which breaks theming; update the
rendering to derive a color palette from the theme and use those tokens
everywhere: obtain theme.colors via themeSpec(...).theme.colors (or the existing
theme helper used in Cargo), create a small palette object (canvas, border,
accent, highlight, glow, sweep, pixelA/pixelB) and pass that palette into
drawShipSchematic (or wherever this snippet lives) instead of inlining hex
strings, and replace usages in ctx.clear, ctx.roundedRect, ctx.polyline,
ctx.fillCircle/ctx.circle, ctx.arc, ctx.line, ctx.setPixel, and the "USS REZI"
text; also update any line series code (lineSeries) to reference the theme-based
colors rather than raw hex literals (the same palette applies to lines 100-116).
Ensure function/variable names like drawShipSchematic, ctx.clear,
ctx.roundedRect, ctx.polyline, ctx.fillCircle, ctx.circle, ctx.arc, ctx.line,
ctx.setPixel, and lineSeries are updated to accept and use the palette.
- Around line 218-233: The "Systems Status" label uses the wrong text variant:
update the ui.text call that renders "Systems Status" (inside the ui.column
block where each(...) iterates subsystems) to use variant: "caption" instead of
variant: "label" so it matches the project's caption convention for section
labels; leave the surrounding ui.column, ui.row, and progress/ui.status logic
unchanged.
In `@packages/create-rezi/templates/starship/src/screens/cargo.ts`:
- Around line 150-233: Import the conditional helpers (maybe and/or show) from
`@rezi-ui/core` and replace the two ternary renderings: swap the cargo manifest
ternary that chooses between ui.empty(...) and ui.virtualList(...) to use a
conditional helper (e.g., show or maybe) so the empty state and the virtualList
are expressed via the helper instead of `cargo.length === 0 ? ... : ...` (refer
to the virtualList usage and ui.empty call). Likewise replace the selected-item
ternary that renders ui.column(...) vs ui.empty(...) with maybe(selected, (item)
=> ui.column(...)) ?? ui.empty(...) so the selected rendering uses the helper
(refer to the selected variable, ui.column, ui.slider, ui.field and the each
block). Ensure the top-level imports include maybe/show from `@rezi-ui/core` and
leave other symbols (virtualList, eachInline, each) intact.
- Around line 208-218: The Bay Assignment select in cargo.ts currently has a
no-op onChange; wire it to state by dispatching a new action: add a
"set-cargo-bay" variant to the StarshipAction union in types.ts, implement a
reducer branch for "set-cargo-bay" in helpers/state.ts that updates the cargo
bay (mirror the existing set-cargo-priority reducer logic), and change the
ui.select (id "cargo-bay-select", value derived from selected.bay) onChange to
dispatch the set-cargo-bay action with the newly selected bay value.
In `@packages/create-rezi/templates/starship/src/screens/crew.ts`:
- Around line 200-221: The actions array currently renders the Save button
before Cancel; update the ui.actions(...) call in the crew editor so the cancel
action (the button with id ctx.id("crew-cancel") / onPress dispatching { type:
"toggle-crew-editor" }) comes first and the primary Save action (the button with
id ctx.id("crew-save" / onPress dispatching { type: "assign-crew", crewId:
member.id, ... })) is last, preserving all props/handlers but swapping their
order to follow the cancel-first, submit-last guideline.
In `@packages/create-rezi/templates/starship/src/screens/shell.ts`:
- Around line 1-13: The Route Health list is being rendered with .map; replace
that with ui.eachInline (or eachInline) passing the items (systemHealth), a
renderer function, and an options object that contains the key function (e.g., {
key: h => h.id }) instead of embedding the key in ui.row props; update the
occurrences around the Route Health rendering (including the block referencing
systemHealth and the lines noted ~142-148) so the key is provided as the third
parameter to eachInline and the renderer only returns the row vnode.
---
Outside diff comments:
In `@packages/create-rezi/src/scaffold.ts`:
- Around line 5-123: Add a brief changelog entry documenting the new "starship"
template: update CHANGELOG.md to include a short release note referencing the
TemplateDefinition with key "starship" (from TEMPLATE_DEFINITIONS) — mention the
addition of the Starship Command Console template, its purpose/one-line summary,
and link or note that users can scaffold it via the "starship" template key (and
aliases managed in TEMPLATE_ALIASES) so releases remain traceable.
---
Nitpick comments:
In `@packages/create-rezi/templates/starship/src/main.ts`:
- Around line 252-281: Replace the direct app.update(...) usage in the comms
branches with a reducer-style dispatch: call app.getState() to read current
state, compute the candidate/message or next channel using filteredMessages and
the channels array, and then call app.dispatch(...) with the corresponding
action object instead of calling reduceStarshipState inside app.update.
Specifically, for the "comms-acknowledge" branch use app.getState(), find
candidate via filteredMessages(previous).find(...), return early if none,
otherwise app.dispatch({ type: "acknowledge-message", messageId: candidate.id
}); and for "comms-next-channel"/"comms-prev-channel" compute nextIndex using
previous.activeChannel and the channels array then call app.dispatch({ type:
"switch-channel", channel }). Ensure you remove the app.update(...) blocks and
reference the same action types used by reduceStarshipState.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
README.mddocs/dev/repo-layout.mddocs/getting-started/create-rezi.mddocs/getting-started/examples.mddocs/getting-started/quickstart.mddocs/packages/create-rezi.mddocs/packages/index.mdpackages/create-rezi/README.mdpackages/create-rezi/src/scaffold.tspackages/create-rezi/templates/starship/package.jsonpackages/create-rezi/templates/starship/src/__tests__/keybindings.test.tspackages/create-rezi/templates/starship/src/__tests__/reducer.test.tspackages/create-rezi/templates/starship/src/__tests__/render.test.tspackages/create-rezi/templates/starship/src/helpers/keybindings.tspackages/create-rezi/templates/starship/src/helpers/state.tspackages/create-rezi/templates/starship/src/main.tspackages/create-rezi/templates/starship/src/screens/bridge.tspackages/create-rezi/templates/starship/src/screens/cargo.tspackages/create-rezi/templates/starship/src/screens/comms.tspackages/create-rezi/templates/starship/src/screens/crew.tspackages/create-rezi/templates/starship/src/screens/engineering.tspackages/create-rezi/templates/starship/src/screens/settings.tspackages/create-rezi/templates/starship/src/screens/shell.tspackages/create-rezi/templates/starship/src/theme.tspackages/create-rezi/templates/starship/src/types.tspackages/create-rezi/templates/starship/tsconfig.json
✅ Files skipped from review due to trivial changes (2)
- docs/getting-started/create-rezi.md
- README.md
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/create-rezi/templates/starship/src/tests/render.test.ts
- packages/create-rezi/templates/starship/tsconfig.json
- packages/create-rezi/templates/starship/src/screens/settings.ts
- packages/create-rezi/templates/starship/src/types.ts
- packages/create-rezi/templates/starship/src/screens/engineering.ts
| if (action.type === "set-cargo-scroll") { | ||
| return freezeState({ ...state, cargoScrollTop: Math.max(0, Math.floor(action.scrollTop)) }); | ||
| } | ||
|
|
||
| if (action.type === "set-cargo-sort") { | ||
| return freezeState({ ...state, cargoSortBy: action.sortBy }); | ||
| } | ||
|
|
||
| if (action.type === "set-cargo-category-filter") { | ||
| return freezeState({ ...state, cargoCategoryFilter: action.category }); | ||
| } | ||
|
|
||
| if (action.type === "select-cargo") { | ||
| return freezeState({ ...state, selectedCargoId: action.cargoId }); | ||
| } | ||
|
|
||
| if (action.type === "set-cargo-bulk-checked") { | ||
| return freezeState({ ...state, cargoBulkChecked: action.checked }); | ||
| } | ||
|
|
||
| if (action.type === "set-cargo-priority") { | ||
| const cargo = Object.freeze( | ||
| state.cargo.map((item) => { | ||
| if (item.id !== action.cargoId) return item; | ||
| return Object.freeze({ ...item, priority: clampInt(action.priority, 1, 5) }); | ||
| }), | ||
| ); | ||
| return freezeState({ ...state, cargo }); | ||
| } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 1. Check the StarshipAction type definition in types.ts
echo "=== StarshipAction definition ==="
rg -n --type=ts -A 50 'type\s+StarshipAction' packages/create-rezi/templates/starship/src/types.ts | head -80Repository: RtlZeroMemory/Rezi
Length of output: 3115
🏁 Script executed:
#!/bin/bash
# 2. Check for bay-related code in screens (searching for "bay" mentions)
echo "=== Bay-related code in screens ==="
find packages/create-rezi/templates/starship/src/screens -name "*.ts" -o -name "*.tsx" | xargs rg -i 'bay' -nRepository: RtlZeroMemory/Rezi
Length of output: 661
🏁 Script executed:
#!/bin/bash
# 3. Check the full state.ts to see what's in the reducer
echo "=== Check if set-cargo-bay already exists in state.ts ==="
rg -n 'set-cargo-bay' packages/create-rezi/templates/starship/src/helpers/state.tsRepository: RtlZeroMemory/Rezi
Length of output: 119
🏁 Script executed:
#!/bin/bash
# 4. Look at the Cargo interface/type definition to understand what properties it has
echo "=== Cargo type definition ==="
rg -n --type=ts -B 2 -A 10 'interface Cargo|type Cargo' packages/create-rezi/templates/starship/src/types.tsRepository: RtlZeroMemory/Rezi
Length of output: 732
Add missing set-cargo-bay reducer case to persist bay changes.
The cargo screen has an interactive "Bay Assignment" select UI but no state action to persist the selection. Without the set-cargo-bay reducer case (and corresponding StarshipAction type update), bay changes are discarded on re-render.
Proposed reducer case (plus update StarshipAction union in types.ts)
if (action.type === "set-cargo-bulk-checked") {
return freezeState({ ...state, cargoBulkChecked: action.checked });
}
+ if (action.type === "set-cargo-bay") {
+ const bay = clampInt(action.bay, 1, 12);
+ const cargo = Object.freeze(
+ state.cargo.map((item) => {
+ if (item.id !== action.cargoId) return item;
+ return Object.freeze({ ...item, bay });
+ }),
+ );
+ return freezeState({ ...state, cargo });
+ }
+
if (action.type === "set-cargo-priority") {
const cargo = Object.freeze(
state.cargo.map((item) => {
if (item.id !== action.cargoId) return item;
return Object.freeze({ ...item, priority: clampInt(action.priority, 1, 5) });
}),
);
return freezeState({ ...state, cargo });
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/helpers/state.ts` around lines
554 - 583, Add a new reducer branch handling action.type === "set-cargo-bay"
that updates the matching cargo item's bay and returns the frozen updated state
(following the pattern used in set-cargo-priority): map state.cargo, replace the
item with Object.freeze({ ...item, bay: action.bay }) when item.id ===
action.cargoId, wrap the resulting cargo array with Object.freeze and return
freezeState({ ...state, cargo }); also update the StarshipAction union in
types.ts to include the set-cargo-bay action shape (cargoId and bay) so the
action is typed across the codebase.
| ctx.clear("#0e1626"); | ||
| ctx.roundedRect(2, 2, Math.max(6, width - 4), Math.max(6, height - 4), 2, "#35608a"); | ||
|
|
||
| const hullPoints = [ | ||
| { x: 4, y: centerY }, | ||
| { x: centerX - 3, y: centerY - 3 }, | ||
| { x: centerX + 8, y: centerY - 2 }, | ||
| { x: width - 6, y: centerY }, | ||
| { x: centerX + 8, y: centerY + 2 }, | ||
| { x: centerX - 3, y: centerY + 3 }, | ||
| { x: 4, y: centerY }, | ||
| ] as const; | ||
| ctx.polyline(hullPoints, "#89d0ff"); | ||
|
|
||
| const reactorRadius = 1.2 + options.reactor * 1.8; | ||
| ctx.fillCircle(centerX - 4, centerY, reactorRadius, "#6ff0cf"); | ||
| ctx.circle(centerX - 4, centerY, reactorRadius + 1.2, "#afffe8"); | ||
|
|
||
| const shieldRadius = Math.max(2, Math.floor(5 + options.shield * 3 + options.pulse * 2)); | ||
| ctx.arc(centerX + 3, centerY, shieldRadius, 0.2, Math.PI * 1.8, "#7eb9ff"); | ||
|
|
||
| const sweepX = Math.floor(((options.sweep % 100) / 100) * (width - 8)) + 4; | ||
| ctx.line(sweepX, 3, sweepX, height - 4, "#4fd3f7"); | ||
|
|
||
| const warpTrail: Array<Readonly<{ x: number; y: number }>> = []; | ||
| for (let i = 0; i < 8; i++) { | ||
| warpTrail.push({ | ||
| x: centerX + 9 + i * 2, | ||
| y: centerY + Math.sin(i * 0.8 + options.warp * 2.1) * 1.2, | ||
| }); | ||
| } | ||
| ctx.polyline(warpTrail, "#80f5ff"); | ||
|
|
||
| for (let i = 0; i < 12; i++) { | ||
| const x = Math.floor(3 + ((i * 17 + Math.floor(options.sweep)) % Math.max(3, width - 6))); | ||
| const y = Math.floor(3 + ((i * 11 + Math.floor(options.sweep * 0.7)) % Math.max(3, height - 6))); | ||
| ctx.setPixel(x, y, i % 3 === 0 ? "#d5f3ff" : "#8ec7ff"); | ||
| } | ||
|
|
||
| ctx.text(4, 1, "USS REZI // COMMAND SCOPE", "#b6d9ff"); | ||
| } |
There was a problem hiding this comment.
Replace hardcoded hex colors with theme tokens (canvas + line chart).
Bridge currently hardcodes a full palette (#0e1626, #35608a, #89d0ff, etc.), so theme switching won’t apply and it violates the theming rules. Suggest deriving a palette from themeSpec(...).theme.colors (similar to Cargo) and passing it into drawShipSchematic, plus using theme-based colors for lineSeries.
As per coding guidelines, “NEVER hardcode RGB values — let the theme handle colors via tokens”.
One way to theme the palette using existing tokens + derived shades
import { formatPower, formatWarpFactor } from "../helpers/formatters.js";
import { selectedCrew } from "../helpers/state.js";
-import { stylesForTheme } from "../theme.js";
+import { stylesForTheme, themeSpec } from "../theme.js";
import type { RouteDeps, StarshipState } from "../types.js";
import { renderShell } from "./shell.js";
+type Rgb = Readonly<{ r: number; g: number; b: number }>;
+
+function toHex(color: Rgb): string {
+ const channel = (value: number) =>
+ Math.max(0, Math.min(255, Math.round(value)))
+ .toString(16)
+ .padStart(2, "0");
+ return `#${channel(color.r)}${channel(color.g)}${channel(color.b)}`;
+}
+
+function scaleRgb(color: Rgb, factor: number): Rgb {
+ return Object.freeze({
+ r: clamp(color.r * factor, 0, 255),
+ g: clamp(color.g * factor, 0, 255),
+ b: clamp(color.b * factor, 0, 255),
+ });
+}
+
+type BridgePalette = Readonly<{
+ bg: string;
+ frame: string;
+ hull: string;
+ reactor: string;
+ reactorGlow: string;
+ shield: string;
+ sweep: string;
+ warp: string;
+ starBright: string;
+ starDim: string;
+ text: string;
+}>;
function drawShipSchematic(
ctx: CanvasContext,
options: Readonly<{
warp: number;
pulse: number;
sweep: number;
reactor: number;
shield: number;
+ palette: BridgePalette;
}>,
): void {
const width = ctx.width;
const height = ctx.height;
const centerX = Math.floor(width * 0.35);
const centerY = Math.floor(height * 0.52);
- ctx.clear("#0e1626");
- ctx.roundedRect(2, 2, Math.max(6, width - 4), Math.max(6, height - 4), 2, "#35608a");
+ ctx.clear(options.palette.bg);
+ ctx.roundedRect(
+ 2,
+ 2,
+ Math.max(6, width - 4),
+ Math.max(6, height - 4),
+ 2,
+ options.palette.frame,
+ );
const hullPoints = [
{ x: 4, y: centerY },
{ x: centerX - 3, y: centerY - 3 },
{ x: centerX + 8, y: centerY - 2 },
{ x: width - 6, y: centerY },
{ x: centerX + 8, y: centerY + 2 },
{ x: centerX - 3, y: centerY + 3 },
{ x: 4, y: centerY },
] as const;
- ctx.polyline(hullPoints, "#89d0ff");
+ ctx.polyline(hullPoints, options.palette.hull);
const reactorRadius = 1.2 + options.reactor * 1.8;
- ctx.fillCircle(centerX - 4, centerY, reactorRadius, "#6ff0cf");
- ctx.circle(centerX - 4, centerY, reactorRadius + 1.2, "#afffe8");
+ ctx.fillCircle(centerX - 4, centerY, reactorRadius, options.palette.reactor);
+ ctx.circle(centerX - 4, centerY, reactorRadius + 1.2, options.palette.reactorGlow);
const shieldRadius = Math.max(2, Math.floor(5 + options.shield * 3 + options.pulse * 2));
- ctx.arc(centerX + 3, centerY, shieldRadius, 0.2, Math.PI * 1.8, "#7eb9ff");
+ ctx.arc(centerX + 3, centerY, shieldRadius, 0.2, Math.PI * 1.8, options.palette.shield);
const sweepX = Math.floor(((options.sweep % 100) / 100) * (width - 8)) + 4;
- ctx.line(sweepX, 3, sweepX, height - 4, "#4fd3f7");
+ ctx.line(sweepX, 3, sweepX, height - 4, options.palette.sweep);
const warpTrail: Array<Readonly<{ x: number; y: number }>> = [];
for (let i = 0; i < 8; i++) {
warpTrail.push({
x: centerX + 9 + i * 2,
y: centerY + Math.sin(i * 0.8 + options.warp * 2.1) * 1.2,
});
}
- ctx.polyline(warpTrail, "#80f5ff");
+ ctx.polyline(warpTrail, options.palette.warp);
for (let i = 0; i < 12; i++) {
const x = Math.floor(3 + ((i * 17 + Math.floor(options.sweep)) % Math.max(3, width - 6)));
const y = Math.floor(3 + ((i * 11 + Math.floor(options.sweep * 0.7)) % Math.max(3, height - 6)));
- ctx.setPixel(x, y, i % 3 === 0 ? "#d5f3ff" : "#8ec7ff");
+ ctx.setPixel(x, y, i % 3 === 0 ? options.palette.starBright : options.palette.starDim);
}
- ctx.text(4, 1, "USS REZI // COMMAND SCOPE", "#b6d9ff");
+ ctx.text(4, 1, "USS REZI // COMMAND SCOPE", options.palette.text);
}
const BridgeCommandDeck = defineWidget<BridgeCommandDeckProps>((props, ctx): VNode => {
const viewport = ctx.useViewport?.() ?? { width: 120, height: 40, breakpoint: "lg" as const };
const redAlertId = ctx.id("red-alert");
const [scanSweep, setScanSweep] = ctx.useState(0);
const [scanBoost, setScanBoost] = ctx.useState(false);
const [uptime, setUptime] = ctx.useState(0);
const lastReactorRef = ctx.useRef(props.state.telemetry.reactorPower);
+ const palette = ctx.useMemo(() => {
+ const colors = themeSpec(props.state.themeName).theme.colors;
+ const base = colors.accent.primary;
+ const success = colors.success;
+ return Object.freeze({
+ bg: toHex(scaleRgb(base, 0.12)),
+ frame: toHex(scaleRgb(base, 0.45)),
+ hull: toHex(scaleRgb(base, 1.05)),
+ reactor: toHex(scaleRgb(success, 1.0)),
+ reactorGlow: toHex(scaleRgb(success, 1.35)),
+ shield: toHex(scaleRgb(base, 0.95)),
+ sweep: toHex(scaleRgb(base, 1.25)),
+ warp: toHex(scaleRgb(base, 1.4)),
+ starBright: toHex(scaleRgb(base, 1.8)),
+ starDim: toHex(scaleRgb(base, 1.2)),
+ text: toHex(scaleRgb(base, 1.6)),
+ });
+ }, [props.state.themeName]);
+
const selected = selectedCrew(props.state);
const subsystemNames = props.state.subsystems.map((item) => item.name);
const chartWidth = clamp(Math.floor(viewport.width * 0.42), 28, 64);
const lineSeries = ctx.useMemo(
() =>
Object.freeze([
{
data: props.state.telemetryHistory,
- color: "#7ec8ff",
+ color: palette.hull,
label: "Reactor",
},
{
data: props.state.shieldHistory,
- color: "#7cf0c1",
+ color: palette.reactor,
label: "Shields",
},
]),
- [props.state.telemetryHistory, props.state.shieldHistory],
+ [props.state.telemetryHistory, props.state.shieldHistory, palette],
);
@@
draw: (canvasCtx) =>
drawShipSchematic(canvasCtx, {
warp,
pulse: shieldPulse,
sweep: scanSweep,
reactor,
shield: props.state.telemetry.shieldStrength / 100,
+ palette,
}),
}),Also applies to: 100-116
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/bridge.ts` around lines
47 - 87, The bridge screen is using hardcoded hex colors (e.g., "#0e1626",
"#35608a", "#89d0ff", etc.) which breaks theming; update the rendering to derive
a color palette from the theme and use those tokens everywhere: obtain
theme.colors via themeSpec(...).theme.colors (or the existing theme helper used
in Cargo), create a small palette object (canvas, border, accent, highlight,
glow, sweep, pixelA/pixelB) and pass that palette into drawShipSchematic (or
wherever this snippet lives) instead of inlining hex strings, and replace usages
in ctx.clear, ctx.roundedRect, ctx.polyline, ctx.fillCircle/ctx.circle, ctx.arc,
ctx.line, ctx.setPixel, and the "USS REZI" text; also update any line series
code (lineSeries) to reference the theme-based colors rather than raw hex
literals (the same palette applies to lines 100-116). Ensure function/variable
names like drawShipSchematic, ctx.clear, ctx.roundedRect, ctx.polyline,
ctx.fillCircle, ctx.circle, ctx.arc, ctx.line, ctx.setPixel, and lineSeries are
updated to accept and use the palette.
| ui.column({ gap: 1 }, [ | ||
| ui.text("Systems Status", { variant: "label" }), | ||
| each( | ||
| props.state.subsystems.slice(0, 8), | ||
| (subsystem, index) => { | ||
| const progress = bootProgress[index] ?? 0; | ||
| const online = subsystem.health >= props.state.alertThreshold; | ||
| return ui.row({ key: subsystem.id, gap: 1, items: "center", wrap: true }, [ | ||
| ui.status(online ? "online" : "busy", { showLabel: false }), | ||
| ui.text(`${subsystem.name}: ${online ? "OK" : "CHECK"}`), | ||
| ui.progress(progress, { | ||
| label: `${String(Math.round(progress * 100)).padStart(3, " ")}%`, | ||
| dsTone: online ? "default" : "warning", | ||
| }), | ||
| ]); | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check the exact content at the specified lines
head -233 "packages/create-rezi/templates/starship/src/screens/bridge.ts" | tail -16Repository: RtlZeroMemory/Rezi
Length of output: 838
🏁 Script executed:
# Search for ui.text usage with "label" variant to understand current patterns
rg 'ui\.text.*variant.*label' packages/create-rezi/templates/starship/src/ -A 1 -B 1Repository: RtlZeroMemory/Rezi
Length of output: 702
🏁 Script executed:
# Search for ui.text usage with "caption" variant to see the preferred pattern
rg 'ui\.text.*variant.*caption' packages/create-rezi/templates/starship/src/ -A 1 -B 1Repository: RtlZeroMemory/Rezi
Length of output: 4032
Change variant: "label" to variant: "caption" for "Systems Status".
The coding guidelines specify that labels should use variant: "caption", not variant: "label". The codebase consistently applies variant: "caption" for similar section labels and descriptive text throughout the template.
- ui.text("Systems Status", { variant: "label" }),
+ ui.text("Systems Status", { variant: "caption" }),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ui.column({ gap: 1 }, [ | |
| ui.text("Systems Status", { variant: "label" }), | |
| each( | |
| props.state.subsystems.slice(0, 8), | |
| (subsystem, index) => { | |
| const progress = bootProgress[index] ?? 0; | |
| const online = subsystem.health >= props.state.alertThreshold; | |
| return ui.row({ key: subsystem.id, gap: 1, items: "center", wrap: true }, [ | |
| ui.status(online ? "online" : "busy", { showLabel: false }), | |
| ui.text(`${subsystem.name}: ${online ? "OK" : "CHECK"}`), | |
| ui.progress(progress, { | |
| label: `${String(Math.round(progress * 100)).padStart(3, " ")}%`, | |
| dsTone: online ? "default" : "warning", | |
| }), | |
| ]); | |
| }, | |
| ui.column({ gap: 1 }, [ | |
| ui.text("Systems Status", { variant: "caption" }), | |
| each( | |
| props.state.subsystems.slice(0, 8), | |
| (subsystem, index) => { | |
| const progress = bootProgress[index] ?? 0; | |
| const online = subsystem.health >= props.state.alertThreshold; | |
| return ui.row({ key: subsystem.id, gap: 1, items: "center", wrap: true }, [ | |
| ui.status(online ? "online" : "busy", { showLabel: false }), | |
| ui.text(`${subsystem.name}: ${online ? "OK" : "CHECK"}`), | |
| ui.progress(progress, { | |
| label: `${String(Math.round(progress * 100)).padStart(3, " ")}%`, | |
| dsTone: online ? "default" : "warning", | |
| }), | |
| ]); | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/bridge.ts` around lines
218 - 233, The "Systems Status" label uses the wrong text variant: update the
ui.text call that renders "Systems Status" (inside the ui.column block where
each(...) iterates subsystems) to use variant: "caption" instead of variant:
"label" so it matches the project's caption convention for section labels; leave
the surrounding ui.column, ui.row, and progress/ui.status logic unchanged.
| ui.panel("Manifest", [ | ||
| cargo.length === 0 | ||
| ? ui.empty("No cargo matches the active filter", { | ||
| description: "Adjust category or sort settings", | ||
| }) | ||
| : ui.virtualList<CargoItem>({ | ||
| id: "cargo-virtual-list", | ||
| items: cargo, | ||
| itemHeight: 1, | ||
| overscan: 5, | ||
| renderItem: (item, index, focused) => | ||
| ui.row( | ||
| { | ||
| key: item.id, | ||
| gap: 1, | ||
| wrap: true, | ||
| ...(focused ? { style: { inverse: true } } : {}), | ||
| }, | ||
| [ | ||
| ui.text(`${String(index + 1).padStart(4, "0")}`), | ||
| ui.text(item.name), | ||
| ...eachInline( | ||
| [categoryLabel(item.category)], | ||
| (tag) => ui.tag(tag, { variant: categoryVariant(item.category) }), | ||
| { key: (_, i) => `${item.id}-tag-${i}` }, | ||
| ), | ||
| ui.text(`Q${item.quantity}`, { variant: "code" }), | ||
| ui.text(`P${item.priority}`, { variant: "caption" }), | ||
| ], | ||
| ), | ||
| onScroll: (scrollTop) => deps.dispatch({ type: "set-cargo-scroll", scrollTop }), | ||
| onSelect: (item) => deps.dispatch({ type: "select-cargo", cargoId: item.id }), | ||
| }), | ||
| ]), | ||
| ]), | ||
| ui.panel("Selected Cargo", [ | ||
| selected | ||
| ? ui.column({ gap: 1 }, [ | ||
| ui.row({ gap: 1, wrap: true }, [ | ||
| ui.badge(selected.name, { variant: "info" }), | ||
| ui.tag(categoryLabel(selected.category), { | ||
| variant: categoryVariant(selected.category), | ||
| }), | ||
| ]), | ||
| ui.slider({ | ||
| id: "cargo-priority-slider", | ||
| value: selected.priority, | ||
| min: 1, | ||
| max: 5, | ||
| step: 1, | ||
| label: "Priority", | ||
| onChange: (priority) => | ||
| deps.dispatch({ | ||
| type: "set-cargo-priority", | ||
| cargoId: selected.id, | ||
| priority, | ||
| }), | ||
| }), | ||
| ui.field({ | ||
| label: "Bay Assignment", | ||
| children: ui.select({ | ||
| id: "cargo-bay-select", | ||
| value: String(selected.bay), | ||
| options: Array.from({ length: 12 }, (_, index) => ({ | ||
| value: String(index + 1), | ||
| label: `Bay ${index + 1}`, | ||
| })), | ||
| onChange: () => {}, | ||
| }), | ||
| }), | ||
| each( | ||
| [ | ||
| `Quantity: ${selected.quantity}`, | ||
| `Priority: ${selected.priority}`, | ||
| `Bay: ${selected.bay}`, | ||
| ], | ||
| (line, index) => ui.text(line, { key: `${selected.id}-detail-${index}`, variant: "caption" }), | ||
| { key: (_, index) => `${selected?.id ?? "none"}-${index}` }, | ||
| ), | ||
| ]) | ||
| : ui.empty("No cargo item selected", { | ||
| description: "Select an item in the manifest", | ||
| }), | ||
| ]), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for the conditional rendering helper functions
rg -n --type=ts '\b(show|when|maybe|match)\s*\(' packages/create-rezi/templates/starship/src --max-count=20Repository: RtlZeroMemory/Rezi
Length of output: 2368
🏁 Script executed:
# Check if these functions are exported from the main package
rg -n --type=ts 'export.*\b(show|when|maybe|match)\b' packages/ --max-count=20Repository: RtlZeroMemory/Rezi
Length of output: 600
🏁 Script executed:
# Look for where these might be imported from in the templates
rg -n --type=ts 'import.*\{.*\b(show|when|maybe|match)\b' packages/create-rezi/templates --max-count=20Repository: RtlZeroMemory/Rezi
Length of output: 170
🏁 Script executed:
# Check the actual cargo.ts file to see current state and imports
head -n 30 packages/create-rezi/templates/starship/src/screens/cargo.tsRepository: RtlZeroMemory/Rezi
Length of output: 1261
🏁 Script executed:
# Look at crew.ts to see how conditional helpers are used
sed -n '130,155p' packages/create-rezi/templates/starship/src/screens/crew.ts
sed -n '145,160p' packages/create-rezi/templates/starship/src/screens/crew.tsRepository: RtlZeroMemory/Rezi
Length of output: 1768
🏁 Script executed:
# Check comms.ts for additional examples
sed -n '50,70p' packages/create-rezi/templates/starship/src/screens/comms.ts
sed -n '135,155p' packages/create-rezi/templates/starship/src/screens/comms.tsRepository: RtlZeroMemory/Rezi
Length of output: 1886
🏁 Script executed:
# Verify that cargo.ts is missing the conditional helper imports
rg -A 5 "^import" packages/create-rezi/templates/starship/src/screens/cargo.ts | head -n 15Repository: RtlZeroMemory/Rezi
Length of output: 674
🏁 Script executed:
# Confirm the exact ternary patterns in cargo.ts that should be converted
sed -n '150,155p' packages/create-rezi/templates/starship/src/screens/cargo.ts
sed -n '185,190p' packages/create-rezi/templates/starship/src/screens/cargo.tsRepository: RtlZeroMemory/Rezi
Length of output: 612
🏁 Script executed:
# Double-check the exact pattern used: are there any ternaries in crew.ts for comparison?
rg -B 2 -A 5 'selected\s*\?' packages/create-rezi/templates/starship/src/screens/crew.ts | head -n 40Repository: RtlZeroMemory/Rezi
Length of output: 600
🏁 Script executed:
# Check comms.ts for ternary patterns
rg -B 2 -A 5 'state\.\w+\s*\?' packages/create-rezi/templates/starship/src/screens/comms.ts | head -n 20Repository: RtlZeroMemory/Rezi
Length of output: 44
Import and use conditional rendering helpers instead of ternaries.
The cargo.ts file uses ternary operators at lines 151 and 186, but the guideline requires using show() / when() / maybe() / match() for consistency with other templates like crew.ts and comms.ts.
Import maybe and/or show from @rezi-ui/core and refactor:
- Line 151: Replace
cargo.length === 0 ? ui.empty(...) : ui.virtualList(...)withmaybe()or conditional logic - Line 186: Replace
selected ? ui.column(...) : ui.empty(...)withmaybe(selected, (item) => ui.column(...)) ?? ui.empty(...)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/cargo.ts` around lines
150 - 233, Import the conditional helpers (maybe and/or show) from `@rezi-ui/core`
and replace the two ternary renderings: swap the cargo manifest ternary that
chooses between ui.empty(...) and ui.virtualList(...) to use a conditional
helper (e.g., show or maybe) so the empty state and the virtualList are
expressed via the helper instead of `cargo.length === 0 ? ... : ...` (refer to
the virtualList usage and ui.empty call). Likewise replace the selected-item
ternary that renders ui.column(...) vs ui.empty(...) with maybe(selected, (item)
=> ui.column(...)) ?? ui.empty(...) so the selected rendering uses the helper
(refer to the selected variable, ui.column, ui.slider, ui.field and the each
block). Ensure the top-level imports include maybe/show from `@rezi-ui/core` and
leave other symbols (virtualList, eachInline, each) intact.
| ui.field({ | ||
| label: "Bay Assignment", | ||
| children: ui.select({ | ||
| id: "cargo-bay-select", | ||
| value: String(selected.bay), | ||
| options: Array.from({ length: 12 }, (_, index) => ({ | ||
| value: String(index + 1), | ||
| label: `Bay ${index + 1}`, | ||
| })), | ||
| onChange: () => {}, | ||
| }), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -path "*/packages/create-rezi/templates/starship/src/screens/cargo.ts" -type fRepository: RtlZeroMemory/Rezi
Length of output: 124
🏁 Script executed:
cat -n packages/create-rezi/templates/starship/src/screens/cargo.ts | head -250Repository: RtlZeroMemory/Rezi
Length of output: 11314
🏁 Script executed:
# Search for reducer setup and action type definitions in the template
rg -A 5 "type.*Action|useReducer|dispatch" packages/create-rezi/templates/starship/src/ --max-count=20Repository: RtlZeroMemory/Rezi
Length of output: 47798
🏁 Script executed:
# Check the StarshipAction type definition and look for bay-related actions
rg -A 50 "type StarshipAction" packages/create-rezi/templates/starship/src/types.tsRepository: RtlZeroMemory/Rezi
Length of output: 2877
🏁 Script executed:
# Search for any existing "bay" references in action handlers or reducer
rg "bay" packages/create-rezi/templates/starship/src/ -iRepository: RtlZeroMemory/Rezi
Length of output: 1027
🏁 Script executed:
# Check for reducer implementation
fd "*.ts" packages/create-rezi/templates/starship/src/helpers/ -type fRepository: RtlZeroMemory/Rezi
Length of output: 232
🏁 Script executed:
# Find reducer and state helpers
find packages/create-rezi/templates/starship/src/helpers -name "*.ts" -type fRepository: RtlZeroMemory/Rezi
Length of output: 321
🏁 Script executed:
# Check the reducer implementation for cargo-related actions
rg -A 3 "set-cargo-priority|select-cargo" packages/create-rezi/templates/starship/src/helpers/Repository: RtlZeroMemory/Rezi
Length of output: 834
🏁 Script executed:
# View the reducer implementation for cargo actions
rg -B 2 -A 10 "set-cargo-priority|select-cargo" packages/create-rezi/templates/starship/src/helpers/state.tsRepository: RtlZeroMemory/Rezi
Length of output: 669
🏁 Script executed:
# Check if there's any set-cargo-bay action handler in the reducer
rg "set-cargo-bay" packages/create-rezi/templates/starship/src/Repository: RtlZeroMemory/Rezi
Length of output: 44
Wire "Bay Assignment" select to state — current onChange is a no-op.
onChange: () => {} makes the control feel broken in an otherwise "full integration" template. Add a set-cargo-bay action to the StarshipAction union type (in types.ts), implement its reducer case (in helpers/state.ts), and dispatch it from the select:
Changes required (cargo.ts, types.ts, state.ts)
cargo.ts — replace the onChange handler:
onChange: () => {},
+onChange: (value) =>
+ deps.dispatch({
+ type: "set-cargo-bay",
+ cargoId: selected.id,
+ bay: Number(value),
+ }),types.ts — add to StarshipAction union:
+ | Readonly<{ type: "set-cargo-bay"; cargoId: string; bay: number }>state.ts — add reducer case (similar to set-cargo-priority):
+if (action.type === "set-cargo-bay") {
+ const cargo = Object.freeze(
+ state.cargo.map((item) => {
+ if (item.id !== action.cargoId) return item;
+ return Object.freeze({ ...item, bay: clampInt(action.bay, 1, 12) });
+ }),
+ );
+ return freezeState({ ...state, cargo });
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/cargo.ts` around lines
208 - 218, The Bay Assignment select in cargo.ts currently has a no-op onChange;
wire it to state by dispatching a new action: add a "set-cargo-bay" variant to
the StarshipAction union in types.ts, implement a reducer branch for
"set-cargo-bay" in helpers/state.ts that updates the cargo bay (mirror the
existing set-cargo-priority reducer logic), and change the ui.select (id
"cargo-bay-select", value derived from selected.bay) onChange to dispatch the
set-cargo-bay action with the newly selected bay value.
| ui.actions([ | ||
| ui.button({ | ||
| id: ctx.id("crew-save"), | ||
| label: "Save Assignment", | ||
| intent: "primary", | ||
| onPress: () => { | ||
| if (!staffingError) { | ||
| props.dispatch({ | ||
| type: "assign-crew", | ||
| crewId: member.id, | ||
| department: props.state.crewDraft.department, | ||
| status: props.state.crewDraft.status, | ||
| }); | ||
| } | ||
| }, | ||
| }), | ||
| ui.button({ | ||
| id: ctx.id("crew-cancel"), | ||
| label: "Cancel", | ||
| intent: "secondary", | ||
| onPress: () => props.dispatch({ type: "toggle-crew-editor" }), | ||
| }), |
There was a problem hiding this comment.
Swap Cancel/Save order in Assignment Editor actions.
Cancel should precede the primary Save button to match the form action order guideline.
🔧 Suggested change
ui.actions([
- ui.button({
- id: ctx.id("crew-save"),
- label: "Save Assignment",
- intent: "primary",
- onPress: () => {
- if (!staffingError) {
- props.dispatch({
- type: "assign-crew",
- crewId: member.id,
- department: props.state.crewDraft.department,
- status: props.state.crewDraft.status,
- });
- }
- },
- }),
ui.button({
id: ctx.id("crew-cancel"),
label: "Cancel",
intent: "secondary",
onPress: () => props.dispatch({ type: "toggle-crew-editor" }),
}),
+ ui.button({
+ id: ctx.id("crew-save"),
+ label: "Save Assignment",
+ intent: "primary",
+ onPress: () => {
+ if (!staffingError) {
+ props.dispatch({
+ type: "assign-crew",
+ crewId: member.id,
+ department: props.state.crewDraft.department,
+ status: props.state.crewDraft.status,
+ });
+ }
+ },
+ }),
]),As per coding guidelines: “End forms with ui.actions([cancelBtn, submitBtn]) — cancel first, primary submit last.”
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ui.actions([ | |
| ui.button({ | |
| id: ctx.id("crew-save"), | |
| label: "Save Assignment", | |
| intent: "primary", | |
| onPress: () => { | |
| if (!staffingError) { | |
| props.dispatch({ | |
| type: "assign-crew", | |
| crewId: member.id, | |
| department: props.state.crewDraft.department, | |
| status: props.state.crewDraft.status, | |
| }); | |
| } | |
| }, | |
| }), | |
| ui.button({ | |
| id: ctx.id("crew-cancel"), | |
| label: "Cancel", | |
| intent: "secondary", | |
| onPress: () => props.dispatch({ type: "toggle-crew-editor" }), | |
| }), | |
| ui.actions([ | |
| ui.button({ | |
| id: ctx.id("crew-cancel"), | |
| label: "Cancel", | |
| intent: "secondary", | |
| onPress: () => props.dispatch({ type: "toggle-crew-editor" }), | |
| }), | |
| ui.button({ | |
| id: ctx.id("crew-save"), | |
| label: "Save Assignment", | |
| intent: "primary", | |
| onPress: () => { | |
| if (!staffingError) { | |
| props.dispatch({ | |
| type: "assign-crew", | |
| crewId: member.id, | |
| department: props.state.crewDraft.department, | |
| status: props.state.crewDraft.status, | |
| }); | |
| } | |
| }, | |
| }), | |
| ]), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/crew.ts` around lines 200
- 221, The actions array currently renders the Save button before Cancel; update
the ui.actions(...) call in the crew editor so the cancel action (the button
with id ctx.id("crew-cancel") / onPress dispatching { type: "toggle-crew-editor"
}) comes first and the primary Save action (the button with id
ctx.id("crew-save" / onPress dispatching { type: "assign-crew", crewId:
member.id, ... })) is last, preserving all props/handlers but swapping their
order to follow the cancel-first, submit-last guideline.
| import type { | ||
| CommandItem, | ||
| CommandSource, | ||
| RegisteredBinding, | ||
| RouteRenderContext, | ||
| VNode, | ||
| } from "@rezi-ui/core"; | ||
| import { routerBreadcrumb, routerTabs, ui } from "@rezi-ui/core"; | ||
| import { alertLabel } from "../helpers/formatters.js"; | ||
| import { systemHealth } from "../helpers/state.js"; | ||
| import { alertBadgeVariant, PRODUCT_NAME, PRODUCT_TAGLINE, stylesForTheme, themeSpec } from "../theme.js"; | ||
| import { toCoreToast, type RouteDeps, type RouteId, type StarshipState } from "../types.js"; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "shell.ts" | grep -E "packages/create-rezi/templates/starship"Repository: RtlZeroMemory/Rezi
Length of output: 124
🏁 Script executed:
if [ -f "packages/create-rezi/templates/starship/src/screens/shell.ts" ]; then
wc -l "packages/create-rezi/templates/starship/src/screens/shell.ts"
fiRepository: RtlZeroMemory/Rezi
Length of output: 126
🏁 Script executed:
# Read the file around lines 142-148 to see the Route Health panel code
if [ -f "packages/create-rezi/templates/starship/src/screens/shell.ts" ]; then
sed -n '135,155p' "packages/create-rezi/templates/starship/src/screens/shell.ts"
fiRepository: RtlZeroMemory/Rezi
Length of output: 815
🏁 Script executed:
# Search for all occurrences of .map used in list rendering in this file
rg -n "\.map\(" "packages/create-rezi/templates/starship/src/screens/shell.ts" -A 3Repository: RtlZeroMemory/Rezi
Length of output: 1021
🏁 Script executed:
# Verify if eachInline is exported from `@rezi-ui/core`
rg -l "export.*eachInline" --type ts --type tsxRepository: RtlZeroMemory/Rezi
Length of output: 89
🏁 Script executed:
# Search for eachInline definition or export in the codebase
rg "eachInline" --type ts -lRepository: RtlZeroMemory/Rezi
Length of output: 272
🏁 Script executed:
# Check if eachInline is mentioned in the package exports
rg "eachInline" packages/core/src --type ts -A 2Repository: RtlZeroMemory/Rezi
Length of output: 1540
🏁 Script executed:
# Check the main export file
if [ -f "packages/core/src/index.ts" ]; then
cat -n "packages/core/src/index.ts" | head -100
fiRepository: RtlZeroMemory/Rezi
Length of output: 3325
🏁 Script executed:
# Find where eachInline is exported in index.ts
rg "eachInline" "packages/core/src/index.ts" -B 2 -A 2Repository: RtlZeroMemory/Rezi
Length of output: 138
🏁 Script executed:
# Check the collections.ts file to understand the eachInline signature and usage
cat -n "packages/core/src/widgets/collections.ts" | head -80Repository: RtlZeroMemory/Rezi
Length of output: 2278
🏁 Script executed:
# Let's also check the current imports in the shell.ts file
sed -n '1,15p' "packages/create-rezi/templates/starship/src/screens/shell.ts"Repository: RtlZeroMemory/Rezi
Length of output: 605
🏁 Script executed:
# Verify if eachInline has any function overloads
rg -A 10 "export function eachInline" "packages/core/src/widgets/collections.ts"Repository: RtlZeroMemory/Rezi
Length of output: 392
Use eachInline() for Route Health list rendering with proper options parameter.
The list is rendered via map despite the list-rendering guideline. Switch to eachInline() for consistent list rendering. Note: eachInline requires three parameters—the key function must be passed in the options object, not embedded in the ui.row props.
♻️ Suggested change
-import { routerBreadcrumb, routerTabs, ui } from "@rezi-ui/core";
+import { eachInline, routerBreadcrumb, routerTabs, ui } from "@rezi-ui/core";
@@
ui.panel("Route Health", [
- ...options.deps.routes.map((route) =>
+ ...eachInline(options.deps.routes, (route) =>
ui.row({ key: `route-health-${route.id}`, gap: 1, items: "center" }, [
ui.status(route.id === currentRoute ? "online" : "away", { showLabel: false }),
ui.text(route.title, { variant: "label" }),
]),
+ { key: (route) => `route-health-${route.id}` },
),
]),As per coding guidelines: "Use each() / eachInline() for list rendering with keys."
Also applies to: 142-148
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/create-rezi/templates/starship/src/screens/shell.ts` around lines 1
- 13, The Route Health list is being rendered with .map; replace that with
ui.eachInline (or eachInline) passing the items (systemHealth), a renderer
function, and an options object that contains the key function (e.g., { key: h
=> h.id }) instead of embedding the key in ui.row props; update the occurrences
around the Route Health rendering (including the block referencing systemHealth
and the lines noted ~142-148) so the key is provided as the third parameter to
eachInline and the renderer only returns the row vnode.
Summary
Validation
Summary by CodeRabbit