Skip to content

feat(core,node,native): app.printAbove + app.setInlineRows (scrollback commits)#417

Merged
RtlZeroMemory merged 3 commits into
mainfrom
feat/print-above
Jun 11, 2026
Merged

feat(core,node,native): app.printAbove + app.setInlineRows (scrollback commits)#417
RtlZeroMemory merged 3 commits into
mainfrom
feat/print-above

Conversation

@RtlZeroMemory

Copy link
Copy Markdown
Owner

Summary

Platform work for the upcoming pi-style coding-agent TUI: exposes Zireael's new scrollback-commit primitive end-to-end as app.printAbove(view), plus runtime inline viewport resizing as app.setInlineRows(rows).

// Commit finished content into terminal history above the live region
await app.printAbove(ui.text("[sync] checkpoint reached: 50%"));
// Grow/shrink the live region at runtime
await app.setInlineRows(12);

This is the Ink <Static> / Bubble Tea Println pattern: committed blocks become ordinary terminal scrollback (they scroll with the session and survive app exit) while the live region keeps rendering below.

Depends on RtlZeroMemory/Zireael#112 (vendored at its branch head ca3e6b5; ⚠️ repin submodule + VENDOR_COMMIT.txt to the squash SHA once that PR merges, before merging this one).

What changed

Native (@rezi-ui/native)

  • engineCommitScrollback(engineId, drawlist, rows) NAPI binding + FFI extern.
  • Fixed the hand-maintained index.js export list — NAPI-RS regenerates index.d.ts but not this file, so the new symbol was silently undefined through the package entry (found via real-terminal testing; worth a future codegen guard).

Core (@rezi-ui/core)

  • renderViewForScrollback(): one-off widget-tree → ZRDL bytes at (viewport cols × measured rows), mirroring the testkit pipeline (commit → layout → renderToDrawlist) with auto height via measure() (wrapping and stack gaps included), clamped to the engine's 1024-row bound.
  • App.printAbove(view, { rows? }) + App.setInlineRows(rows) with lifecycle guards; optional RuntimeBackend.commitScrollback/setInlineRows so other backends are unaffected; inspector-overlay wrapper delegates added.
  • Requested engine ABI → 1.4.0.

Node (@rezi-ui/node)

  • New commitScrollback worker message with an rc-carrying commitResult ack — commit failures (e.g. staged-rows backpressure ZR_ERR_LIMIT) surface as rejected promises rather than the fatal path used by setConfig.
  • setInlineRows resends the derived runtime-config baseline (created limits/plat/toggles) with only inlineRows changed — required because engine_set_config validates the full surface and rejects plat drift.
  • Both worker and in-process execution paths implemented; client-side validation (ZRUI_INVALID_PROPS bounds, ZRUI_BACKEND_ERROR outside inline screen mode).

Example + docs

  • examples/inline-status: prints checkpoint lines above at each progress quarter, +/- resize the region live.
  • Guide sections (printing into scrollback, runtime resizing), backend doc, CLAUDE.md.

Test evidence

  • 5093/5093 suite green (14 new): pure render-helper coverage (ZRDL magic/cmds, measured heights incl. wrapping + column gaps, bounds), backend round-trips on both execution paths via a strict expectation shim (the shim only accepts inlineRows=5 + plat.screenMode=1, so a resolving call proves payload integrity), engine-rejection surfacing, alt-mode rejection.
  • Lint/typecheck/docs build/e2e/templates green; vendor integrity + unicode sync green.
  • Real terminal (tmux): four printAbove checkpoints stacked in scrollback above the live Rezi panel during the run, surviving exit with the prompt below; +/- resized the region live in both directions; zero stderr errors.

Compatibility

  • Both APIs throw ZRUI_BACKEND_ERROR unless the app was created with screen.mode: "inline"; backends without the methods are unaffected (optional interface members).
  • Requires the vendored Zireael ABI 1.4.0 (exact-match negotiation).

