Mobile
This section documents the mobile-responsive foundation for zudotext iOS and Android builds, and the web build served on mobile browsers.
Pages
Responsive Foundation — Mobile viewport contract: breakpoint token, hooks, CSS base rules, platform flags, and the
user-scalable=noaccessibility trade-offAndroid Build — Toolchain setup (Linux/WSL2 and macOS), the generated
gen/android/project, the platform-gating vocabulary, secure storage, the WebView runtime, CI, and the on-device checks still owedViewport and Scroll Trace — Enable the per-device editor trace, reproduce a mobile keyboard scroll issue, and copy evidence for an issue
Native mobile development IPC
From the repository root, use pnpm android:dev or pnpm ios:dev:simulator (macOS + Xcode). Both select tauri- explicitly so the live Vite origin has the same platform-specific plugin permissions as local app content. From tauri-app/, a direct CLI invocation also needs --config tauri.conf.mobile-dev.json, for example cargo tauri ios dev --config tauri.conf.mobile-dev.json or cargo tauri android dev --config tauri.conf.mobile-dev.json.
Only the development overlay grants IPC to the existing loopback/private IPv4 HTTP origins on port 37461, on the matching platform's main window. Keep any exact iOS IPv6 TUN-host exception in its inline dev grant in this overlay. Never add dev origins under capabilities/: Tauri scans that directory for plain builds. pnpm android:build, pnpm android:build:release, pnpm ios:build:simulator, and cargo tauri ios build omit the dev overlay and use remote-free mobile capabilities. See Android Build for the platform permissions and network setup.
Samsung IME + external keyboard + Vim investigation
Use this runbook for the Android Chrome input-state issue tracked in #7051. It targets the web editor in Chrome, not the Android Tauri WebView. The harness starts from synthetic fixture text and can receive physical-device keyboard input. The #7082 Chromium characterization uses synthetic composition; only the device run can establish Samsung Keyboard behavior.
Record the setup first
Record these values for each run so results can be compared:
Device model; Android and One UI versions; Chrome version; Samsung Keyboard version.
External keyboard connection and physical layout (for example, Bluetooth or USB and JIS or US).
Effective Vim setting and active vimrc mappings; typewriter-scrolling setting.
Compact or wide editor shell; whether the candidate strip or the full on-screen keyboard is visible.
Deployed web build SHA.
Use a disposable note containing at least 50 lines of synthetic Japanese and English Markdown, including long wrapped lines. Do not use a personal note or attach traces captured from personal notes.
Enable and export the trace
For Android and iOS, follow Viewport and Scroll Trace: enable Settings → Mobile → Record viewport and scroll trace, reproduce the issue with synthetic fixture text, copy the trace, turn recording off, and paste the clipboard contents into a local JSON file. The preference stays on this device and is not synced; only the choice is stored locally, while captured entries remain in memory and are bounded to 500. The trace contains no note text or printable key stream. A native clipboard denial is a failed export and does not create a download. On the production web editor, ?debugViewport=1 remains available as the initial opt-in and browser download fallback still works.
The fields added for this investigation include ordered monotonic event timing; key category, repeat and composition state; input type and data length; trusted versus synthetic composition events; CodeMirror selection ranges, document revision and length, and change metrics; Vim mode and useNextTextInput; change provenance (native-input, vim-command, app-effect, external-apply, or lifecycle); editor identity, lifecycle and focus; scroller detach/reattach; ghost-text composing state; scroller dimensions, viewport width, and effective Vim/typewriter settings; and redacted plugin error categories. doubleHandled marks the first key sequence observed as both a Vim command and a text insertion. Scroller detach/reattach timestamps are observation times because the browser reports the synchronous mutation after it has happened.
Isolate on the same device and keyboard
Keep the device, Chrome, and external keyboard the same as you move through this ladder. Run the same input sequence at each step and export the trace:
Open
/.ime- harness. html? layer= textarea Change the
layervalue tocm, thencm-vim, thencm-vim-app.In the real app, repeat with Vim off, then with Vim on.
The harness starts from synthetic fixture text and provides short versus long/wrapped content, plain versus decorated Markdown, typewriter-scrolling and ghost-text toggles in cm-vim-app, a document-versus-visible indicator, and an Export trace button. In the first failing CodeMirror layer, vary the fixture and Markdown controls; in cm-vim-app, also toggle typewriter scrolling and ghost text. Test the candidate strip both hidden and visible. The indicator compares rendered CodeMirror lines with the document model; Markdown decoration can change rendered text, and the indicator cannot see content hidden behind the keyboard or browser UI.
Use these as separate controls: Samsung Japanese versus Samsung English input; Samsung versus Gboard Japanese input; and the external versus on-screen keyboard. They help locate the failing layer; they are not workarounds.
Reproduce and capture
In the disposable note, select Samsung Japanese input and enter Insert mode. Type romaji, choose and commit candidates, and add newlines near the lower visible edge. Include candidate-strip show/hide changes and continue until caret reveal or scrolling occurs.
Exercise Normal ↔ Insert transitions, held-key navigation, and Backspace, Enter, and Escape around composition commit and cancel. Note the first action that precedes any repeated, unexpected, or stuck input.
After the bad state begins, export the trace before reloading. Compare the visible text with the editor document and the autosaved result; use the harness document-versus-visible indicator where applicable. Check undo and autosave as well.
Test switching to English as its own run, separately from reloading, so the two recovery actions are not conflated.
Read the trace and synthetic results
Check whether composition-end events have isTrusted: false, whether scrollerDetached and scrollerReattached bracket the first bad transition, and how Vim's useNextTextInput, mode, change provenance, and doubleHandled entries line up with it. A synthetic composition-end event or a scroller mutation is evidence about event order, not by itself proof of the device root cause.
The #7082 characterization used synthetic browser composition with the locked @replit/codemirror-vim 6.3.0. Composed j in Normal mode moved the model cursor once without changing the document, cleared the ghost composing flag, and left a following ordinary j usable. Multi-character Japanese input in Insert mode landed once; Enter during a live Insert-mode conversion committed without adding a newline or opening the command palette; and insertReplacementText replaced the selection once. Repeated j navigation moved the cursor once, and / search left the document unchanged. Synthetic edge behavior included i entering Insert mode during composition, Escape being deferred during a live conversion, multi-character Normal-mode input entering the document, and composed r inserting Z before the character rather than replacing it. No same-input double handling was found, and no repair issue was opened. These results describe the synthetic test path only; they do not establish physical Samsung IME behavior.
Upgrading @replit/codemirror-vim from 6.3.0 to 6.4.0 leaves its forceEndComposition path unchanged: it still detaches and reinserts scrollDOM, restores selection, refocuses, and dispatches a synthetic compositionend (6.4.0 source). A version bump alone does not settle the device question.
Deferred until the device trace
Choose and implement the real fix only after the native event sequence and failing layer are recorded.
If bare CodeMirror + Vim reproduces while bare CodeMirror does not, prepare an upstream minimal reproduction for
replit/codemirror-vimand a scoped dependency patch here.Compare dependency versions in a harness variant; the current harness ships only the locked versions.
Add the geometry regression for the confirmed path after the device trace identifies it.