feat(mobile): rebase iOS PR #17 onto main + scaffold Flutter→RN migration#136
Draft
matej21 wants to merge 30 commits into
Draft
feat(mobile): rebase iOS PR #17 onto main + scaffold Flutter→RN migration#136matej21 wants to merge 30 commits into
matej21 wants to merge 30 commits into
Conversation
Prevents alacritty from panicking when receiving zero-dimension resize events, which can occur during layout transitions on mobile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ApiFolder struct, folders/project_order fields to StateResponse, folder_color to ApiProject, and cols/rows to ApiLayoutNode::Terminal for terminal size propagation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add collect_terminal_sizes() to walk the layout tree and build a size map. Update ConnectionHandler::create_terminal to accept cols/rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…state response Build terminal size map from registry and use to_api_with_sizes() to populate cols/rows in layout nodes. Include folders and project_order in state responses for mobile/web clients. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set up CocoaPods integration, development team, scene manifest, local networking permissions, and rename native library to okena_mobile_native. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce centralized color palette (backgrounds, borders, accent, text hierarchy, glass effects) and typography system (SF Pro Text) for iOS-native dark theme. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add scroll_terminal, get_display_offset, and resize_local functions. Improve wide char spacer handling and move inverse flag to painter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add folder info, project ordering, server terminal size, and create/close/focus terminal actions. Update handler to use server terminal dimensions when creating terminals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…provements Rewrite all screens and widgets with Cupertino-inspired design: frosted glass headers, card-based layouts, haptic feedback, animated status indicators. Add pinch-to-zoom, scroll support, auto-fit font sizing, modifier key system, and text batching optimization to terminal rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ment UI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ntents FFI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…minal UI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…color picker) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…orrectness Rewrite to operate on byte offsets with char boundary checks instead of collecting into a Vec<char>, which gave wrong results for multi-byte characters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion Deregister pane map entries for inactive tabs so stale bounds don't interfere with spatial navigation. Add try_switch_tab() so Left/Right keys cycle tabs before falling through to cross-pane navigation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The tab-pane deregistration added in feat/ios predates main's multi-window support, which gave deregister_pane_bounds a leading WindowId parameter. Adapt the inactive-tab cleanup callsite to main's signature so the rebased branch compiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…endering) Plan to replace the Flutter UI with React Native while keeping okena-core and alacritty emulation. Primary path: uniffi-bindgen-react-native (JSI) + react-native-skia for native terminal rendering; xterm.js explicitly rejected. Documents the FFI seam, the hot-path packed-buffer bridge, a spike-gated phased plan, and the "drop Rust" fallback (reuse the web TS protocol client, still native rendering). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (Phase 1) New crate `crates/okena-mobile-ffi` re-expresses mobile/native's ~60-function FFI surface via uniffi proc-macros (uniffi 0.29, JSI/ubrn-ready) — no logic duplication: every fn delegates to okena_mobile_native's ConnectionManager, which is reused verbatim (its crate-type gains "lib" so it can be a path dep). - 31 genuinely-async fns exported with #[uniffi::export(async_runtime="tokio")] → JS Promises; sync getters (cells/cursor/scroll/selection/state) stay sync for the render hot path. - Adds get_visible_cells_packed(): the visible grid as a compact little-endian buffer (4B cols/rows header + 13B/cell: codepoint u32, fg u32, bg u32, flags u8) for the RN Skia renderer (RN_MIGRATION.md Decision C). - connect() accepts tls + pinned cert fingerprint at the boundary, but they are a documented no-op pass-through: client-side TLS lives on arch-review-fixes (PR #134), not on this branch's okena-core. Wiring is a follow-up. Verified: cargo check passes for okena-mobile-ffi AND okena_mobile_native (Flutter Rust side intact). Does not touch okena-core, Dart, or frb codegen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(Phase 2/3 scaffold) Scaffolds the React Native side under mobile/rn/ — the two technically-meaty pieces, not a full app: - src/native/okena.ts — the native↔TS binding contract: typed OkenaNative interface for all ~60 FFI fns + record/enum types, sync/async split mirroring the Rust side. ubrn generates the real impl from crates/okena-mobile-ffi. - src/native/cells.ts — packed cell-buffer decoder, byte-for-byte matching the Rust get_visible_cells_packed format (+ a zero-alloc PackedCells view). - src/components/TerminalView.tsx — terminal_painter.dart's 3-pass paint ported to @shopify/react-native-skia (no xterm.js): bg rects, style-batched glyph runs, cursor; rAF repaint gated on isDirty (Decision C); onLayout sizing. - src/theme.ts, package.json, tsconfig.json, README.md (exact local build steps). Verified on this box: npm install + tsc --noEmit (strict) pass; Skia APIs type-checked against @shopify/react-native-skia@1.5.x. Device build is not possible here (no Android/iOS SDK, no ubrn-generated module) — README documents the local steps. node_modules gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bar, layout (Phase 2)
Builds the RN UI on the existing contract + Skia renderer:
- Foundation: SavedServer + LayoutNode models (parseLayout mirrors the Flutter
parser), zustand connection/workspace stores (polling cadence ported from the
Flutter providers, native module injectable for tests), AsyncStorage-backed
persistence behind a swappable interface, and a minimal state-driven router
(no react-navigation native deps). App.tsx wires connection status → nav and
drives workspace polling.
- Screens: ServerList (list + add-server sheet), Pairing (connect→code→paired
with TLS-fingerprint footnote), Workspace (app bar + drawer + layout + toolbar).
- Widgets: ProjectDrawer (custom slide-in; projects/folders, add/reorder/color),
KeyToolbar (ESC/TAB + sticky CTRL/ALT/CMD with control-char + CSI encoding,
shared modifier store), LayoutRenderer (recursive split/tabs with portrait
rotation), TerminalPane (hidden TextInput, tap-focus, scroll/selection gestures
around the Skia TerminalView), StatusIndicator.
Ported from mobile/lib/src/{models,providers,screens,widgets}. Verified:
npm install + npx tsc --noEmit (strict) pass over all 22 RN source files.
No device build here (no SDK / ubrn module) — see mobile/rn/README.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Two layers on one branch:
feat/ios) rebased onto currentmain. PR feat/ios #17 was ~242 commits behindmainandCONFLICTING. This branch replays it on top of currentmain(5 desktop conflicts resolved + one post-rebase API fixup). Builds and tests pass.xterm.js), permobile/RN_MIGRATION.md.Closes the rebase need of #17; the RN work is additive and does not touch the desktop app.
Layer 1 — rebased iOS/mobile work (from #17)
Conflicts resolved against
main's evolution:okena-terminal/.../resize.rs—terminal.rswas split into aterminal/dir inmain; the min-1 col/row clamp was re-applied in the new location.remote_commands.rs— combinedmain'sapi_project_visibilitywith feat/ios #17'sto_api_with_sizes(terminal sizes in state).navigation.rs— semantic merge ofmain's multi-window +FocusManagerwith feat/ios #17's tab-aware navigation.views/window/{mod,render}.rs— tookmain's side; dropped feat/ios #17's desktop "ensure-visible vs center scroll" tweak (it renamed a fieldmainreferences in 5 other places; unrelated to mobile).fix(rebase):deregister_pane_boundsgained awindow_idarg inmain; adapted the tab-container callsite.Verified:
cargo check -p okena -p okena_mobile_native✅ ·cargo test -p okena-terminal -p okena-layout→ 92 passed ✅Layer 2 — React Native migration (Phase 1–2 scaffold)
mobile/RN_MIGRATION.md— the plan: keepokena-core+ alacritty, swap the binding (flutter_rust_bridge→ uniffi/ubrn, JSI), render the terminal natively viareact-native-skia.xterm.jsexplicitly rejected. Documents the hot-path packed-buffer bridge and a "drop Rust" fallback.crates/okena-mobile-ffi— uniffi binding crate (uniffi 0.29, 61 exports) that reusesokena_mobile_native'sConnectionManager/handler/holder verbatim (no logic duplication; itscrate-typegains"lib"). Addsget_visible_cells_packed()(compact LE cell buffer for the renderer).cargo checkpasses for it andokena_mobile_native(Flutter Rust side intact).mobile/rn/— the RN app: a typedOkenaNativebinding contract + packed-cell decoder, a SkiaTerminalView(3-pass paint ported fromterminal_painter.dart), zustand state stores (ported from the Flutter providers), models, navigation, persistence, and the screens/widgets (ServerList, Pairing, Workspace, ProjectDrawer, KeyToolbar, LayoutRenderer, TerminalPane). All 15 Flutter app source files have an RN counterpart. Verified:npm install+npx tsc --noEmit(strict) pass over all 22 RN source files.Not done yet (honest status — why this is a draft)
ubrn-generated native module is not wired —getOkenaNative()throws until it is. So this is a structurally-complete, type-checked port, not a proven-equivalent running app.connect()— client-side TLS lives onarch-review-fixes(PR Architecture review fixes: PTY starvation, persistence durability, remote/mobile leaks #134), not onmain. Forward-compatible signature is in place.layout_node_test,saved_server_test,terminal_flags_test,widget_test); no RN test runner yet.SafeArea, and per-cell metrics are simplified/omitted (spike-level). Build tooling (cargokit) is replaced byubrn, not ported.Next steps (require a local RN toolchain)
ubrn build android/ios --and-generateovercrates/okena-mobile-ffi; replacegetOkenaNative()'s body with the generated module (mobile/rn/README.mdhas exact commands).main.Reviewing
The diff is large because it includes the #17 rebase. The desktop/Flutter portion was reviewed in #17; the new work in this PR is the RN migration:
mobile/RN_MIGRATION.md,crates/okena-mobile-ffi/, andmobile/rn/.🤖 Generated with Claude Code