🤖 Generated with Claude Code

…crollback commits)

Exposes the engine's scrollback-commit primitive (Zireael ABI 1.4.0) as
first-class app APIs for agent-style inline CLIs:

- app.printAbove(view, { rows? }): render a standalone widget tree at the
  current viewport width (rows auto-measured, clamped to the engine's 1024)
  and commit it into terminal scrollback above the live region. Committed
  blocks become ordinary terminal history and survive app exit.
- app.setInlineRows(rows): change the inline viewport height at runtime;
  the engine clamps to the terminal and emits a resize so layout follows.

Plumbing:
- vendor: Zireael bumped to the scrollback-commit build (ca3e6b5; repin to
  the squash SHA after RtlZeroMemory/Zireael#112 merges)
- native: engineCommitScrollback NAPI binding + FFI extern; fixed the
  hand-maintained index.js export list (the silent-undefined trap)
- core: renderViewForScrollback helper (measure -> commit -> layout ->
  renderToDrawlist, mirroring the testkit pipeline); optional
  RuntimeBackend.commitScrollback/setInlineRows; requested ABI 1.4.0
- node: commitScrollback worker message with rc-carrying commitResult ack
  (commit failures are recoverable backpressure, not fatal); runtime
  config baseline derivation so setConfig resends created limits/plat with
  only inlineRows changed; both worker and in-process execution paths
- example: inline-status prints checkpoints above and resizes on +/-

Tests: 14 new (pure render helper incl. measured wrapping/gap heights;
backend commit/resize round-trips on both paths via a strict expectation
shim; engine-rejection surfacing; alt-mode and bounds validation).
5093/5093 suite green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added app.printAbove(view, opts?) to render and commit content into terminal scrollback above the inline region.
    • Added app.setInlineRows(rows) to dynamically resize the inline viewport height at runtime.
  • Documentation

    • Updated guides and API documentation for inline screen mode capabilities.
    • Enhanced examples demonstrating scrollback commit and dynamic resizing workflows.
  • Version Updates

    • Bumped engine ABI to 1.4.0 and library to 1.5.0.

Walkthrough

This PR adds inline-screen scrollback commit and dynamic region resize capabilities. It introduces app.printAbove(view) to render content into scrollback and app.setInlineRows(rows) to adjust region height. Engine ABI advances to 1.4.0 with corresponding C/Rust/Node backend implementations spanning staged commit queuing, framebuffer rendering, worker protocol messages, and integration tests.

Changes

Inline scrollback commit and resize feature

Layer / File(s) Summary
Version pins and API contracts
packages/core/src/abi.ts, packages/native/vendor/zireael/include/zr/zr_version.h, packages/native/vendor/VENDOR_COMMIT.txt, packages/native/vendor/zireael/include/zr/zr_config.h, packages/native/vendor/zireael/include/zr/zr_engine.h, packages/native/vendor/zireael/src/core/zr_diff.h
Engine ABI bumped; vendor commit updated; new limits and public engine header/API for scrollback commit with inline-only, staging, emission ordering, and no-partial-effects semantics.
Scrollback rendering pipeline
packages/core/src/app/printAbove.ts, packages/core/src/app/__tests__/printAbove.test.ts
renderViewForScrollback validates cols/rows, measures or applies override, commits/layouts VNode tree, renders drawlist bytes, and returns { bytes, rows }. Tests cover successes and invalid-input errors.
App instance APIs and backend interface
packages/core/src/app/types.ts, packages/core/src/app/createApp.ts, packages/core/src/app/inspectorOverlayHelper.ts, packages/core/src/backend.ts
App adds printAbove and setInlineRows methods with lifecycle/guard checks; inspector overlay delegates; RuntimeBackend interface gains optional commitScrollback and setInlineRows.
Native FFI, N-API, and C engine implementation
packages/native/index.d.ts, packages/native/index.js, packages/native/src/ffi.rs, packages/native/src/lib.rs, packages/native/vendor/zireael/src/core/zr_diff.c, packages/native/vendor/zireael/src/core/zr_engine.c, packages/native/vendor/zireael/src/core/zr_engine_present.inc
N-API/FFI binding and JS export for engineCommitScrollback, Rust wrapper, C engine staging and per-block execution, zr_diff_render_commit for committing rows, present-time emission of staged commits, and cleanup of pending blocks.
Worker protocol and shared utilities
packages/node/src/worker/protocol.ts, packages/node/src/backend/backendSharedConfig.ts
Adds commitScrollback/commitResult message types and utilities deriveRuntimeConfigBase, isInlineScreenNativeConfig, and validateInlineRowsOrThrow.
Node backend: worker handlers and implementations
packages/node/src/worker/engineWorker.ts, packages/node/src/worker/engineWorker/messageHandlers.ts, packages/node/src/worker/engineWorker/shared.ts, packages/node/src/backend/nodeBackend.ts, packages/node/src/backend/nodeBackendInline.ts
Worker handler calls native engineCommitScrollback; backends detect inline mode, validate rows, track commitWaiters, and expose commitScrollback and setInlineRows methods.
Test shim and integration test
packages/node/src/__tests__/worker/testShims/commitNative.ts, packages/node/src/__tests__/scrollback_commit.test.ts
In-memory native shim validates drawlist magic and inline config; tests verify commit success, backend error propagation as ZrUiError ZRUI_BACKEND_ERROR, setInlineRows behavior, invalid-row rejection, and alt-screen rejections.
Documentation and example usage
CLAUDE.md, docs/backend/node.md, docs/guide/screen-modes.md, examples/inline-status/src/index.ts
Docs updated to describe app.printAbove and app.setInlineRows (ABI 1.4.0+); example prints periodic checkpoints into scrollback and lets the user resize the inline region at runtime.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • RtlZeroMemory/Rezi#416: Introduces inline screen mode plumbing that this PR depends on for runtime inline operations.

Poem

🐰 I stitched some rows into scrollback's seam,

checkpoints hop above the live stream.
Keys press, the inline meadow grows or shrinks,
ABI bumped — the terminal blinks.
A rabbit's cheer for rows and commits!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: introducing two new app APIs (printAbove and setInlineRows) for scrollback commits and inline viewport resizing.
Description check ✅ Passed The description is directly related to the changeset, providing context about the scrollback-commit primitive, implementation details across multiple packages, test coverage, and compatibility notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/print-above

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/guide/screen-modes.md (1)

113-114: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix ABI version mismatch in the guide.

Line 113 says engine ABI 1.3.0, but this PR introduces/depends on ABI 1.4.0. Please align this line to avoid contradictory integration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guide/screen-modes.md` around lines 113 - 114, Update the documentation
line describing Inline mode to reflect the correct engine ABI version used by
this PR: change the ABI from "1.3.0" to "1.4.0" so the sentence reads that
Zireael engine (v1.4.0+, engine ABI 1.4.0) implements Inline mode; ensure the
phrase in the Inline mode description that references "engine ABI" and the
version number is updated consistently to 1.4.0.
packages/node/src/backend/nodeBackend.ts (1)

231-245: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle commitWaiters in every teardown path.

The new queue is drained in failAll() and rejectPending(), but dispose() still leaves in-flight commitScrollback() promises unresolved. Disposing the backend mid-commit now hangs callers waiting on that promise.

Suggested fix
     dispose(): void {
       if (disposed) return;
       disposed = true;
@@
       const err = new Error("NodeBackend: disposed");
       while (eventWaiters.length > 0) eventWaiters.shift()?.reject(err);
       while (capsWaiters.length > 0) capsWaiters.shift()?.reject(err);
+      while (commitWaiters.length > 0) commitWaiters.shift()?.reject(err);
       eventQueue.length = 0;
       rejectFrameWaiters(frameTracking, frameAudit, err);
       rejectDebugWaiters(debugChannel, err);

Also applies to: 259-263

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/node/src/backend/nodeBackend.ts` around lines 231 - 245, The
teardown logic misses rejecting pending commitScrollback() promises: ensure
commitWaiters are rejected wherever other waiters are (failAll(),
rejectPending(), and dispose()) so in-flight commitScrollback() callers don't
hang; update dispose() and any early-return/error teardown paths to iterate
commitWaiters (like you do for eventWaiters and capsWaiters) and call
.reject(err) (or .reject(new Error("disposed"))) on each Deferred, and clear
commitWaiters and eventQueue after rejecting.
🧹 Nitpick comments (3)
packages/node/src/__tests__/worker/testShims/commitNative.ts (1)

1-3: ⚡ Quick win

Use interface for object-shape declarations in this TS file.

On Line 1 and Lines 27-33, object shapes are declared with type aliases (EngineState, RuntimeCfg, PlatCfg). Please switch these to interface to match repo standards.

Also applies to: 27-33

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/node/src/__tests__/worker/testShims/commitNative.ts` around lines 1
- 3, Replace the three object-shape type aliases with equivalent interfaces:
change the `type EngineState = Readonly<{ destroyed: boolean }>` to `interface
EngineState { readonly destroyed: boolean }`, and convert `type RuntimeCfg =
Readonly<...>` and `type PlatCfg = Readonly<...>` to `interface RuntimeCfg { ...
}` and `interface PlatCfg { ... }` respectively, retaining all property names
and readonly modifiers (use `readonly` for properties instead of
`Readonly<...>`). Update any local references if necessary to the interface
names (EngineState, RuntimeCfg, PlatCfg) so the rest of the file still compiles.

Source: Coding guidelines

packages/node/src/__tests__/scrollback_commit.test.ts (1)

52-58: ⚡ Quick win

Avoid asserting internal error-message wording as primary evidence.

On Lines 56-57, checking for "engine_commit_scrollback failed" ties the test to implementation phrasing. Prefer stable contract evidence (error type/code and operation context) to keep the test behavior-first.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/node/src/__tests__/scrollback_commit.test.ts` around lines 52 - 58,
The test currently asserts the rejected error by matching an internal message
substring ("engine_commit_scrollback failed"); update the assert.rejects
predicate to only check the error type and stable error code: ensure the
rejected error is an instance of ZrUiError and has err.code ===
"ZRUI_BACKEND_ERROR" (remove the err.message.includes(...) check). Keep the call
under test (backend.commitScrollback?.(makeClearDrawlist(), 99)) and the use of
assert.rejects unchanged, but simplify the predicate to only verify ZrUiError
and the error code.

Source: Coding guidelines

packages/core/src/app/printAbove.ts (1)

20-23: ⚡ Quick win

Use an interface for this exported object shape.

At Line 20, PrintAboveRender is an object-shape alias; repository standards prefer interface for this case.

♻️ Proposed change
-export type PrintAboveRender = Readonly<{
-  bytes: Uint8Array;
-  rows: number;
-}>;
+export interface PrintAboveRender {
+  readonly bytes: Uint8Array;
+  readonly rows: number;
+}
As per coding guidelines, `**/*.{ts,tsx}`: "Prefer `interface` for defining object shapes in TypeScript files".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/app/printAbove.ts` around lines 20 - 23, Replace the
exported type alias PrintAboveRender with an exported interface to follow repo
style: declare export interface PrintAboveRender { readonly bytes: Uint8Array;
readonly rows: number; } — keep the same property names and readonly modifiers
so runtime shapes and usages (e.g., any code referencing PrintAboveRender)
remain compatible; update any local references if needed to use the interface
name (no other behavior changes).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/inline-status/src/index.ts`:
- Around line 79-86: The handlers mutate inlineRows before the async call so
local state can drift if app.setInlineRows fails; change both handlers to
compute a nextRows local variable (e.g., nextRows = Math.min(16, inlineRows + 1)
and nextRows = Math.max(5, inlineRows - 1)), call app.setInlineRows(nextRows)
and only assign inlineRows = nextRows after that promise resolves (or in .then),
and handle rejection without changing inlineRows (optionally log or show the
error) so the local value stays consistent with the backend.

In `@packages/core/src/app/createApp.ts`:
- Around line 1019-1051: printAbove and setInlineRows call backend methods while
stop() may be tearing down (state still "Running") and they skip the module's
in-render/in-commit re-entrancy protections; fix by acquiring the same
lifecycle/re-entrancy guard used elsewhere before calling
backend.commitScrollback or backend.setInlineRows (e.g. enter the in-render /
in-commit guard), re-check that the app is still operational after acquiring the
guard, and only then invoke commitFn(rendered...) or setFn(rows); if the guard
indicates shutdown/in-progress, bail with the existing guards.throwCode path
instead. Ensure you reference the existing symbols printAbove, setInlineRows,
commitFn (backend.commitScrollback), setFn (backend.setInlineRows),
guards.assertOperational and sm.assertOneOf when applying the guard.

In `@packages/node/src/__tests__/scrollback_commit.test.ts`:
- Around line 34-44: The test bodies currently call backend.start() then perform
assertions and call backend.stop()/backend.dispose() only on the happy path;
wrap each test body (e.g., the test that uses createNodeBackendInternal and
calls backend.start(), backend.commitScrollback, etc.) in a try/finally so that
backend.stop() and backend.dispose() are always executed in the finally block;
apply the same change to the other tests mentioned (the blocks covering lines
46-61, 63-76, 78-90, 93-111) to guarantee teardown even if assertions fail.

In `@packages/node/src/backend/nodeBackend.ts`:
- Around line 207-210: The runtimeConfigBase is derived too early (before
start() resolves widthPolicy), causing setInlineRows() to resend a stale
baseline; move the call to deriveRuntimeConfigBase so it runs after start() has
completed and the actual widthPolicy is available (i.e., compute
runtimeConfigBase only after start() resolves), update any code paths that call
setInlineRows() to use the post-start runtimeConfigBase, and apply the same
change for the second occurrence that uses deriveRuntimeConfigBase (the block
around the other setInlineRows()/start() pair referenced in the comment).
- Around line 883-896: commitScrollback currently sends a backend request
without validating the rows argument locally; call
validateInlineRowsOrThrow(rows) at the start of commitScrollback (same
validation used by setInlineRows) before creating the ArrayBuffer, adding the
deferred, or calling send so invalid row values raise ZRUI_INVALID_PROPS
immediately instead of after a worker round-trip.
- Around line 883-896: In commitScrollback, a deferred (d) is pushed into
commitWaiters before calling send, so if send (worker.postMessage) throws
synchronously the stale waiter remains; change the flow to either push d only
after send succeeds or wrap send(...) in try/catch and on synchronous throw
remove the queued waiter (commitWaiters) and rethrow the error; refer to
commitScrollback, the local deferred d, commitWaiters, and send to locate the
change.

---

Outside diff comments:
In `@docs/guide/screen-modes.md`:
- Around line 113-114: Update the documentation line describing Inline mode to
reflect the correct engine ABI version used by this PR: change the ABI from
"1.3.0" to "1.4.0" so the sentence reads that Zireael engine (v1.4.0+, engine
ABI 1.4.0) implements Inline mode; ensure the phrase in the Inline mode
description that references "engine ABI" and the version number is updated
consistently to 1.4.0.

In `@packages/node/src/backend/nodeBackend.ts`:
- Around line 231-245: The teardown logic misses rejecting pending
commitScrollback() promises: ensure commitWaiters are rejected wherever other
waiters are (failAll(), rejectPending(), and dispose()) so in-flight
commitScrollback() callers don't hang; update dispose() and any
early-return/error teardown paths to iterate commitWaiters (like you do for
eventWaiters and capsWaiters) and call .reject(err) (or .reject(new
Error("disposed"))) on each Deferred, and clear commitWaiters and eventQueue
after rejecting.

---

Nitpick comments:
In `@packages/core/src/app/printAbove.ts`:
- Around line 20-23: Replace the exported type alias PrintAboveRender with an
exported interface to follow repo style: declare export interface
PrintAboveRender { readonly bytes: Uint8Array; readonly rows: number; } — keep
the same property names and readonly modifiers so runtime shapes and usages
(e.g., any code referencing PrintAboveRender) remain compatible; update any
local references if needed to use the interface name (no other behavior
changes).

In `@packages/node/src/__tests__/scrollback_commit.test.ts`:
- Around line 52-58: The test currently asserts the rejected error by matching
an internal message substring ("engine_commit_scrollback failed"); update the
assert.rejects predicate to only check the error type and stable error code:
ensure the rejected error is an instance of ZrUiError and has err.code ===
"ZRUI_BACKEND_ERROR" (remove the err.message.includes(...) check). Keep the call
under test (backend.commitScrollback?.(makeClearDrawlist(), 99)) and the use of
assert.rejects unchanged, but simplify the predicate to only verify ZrUiError
and the error code.

In `@packages/node/src/__tests__/worker/testShims/commitNative.ts`:
- Around line 1-3: Replace the three object-shape type aliases with equivalent
interfaces: change the `type EngineState = Readonly<{ destroyed: boolean }>` to
`interface EngineState { readonly destroyed: boolean }`, and convert `type
RuntimeCfg = Readonly<...>` and `type PlatCfg = Readonly<...>` to `interface
RuntimeCfg { ... }` and `interface PlatCfg { ... }` respectively, retaining all
property names and readonly modifiers (use `readonly` for properties instead of
`Readonly<...>`). Update any local references if necessary to the interface
names (EngineState, RuntimeCfg, PlatCfg) so the rest of the file still compiles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b9c70594-96c5-4905-870e-59f64ac5520d

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6d9f3 and 2668a07.

📒 Files selected for processing (33)
  • CLAUDE.md
  • docs/backend/node.md
  • docs/guide/screen-modes.md
  • examples/inline-status/src/index.ts
  • packages/core/src/abi.ts
  • packages/core/src/app/__tests__/printAbove.test.ts
  • packages/core/src/app/createApp.ts
  • packages/core/src/app/inspectorOverlayHelper.ts
  • packages/core/src/app/printAbove.ts
  • packages/core/src/app/types.ts
  • packages/core/src/backend.ts
  • packages/native/index.d.ts
  • packages/native/index.js
  • packages/native/src/ffi.rs
  • packages/native/src/lib.rs
  • packages/native/vendor/VENDOR_COMMIT.txt
  • packages/native/vendor/zireael/include/zr/zr_config.h
  • packages/native/vendor/zireael/include/zr/zr_engine.h
  • packages/native/vendor/zireael/include/zr/zr_version.h
  • packages/native/vendor/zireael/src/core/zr_diff.c
  • packages/native/vendor/zireael/src/core/zr_diff.h
  • packages/native/vendor/zireael/src/core/zr_engine.c
  • packages/native/vendor/zireael/src/core/zr_engine_present.inc
  • packages/node/src/__tests__/scrollback_commit.test.ts
  • packages/node/src/__tests__/worker/testShims/commitNative.ts
  • packages/node/src/backend/backendSharedConfig.ts
  • packages/node/src/backend/nodeBackend.ts
  • packages/node/src/backend/nodeBackendInline.ts
  • packages/node/src/worker/engineWorker.ts
  • packages/node/src/worker/engineWorker/messageHandlers.ts
  • packages/node/src/worker/engineWorker/shared.ts
  • packages/node/src/worker/protocol.ts
  • vendor/zireael

Comment thread examples/inline-status/src/index.ts
Comment thread packages/core/src/app/createApp.ts
Comment thread packages/node/src/__tests__/scrollback_commit.test.ts
Comment thread packages/node/src/backend/nodeBackend.ts Outdated
Comment thread packages/node/src/backend/nodeBackend.ts
- core: printAbove/setInlineRows gain lifecycle-idle and in-commit/
  in-render re-entrancy guards (mirroring update())
- node: setInlineRows derives the runtime baseline from the START-resolved
  config (width policy settles during start), stripping Node-transport keys
  (fpsCap/maxEventBytes/frameTransport) so engine_set_config strict
  validation cannot reject the resend; commitScrollback validates rows
  client-side on both paths
- tests: guaranteed backend teardown via try/finally in all bodies
- example: inlineRows updates commit only after setInlineRows resolves

Re-verified in tmux: 4 printAbove checkpoints, live +/- resize, no stderr.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@RtlZeroMemory

Copy link
Copy Markdown
Owner Author

All five review findings addressed in the latest commit: lifecycle/re-entrancy guards on both app APIs, runtime baseline now derived from the start-resolved config (width policy preserved; Node-transport keys stripped), client-side rows validation on commitScrollback for both paths, try/finally teardown in every test body, and the example commits local inlineRows only after the backend resolves. Suite 5093/5093 green; tmux re-verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
examples/inline-status/src/index.ts (2)

24-35: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Separate side effects from view component lifecycle.

The SyncPanel component triggers side effects by calling maybePrintCheckpoint (which mutates module-level lastCheckpoint and invokes app.printAbove) from within the useInterval callback. This violates the guideline to keep view functions pure without side effects or external dependencies.

Consider refactoring to handle checkpoint printing outside the component—for example, by tracking progress in app state and using a separate observer or lifecycle hook to call app.printAbove.

As per coding guidelines: "Keep view functions pure without side effects or external dependencies".

Also applies to: 48-50

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/inline-status/src/index.ts` around lines 24 - 35,
maybePrintCheckpoint and the module-level lastCheckpoint are causing side
effects from inside the SyncPanel's useInterval callback (mutating state and
calling app.printAbove); move this behavior out of the view by converting
progress updates into pure state events and performing the printing in a
separate effect/observer: update SyncPanel/useInterval to only emit or set a
progress value (e.g., via a callback or shared state) and remove direct calls to
maybePrintCheckpoint from the component; implement a dedicated effect or watcher
(outside the render path) that listens to progress changes and runs
maybePrintCheckpoint (which may keep lastCheckpoint and call app.printAbove) so
view code stays pure while side effects live in a lifecycle handler.

Source: Coding guidelines


70-70: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Wrap the root view in ui.page() to follow framework structure.

The coding guidelines require that the root view use ui.page() or ui.appShell() with at least p: 1 spacing. Currently, the view is a bare widget call. Even in inline mode, examples should demonstrate proper structural patterns.

📐 Proposed fix to add page wrapper
-app.view(() => SyncPanel({}));
+app.view(() => ui.page({ p: 1 }, [SyncPanel({})]));

As per coding guidelines: "Root view must use ui.page() or ui.appShell() with at least p: 1 spacing".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/inline-status/src/index.ts` at line 70, The root view currently
registers a bare widget via app.view(() => SyncPanel({})); update it to wrap the
root in a page or appShell per guidelines by returning ui.page(...) or
ui.appShell(...) with at least p: 1 and placing the SyncPanel widget inside that
container (i.e., change the app.view callback to return a ui.page/ui.appShell
with p: 1 containing SyncPanel({})).

Source: Coding guidelines

🧹 Nitpick comments (1)
examples/inline-status/src/index.ts (1)

48-50: ⚡ Quick win

Avoid setTimeout by computing the next value inline.

Using setTimeout(..., 0) to defer reading percentRef.current after the state update is fragile and relies on implicit timing. Instead, compute the next percent value directly and pass it to maybePrintCheckpoint:

const nextPercent = Math.min(100, percentRef.current + 1);
maybePrintCheckpoint(nextPercent);

This eliminates the timing dependency and makes the logic more explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/inline-status/src/index.ts` around lines 48 - 50, The code uses
setTimeout(..., 0) to defer reading percentRef.current before calling
maybePrintCheckpoint; replace that by computing the next percent synchronously
(e.g., const nextPercent = Math.min(100, percentRef.current + 1)) and call
maybePrintCheckpoint(nextPercent) directly so you remove the timing dependency
and make the update explicit (update the call site where setTimeout wraps
maybePrintCheckpoint and reference percentRef and maybePrintCheckpoint).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@examples/inline-status/src/index.ts`:
- Around line 24-35: maybePrintCheckpoint and the module-level lastCheckpoint
are causing side effects from inside the SyncPanel's useInterval callback
(mutating state and calling app.printAbove); move this behavior out of the view
by converting progress updates into pure state events and performing the
printing in a separate effect/observer: update SyncPanel/useInterval to only
emit or set a progress value (e.g., via a callback or shared state) and remove
direct calls to maybePrintCheckpoint from the component; implement a dedicated
effect or watcher (outside the render path) that listens to progress changes and
runs maybePrintCheckpoint (which may keep lastCheckpoint and call
app.printAbove) so view code stays pure while side effects live in a lifecycle
handler.
- Line 70: The root view currently registers a bare widget via app.view(() =>
SyncPanel({})); update it to wrap the root in a page or appShell per guidelines
by returning ui.page(...) or ui.appShell(...) with at least p: 1 and placing the
SyncPanel widget inside that container (i.e., change the app.view callback to
return a ui.page/ui.appShell with p: 1 containing SyncPanel({})).

---

Nitpick comments:
In `@examples/inline-status/src/index.ts`:
- Around line 48-50: The code uses setTimeout(..., 0) to defer reading
percentRef.current before calling maybePrintCheckpoint; replace that by
computing the next percent synchronously (e.g., const nextPercent =
Math.min(100, percentRef.current + 1)) and call
maybePrintCheckpoint(nextPercent) directly so you remove the timing dependency
and make the update explicit (update the call site where setTimeout wraps
maybePrintCheckpoint and reference percentRef and maybePrintCheckpoint).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b45066ce-80ce-42e4-8c09-a9c565332c28

📥 Commits

Reviewing files that changed from the base of the PR and between 2668a07 and 1a3a8c3.

📒 Files selected for processing (9)
  • examples/inline-status/src/index.ts
  • packages/core/src/app/createApp.ts
  • packages/native/vendor/VENDOR_COMMIT.txt
  • packages/native/vendor/zireael/src/core/zr_diff.c
  • packages/node/src/__tests__/scrollback_commit.test.ts
  • packages/node/src/backend/backendSharedConfig.ts
  • packages/node/src/backend/nodeBackend.ts
  • packages/node/src/backend/nodeBackendInline.ts
  • vendor/zireael
✅ Files skipped from review due to trivial changes (2)
  • packages/native/vendor/VENDOR_COMMIT.txt
  • vendor/zireael
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/node/src/tests/scrollback_commit.test.ts
  • packages/node/src/backend/nodeBackendInline.ts
  • packages/core/src/app/createApp.ts
  • packages/node/src/backend/backendSharedConfig.ts
  • packages/native/vendor/zireael/src/core/zr_diff.c
  • packages/node/src/backend/nodeBackend.ts

@RtlZeroMemory
RtlZeroMemory merged commit 7b920ce into main Jun 11, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant