feat(mobile): add native mobile shell for Hermes Desktop - #52673
feat(mobile): add native mobile shell for Hermes Desktop#52673realfishsam wants to merge 74 commits into
Conversation
|
The title makes some sense but it's missing some core info or I'm having trouble finding it. Are you proposing a standalone native iPhone installation? |
|
Yes — this is proposing a standalone installable iPhone app. It is not a separate native rewrite of Hermes. It is an Expo/React Native iOS shell that runs the existing Desktop renderer in WKWebView, with a small native bridge for gateway-backed API calls and first-run gateway/profile/token setup. I rewrote the PR description to make that architecture and current status clearer, and added the iPhone demo video to the PR. |
…, keyboard - Mobile drawer: portal out of the collapsed <Pane> wrapper, fix the pointer-down/up double-toggle that ate the first tap, auto-close on session/nav/Settings tap. - Safe-area awareness across chat header, overlay close button, dialog & sheet close buttons, sheet top/bottom edges, and the docked composer. - Tap targets: titlebar buttons and composer input bumped to 44 pt; all controls inside OverlayView ≥44 pt with 16 px inputs (no iOS zoom). - iOS keyboard: visualViewport now drives --keyboard-inset; the composer rides up with the keyboard and last-message clearance grows so sent messages stay visible. - Intro centering: pb-composer (not pt-composer) so the wordmark sits visually centered between the top of the screen and the chatbox. - Native bridge: route diagnostics to Metro console (no on-screen overlay), fix getBootstrapState shape so the install overlay doesn't crash, and wrap unknown desktop methods so renderer fallbacks don't throw unhandled rejections. - Tooltips disabled on mobile-standalone (no hover on touch).
…moval - Settings, Command Center, Cron, Profiles, Messaging now open to a list on mobile and drill into the detail with a back button (no more list + detail stacked on a 393pt screen). - Sidebar: unified scroll (nav + sessions in one column, no dead space between search and PINNED), bigger tap-friendly session rows, darker backdrop (bg-black/45 + blur), hamburger gets a Claude-Code style filled-chip backdrop so it stays legible over busy chat content. - Chat header: plain title on mobile — the SessionActionsMenu dropdown crowded the Dynamic Island and the menu painted under it. Sidebar session rows also short-circuit SessionActionsMenu / SessionContextMenu so long-press doesn't surface a desktop-shaped menu. - Overlay close: "Done" pill bottom-right on mobile instead of a tiny X next to the safe-area; touch-friendly and unambiguous. - Switch / toggle: excluded from the mobile button-min-height rule so they render as switches, not enlarged blobs. - Adds three iPhone screenshots (empty state, conversation, settings drill-down) under docs/screenshots/.
assets/renderer/ is Vite output from `npm run renderer:bundle` — hashed filenames per build, ~220k lines of fonts + compiled bundle. Was tracked before the gitignore rule landed, so gitignore was silently bypassed. Untrack and re-add the rule.
Vite emits CSS inside dist/assets/ that references siblings as ./foo.ttf (e.g. codicon-*.ttf font face). The bundler only matched the basename when the file lived at dist root; assets under dist/assets/ never got inlined and rendered as tofu in the standalone WebView.
…ar regions SafeAreaView wrapped the WebView with the dark root backgroundColor and clipped it out of the safe-area regions, producing black bars at the notch and home indicator when the renderer ran a light theme. The renderer CSS already pads for env(safe-area-inset-*), so let the WebView fill the screen and own the insets.
The default iOS input-accessory toolbar (prev/next/done) appeared above the on-screen keyboard and shoved the composer up. The renderer has its own keyboard handling, so suppress it at the native layer.
- Hide the sidebar trigger on skills (added to OVERLAY_VIEWS). - Replace the horizontal category tag-cloud with a drill-down list on mobile: tap a category to filter to its skills, back link returns. - Add a Done pill bottom-right (mirrors OverlayView mobile pattern) so there is a clear way out without the titlebar control. - Bump PageSearchShell top padding on mobile so headers clear the Dynamic Island instead of sitting flush against it.
Messaging and Artifacts weren't in OVERLAY_VIEWS, so the titlebar sidebar trigger still rendered (overlapping the search field) and there was no mobile dismiss affordance. Extract the Done pill that Skills was using into a shared MobileDonePill component and mount it from all three non-OverlayView mobile menus.
Renderer code that calls e.g. window.hermesDesktop.fetchLinkTitle gets a Promise.resolve(undefined) instead of undefined when the mobile bridge doesn't stub the method, avoiding 'X.then is not a function' crashes.
Artifacts crashed with 'bridge(normalizedUrl).then is not a function' because the mobile renderer bridge's fallback object had no fetchLinkTitle stub. Inline-link title fetching is a desktop-only nicety; on mobile return an empty string instead of undefined.
Related mobile-shell decision: #53772 is the explicit conflict-resolved successor of this PR and #64962 is a competing lean iOS shell. Before either route is merged, choose one approach. Separately, this head commits a self-labelled development seed containing a gateway session credential in |
Correction after reviewing current head 7fb8754: the diff uses environment-driven gateway configuration and documentation placeholders; I did not find a committed bearer credential. The earlier credential-exposure conclusion is therefore not substantiated on the current patch. This remains a competing mobile-shell architecture alongside #53772 and #64962. |
Summary
This PR adds a standalone mobile app for Hermes. It is currently validated on iPhone, and the Expo project includes Android configuration as well.
It is not a native rewrite of the Hermes UI. The app is an Expo/React Native mobile shell that runs the existing Hermes Desktop renderer inside a WebView, then bridges renderer API calls to a configured Hermes gateway. On first launch, the app asks for the gateway URL, profile, and token, stores that locally, and then behaves like an installable mobile client for an existing Hermes instance.
Most of the size of this PR comes from landing the first mobile workspace and carrying over the Desktop renderer code needed to make the shell usable on phone-sized screens. The generated renderer bundle was initially tracked by mistake; that has been removed and ignored, so future mobile changes should be much smaller.
The architecture choice here is to keep one shared Hermes renderer/UI, wrap it in a native mobile shell, and apply only the mobile layout fixes needed for safe areas, keyboard behavior, sidebar navigation, and tap targets.
I tested this as a Release build on a physical iPhone, including install/launch via Xcode/Expo +
devicectl. The PR includes screenshots and a short demo video from that iPhone build, plus docs for the exact physical-device install flow, including signing and nested-framework issues encountered during setup. Android configuration is present in the Expo project, but the physical-device verification in this PR is iPhone-focused.Known current status: this PR needs to be rebased against current
main; the current merge conflict is inpackage-lock.json.Demo
Build-to-iPhone docs
iPhone screenshots
Verification
git diff --checknpm run typecheckfromapps/mobiledevicectlFeatured on r/hermesagent: https://www.reddit.com/r/hermesagent/comments/1ugv7i7/hermes_mobile/