* fix(android): correct inverted chat wheel direction
* test(android): run wheel regressions with current React Native
Provide the native-only test context with React Native’s event dispatcher for an absent JavaScript instance, retaining real scroll handling.
Co-authored-by: Seth Jeon <paseo@sethjeon.com>
---------
Co-authored-by: Seth Jeon <paseo@sethjeon.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* feat(keyboard): support hardware keyboards on iPad
Register app shortcuts as iOS UIKeyCommands so a paired hardware keyboard
drives the same bindings as web and desktop. Overlays (menus, comboboxes,
modals, command center) get first refusal on bare keys, and a focused
terminal keeps the keys it needs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(keyboard): record iOS hardware key observation spike
With a UITextView focused on iPad Pro M4/iOS 18.6, window and controller observers received Cmd+B, Cmd+P and Escape. Escape also invoked UIKeyCommand when registered, so the stream must be the sole emitter. Use observation for shortcuts and commands only for text-control ownership. Simulator intercepted Cmd+K as HID184; input capture blocked synthetic typing entirely. Keep capture off and verify the toggle via a rebind during simulator QA.
* refactor(keyboard): give overlays one key registration API
Overlay ownership now follows one layer-ordered registry on every runtime. Web retains focus trapping and restoration through its adapter; native registers the same key handler without a DOM scope. Picker navigation and dismissal no longer require a parallel native stack.
Co-authored-by: jeroenfrenken <16974549+jeroenfrenken@users.noreply.github.com>
* feat(keyboard): route native hardware keys through the shared resolver
Observe each physical press once, including native modal windows, and derive focus from declared composer and terminal surfaces. Native bindings and rebinds now use the same resolver and overlay precedence as browser and Electron input.
Co-authored-by: jeroenfrenken <16974549+jeroenfrenken@users.noreply.github.com>
* feat(keyboard): show shortcuts when hardware keyboards are present
Keep routing independent of compact layout and detect hardware capability for hints and settings. Rebind capture uses the shared overlay interface on native too. Keep the filtered sidebar model available for keyboard navigation while its panel is hidden.
Co-authored-by: Les Stroud <public@lesstroud.com>
Co-authored-by: jeroenfrenken <16974549+jeroenfrenken@users.noreply.github.com>
* test(keyboard): allow shortcut hints in compact menu assertions
Compact menus now display hardware keyboard hints. Assert action labels, order and uniqueness without treating a trailing shortcut badge as part of the action label.
* fix(keyboard): retain an overlay's opening layer in state
Capture the registry's current top when an overlay opens and retain that layer through rerenders. React Compiler removed the previous useMemo cache after the registry getter moved across modules, allowing a parent modal to rise above its child and start an infinite focus loop.
Compile this hook in Vitest as Expo does. The new hook regression failed first with layer 220 instead of 20, then passed with explicit opening state. Focused unit and browser tests pass, and the previously failing onboarding E2E completes through the real Expo compiler. Typecheck, lint and format pass.
* refactor(keyboard): let overlays own native hardware input
Remove the TextInput key handlers that duplicate declared overlay arrows and Escape in Add Project and Command Center. Remove Add Project's hidden responder now that native window sources observe keys without it. Keep soft-keyboard submit editing behavior.
* fix(keyboard): close Add Project outside its state updater
Root Escape previously called the parent close action from a React state updater, producing a cross-component render warning when navigation was still queued. Compute the previous page in the event handler and close outside the updater.
The existing Escape/Back E2E now batches navigation and dismissal through the DOM key source. It failed first with the AddProjectFlowHost render warning, then passed after the correction. Typecheck, lint and format pass; native Escape checks pass on Android and iPad.
* fix(keyboard): preserve Android dead-key composition
* fix(keyboard): recognize focused native text inputs
* test(keyboard): replace JSDOM coverage with browser tests
* refactor(menu): spell out arrow navigation branches
* fix(composer): claim fullscreen Escape on native
* refactor(composer): one fullscreen Escape module for every platform
The shared registration lived in fullscreen-exit.shared.ts with two one-line
re-exports (fullscreen-exit.ts and fullscreen-exit.native.ts). Metro needs no
native override now that the Android back button is its own adapter, so the
implementation moves into fullscreen-exit.ts and the browser test imports the
module every platform resolves.
---------
Co-authored-by: Ubuntu <jeroenfrenken@icloud.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: jeroenfrenken <16974549+jeroenfrenken@users.noreply.github.com>
* fix(terminal): render emoji at their intended width
The native grid scales glyphs to the columns assigned by the terminal buffer. Use grapheme widths across current hosts and clients so emoji fit their slots and replay preserves cursor positions.
* fix(terminal): make width regression test portable
* test(terminal): wait for replay fixture output to finish
A trailing newline could arrive after the readiness marker and change the retained history between replay captures.
Shape rows in native Canvas and CoreText, then place clusters on xterm columns so fallback font advances cannot separate text from its cursor. Wait for measured dimensions before attachment and repaint overhanging ink with affected rows.
Use Android platform ICU to avoid bundling approximately 14 MB of data; retain and document the Android 10 conjunct limitation.
* feat(app): record Android stalls in opt-in diagnostic builds
* refactor(app): own watchdog sinks and bounded log storage
* fix(app): preserve cumulative browser profiling history
* fix(android): restore development builds after Expo upgrade
Port the native touch-cancellation patch to the gesture-handler API while retaining drawing order and surviving native gestures. Match the bundled Skia font scanner and Unicode paragraph construction APIs.
* test(e2e): hide the developer error toast instead of clicking it away
The locator handler that dismissed #error-toast could match a toast that closed before its
click; dispatchEvent then waited for the gone button until the test timed out. Hiding the toast
with CSS keeps it off app actions with no race.
* test(e2e): desktop browser-tabs hides developer toasts on its attached page
* test(cli): keep the Hub deploy fixture alive on malformed requests
An empty request body crashed the HTTP fixture outside its assertions. Return HTTP 400 for invalid JSON and exercise that request before the full validation and installation assertions.
* fix(android): align Unistyles NDK version
* fix(android): use the project NDK for every native module
The diff module still selected the Android Gradle default NDK, causing a second installation after Unistyles was aligned. Inherit the project toolchain and check all configured native projects.
---------
Co-authored-by: dwyanewang <908280968@qq.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Android's scroll guard now reads the view's transform matrix, so React Native's inverted chat list (scale: -1, decomposed into a 180-degree rotation with positive scaleY) no longer scrolls on text focus or selection.
Closes#7125
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
* fix(app): restore character-paced streaming reveal
Restore the v0.8 character reveal model and remove the word-level fade implementation so streamed assistant and reasoning text remains opaque, grapheme-safe, and immediately completes when a turn ends.
* fix(android): preserve inverted timeline selection
React Native's prebuilt Android library does not consume source patches under node_modules. Register the compiled scroll manager before the core package so inverted child-reveal calculations leave focus and text selection in place while normal and explicit scrolling remain unchanged.
* feat(app): reveal streaming text as one continuous fade front
Streaming assistant text is released by word and every character fades in
over the same 150 ms envelope from a start time owned by the message. Start
times never decrease in reading order, so a line paints as a single front
moving left to right, however the text is chunked. Per-word envelopes with an
internal stagger let the next word's first letter overtake the previous word's
last letter, which read as holes and flashing words at the real frame rate;
100 ms frame strips hid it.
Native renderers receive the fade ranges as a prop on a host view wrapping the
root Text, so ranges and text land in the same Fabric mount transaction. The
earlier binding sent ranges through a separate async module call keyed by view
tag and had to reconcile them against the view's text; on a fast phone the UI
thread lags JS by several commits, mounts and calls interleave, and every
reconciliation heuristic guessed wrong. Native code no longer infers newness
or creates start times.
The mock load-test provider now separates cycles with a blank line so a cycle
heading is not appended to the previous paragraph.
* fix(ci): refresh streaming dependencies and isolate E2E setup
* test(app): distinguish fade cleanup from Markdown replacement
* test(app): detect restarted fades during Markdown updates
* docs(app): keep streaming guidance focused on constraints and QA
* test(app): control animation time in Android fade assertions
Fixed wall-clock sleeps let emulator scheduling select the sampled animation frame. Inject the animator time source and drive paint at exact timestamps within one UI-thread operation; production keeps the system clock. Verify the mid-fade sample and the exact cleanup boundary without sleeps, polling, or retries.
* fix(test): compile the desktop renderer before lifecycle checks
The lifecycle fixture treated Metro /status as app readiness. Its first
Settings click therefore spent its 90-second UI budget compiling the cold
bundle. An isolated cold-cache run restricted to one CPU reproduced the
click timeout before the app runtime had loaded.
Reuse the browser suite's existing bundle warmup before launching Electron,
and preserve Metro output in the lifecycle artifacts. The UI timeout,
build-request timeout, lifecycle assertions and product behavior are unchanged.
CI did not preserve Metro output, so its precise startup timing is not
recoverable from the failed run. The retained log makes subsequent startup
failures diagnosable without relying only on an empty renderer text dump.
Validation: three complete lifecycle E2E runs under private Xvfb displays;
existing Metro readiness tests; app typecheck; targeted lint; root format.
* test(file-observer): await burst delivery through native recovery
The deletion phase required every event within 10 seconds, but native
coalescing recovery uses a 30-second safety audit followed by the existing
reconciliation debounce. An idle diagnostic excludes that delayed audit
and therefore cannot act as a completion barrier.
Await the required delivered events instead, preserving every deletion
assertion and the existing overall test watchdog. A real-filesystem test
with controlled native notifications reproduces 95 outstanding paths at
the old deadline and verifies completion when the safety audit delivers
them. No production observer behavior changes.
* test(file-observer): verify burst recovery through public events
The controlled-clock regression constructed a private native backend,
bypassing the interface used by consumers. Consolidate recovery coverage
into the public observer integration test instead: drive real writes,
deletes and directory renames, then await every required public event,
including the 450 surviving files at their renamed paths.
No public capability was added: subscribe callbacks already express
completion. No production behavior or test timeout changes.
* test(app): drive Android fade tests through the native host
The instrumentation suite constructed TailFadeInAnimator directly, advanced
its private clock, and counted FadeInSpan instances. It now mounts the
WordFadeHost that React mounts, feeds it the ranges prop the bridge sends,
and asserts rendered pixels: hidden before a start time, a monotone front
inside and across words, settled frames equal to the baseline, and no frame
callbacks left after settling. Span counts and internal timing state are no
longer asserted.
Choreographer cannot be advanced deterministically from a test and the fade
epoch is the message's wall-clock start time, so the host takes a
WordFadeFrameClock (time plus frame callbacks) as its smallest public seam;
production keeps the Choreographer-backed default.
Fixture text is sized in pixels so every sample stays on one line at any
density; at 420 dpi the 32sp fixture wrapped and the column helper summed
glyphs from the first line.
* perf(app): optimize wrapped measurements and checkpoint native layout experiment
Compute full grapheme geometry only for final wrap fragments. Preserve shaped-text search behavior while bounding additive wrap probes. Keep the Android background layout experiment isolated from the normal diff renderer; native renderer integration and iOS bindings remain unfinished.
* perf(server): reduce cold diff generation work
Keep active diff reads owned across invalidation and wait for the initial watcher inventory before cold generation. Batch patch and blob reads while preserving per-file size isolation, bounded output, and daemon responsiveness. Avoid constructing fallback tokens when full-file highlighting already covers both sides.
* test(server): exercise oversized highlighted diff rejection
The single-line fixture now bypasses highlighting under the long-line guard, so it no longer reaches the structured payload limit. Use short dense lines to keep testing oversized rejection and continued socket usability.
* fix(server): queue fresh diffs for forced reads
A forced request following a repository mutation must not reuse an older active build. Invalidate that result and coalesce waiting callers onto one subsequent build. Give the cache a named object input and completed-value type.
* test(server): share deferred diff loader setup
* test(server): name completed diff cache setup
* refactor(server): use aliases for new diff imports
* fix(server): keep shipped diff imports resolvable by Node
The TypeScript build preserves path aliases without a runtime resolver. Restore relative imports for shipped code and declarations, and correct the server import guideline that prompted the regression.
Make provider catalogs compact and content-addressed so repeat launches can reuse the persisted snapshot instead of transferring it again. Remove eager global catalog loading and the stopped-agent defensive tail fetch, while keeping sequence-gap and reconnect recovery as the correctness paths. Add an opt-in production-ID Android profiling build for local startup traces.