Skip to content

Open documents for reading on phones, with an edit toggle - #477

Merged
MaggieAppleton merged 10 commits into
mainfrom
mobile/read-mode
Oct 11, 2026
Merged

MaggieAppleton merged 10 commits into
mainfrom
mobile/read-mode

Conversation

@MaggieAppleton

@MaggieAppleton MaggieAppleton commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Was stacked on #475 (phone shell), which has merged along with #474 and #478; this is now based on main.

Why

On a phone the document was always contenteditable (critique M-19, P0): tapping prose placed a caret and raised the keyboard, links didn't follow and opened an overflowing "url · Open · Edit" popover (M-20), and readers saw editing chrome such as Show source, code language menus, image handles, and Add/remove tab (M-25).

What changed

  • Reading mode on phones. PlanEditor takes a new editMode?: "reading" | "editing" prop. This is a local, per-client mode and is never synced. Reading locks Lexical the same way any lock does (MDXEditor readOnly, then setEditable(false)), but it stays separate from the permission-based readOnly, so:
    • Selection and commenting still work. The selection bubble shows only the Comment button. Lexical keeps tracking the native selection when the editor is not editable.
    • Permission still wins. Viewers, archived documents and implementation-locked documents get no mode and no pencil.
    • Remote edits render live. Yjs → Lexical updates don't depend on editability. This is covered by an e2e test.
    • No ghost caret for peers. Switching back to reading blurs the editor and dispatches BLUR_COMMAND. A contenteditable that stops being editable while focused fires no blur, so without this, peers would keep seeing this client's caret through awareness.
  • Edit toggle. The phoneAction slot gets a glass pencil, "Edit document". In edit mode it becomes a filled brand check, "Done editing". It changes mode inside the tap with flushSync, so focus lands within the user gesture and iOS raises the keyboard. The caret goes where the reader last tapped (caretPositionFromPoint/caretRangeFromPoint), or else to the first block in view, so starting to edit never scrolls the reader away. Writers only. The pencil fits the compact landscape bar (48px there).
  • New and empty documents open in edit mode. A New document (titleEdit === "new"), or a document that is empty when it first arrives, opens ready to write.
  • M-38. A new document's title now hands focus to the body when its rename save starts, not after the request returns. Characters typed in between used to land in the closing title field and were lost.
  • Docked formatting toolbar (toolbar/dock.tsx). While editing with the software keyboard up (:root[data-keyboard-open]), the toolbar replaces the tab bar in the same row and rides above the keyboard: Bold, Italic, Heading (H2 ↔ text), Bulleted list, Link, Insert block (types a / and arms the slash menu through a new OPEN_SLASH_MENU_COMMAND), and Hide keyboard. It uses 44px buttons and scrolls horizontally on narrow phones. Presses never take focus. It reuses the bubble's commands (block conversion is extracted as convertBlock). While the dock shows, the selection bubble is suppressed, so the two never appear together. With the keyboard down, the tabs and the check come back, and a selection gets the normal bubble.
  • Links in reading mode follow on tap and never preview. External links open a new tab. Chopin routes are already taken by the navigation shell's capture handler and navigate in the app.
  • Editing-only chrome hidden while reading:
    • Already handled by the existing readOnly$ lock: code language menus (shown as a static label), Edit image and resize handles, Add/remove tab, table rails and toolbar, and the slash menu.
    • Hidden by this PR: Show source (reading forces the rendered preview). It is authoring material, a 28px control (M-25), and returns in edit mode.
    • Left as is: the callout type control is already a disabled icon while locked, so readers see it as the callout's icon.
  • Task checkboxes stay inert while reading. Lexical toggles a checkbox only when the editor is editable, and toggling one rewrites the document's MDX. On a phone, a stray tap while scrolling would otherwise check off someone's task. Readers who mean it tap the pencil.
  • Decision cards keep working. They are answers, not edits.
  • Reopen and discard on resolved comments keep working while reading. They are sidecar actions, not prose edits. resolved-layer.tsx used to gate them on Lexical editability.
  • Mode lifetime. The mode lives in RoomWorkspace state, so it survives tab changes and reconnects. RoomWorkspace is keyed by channel id, so opening another document starts in reading mode again.
  • Pure state in packages/editor/src/edit-mode.ts: documentEditMode, opensEditing, and selectionOffer, with unit tests.

#474's selection bar (done in this PR)

SelectionBar from #474 now takes its gate from selectionOffer() in toolbar/index.tsx. A phone shows it unless the offer is "none": the keyboard is up and the dock owns formatting, or a real lock applies. A reader's selection therefore keeps the docked Comment bar. phone-read-mode.e2e.ts asserts the "Selection actions" bar with Comment and no formatting.

One phone class

The interim phone checks are gone: packages/editor/src/phone.ts (coarse pointer and ≤430px) and widgets/phone.ts. The comment sheet (in both comment-layer.tsx and resolved-layer.tsx), the selection bar, decisions, the questionnaire chip and wide diagrams all use usePhone() from @chopin/editor/pointer, which is the class the shell mirrors as :root[data-phone]. The comment sheet and docked bar therefore cover landscape phones and phones up to 600px wide. The toolbar spec that needed a non-phone touch screen moves from 600px to 601px.

Rebase notes

  • Nested documents moved the plan editor into a shared plan element. Reading mode applies to it, but not to a child room rendered on its own, which has no tab bar and so no pencil. On phones a child document stays editable as before (follow-up).
  • The desktop header keeps the M-38 hand-off and the rename-failure refocus.
  • PhoneHeader follows Give phones a bottom tab bar and a collapsing top bar #475's root-title model, with no child title and no Return button.

Screenshots

BEFORE (main, 8811) on the left, AFTER on the right. iPhone 15 Pro emulation.

Tapping prose: before, a focused editor and caret; after, nothing is focused.
Tap prose before and after

Tapping a link: before, the overflowing preview popover; after, the link opens in a new tab and no popover appears.
Link tap before and after

Editing chrome: before, Show source and language menus for a reader; after, plain language labels.
Editing chrome before and after

AFTER, reading compared with editing on the same block. The chrome returns in edit mode.
Reading vs editing chrome

AFTER: a reader's selection offers Comment (left); editing with the keyboard up shows the docked toolbar (middle, fake keyboard from e2e/visual-viewport.ts); editing with the keyboard down shows the tabs and the check (right).
Reading selection, editing with keyboard, editing without keyboard

Compact landscape bar, reading and editing:
Landscape reading and editing

Dock at 320px and on iPhone SE with the keyboard up. It scrolls when narrower than seven buttons.
Dock on small phones

Desktop at 1440×900 is unchanged: always editable, no pencil, and an e2e test covers it.

Testing

  • bun test packages/editor apps/web: all pass, including the new edit-mode.test.ts.
  • bun run types: pass.
  • bun run ci: dprint, oxlint, tokens and design record pass. The design contract fails only on reviewed-hash pins (see below, plus the ones Give phones a bottom tab bar and a collapsing top bar #475 already lists).
  • New e2e/phone-read-mode.e2e.ts covers:
    • Reading opens with no caret on tap.
    • A selection offers Comment only.
    • An external link opens a new tab with no popover, and an in-app link navigates.
    • The pencil enters editing at the tapped spot, typing works and syncs to a desktop peer, editing survives a Chat round trip, and Done returns to reading.
    • Remote edits arrive while reading.
    • With a fake keyboard, the dock replaces the tabs above the keyboard with 44px targets, Bold applies, there is no second formatting surface, and Hide keyboard blurs.
    • Editing chrome is hidden while reading and returns in edit mode.
    • A viewer gets no pencil, and desktop stays editable.
    • An empty document opens in edit mode.
  • Existing phone specs:
    • e2e/room.ts: ready() accepts a phone that opened for reading, and a new edit(page) helper taps the pencil.
    • These tests now enter edit mode before they edit: sidecar compact comment-focus tests, tab-authoring touch removal, the table touch toolbar helper, toolbar touch menus and slash-follow, and the responsive-workspace keyboard tests.
    • The toolbar test "selection toolbar follows its range" now runs against the reading bubble, which is still the Text formatting toolbar.
  • Local e2e could not run because another worktree holds the e2e ports. CI's browser-integration job passes: 553+ tests, including the new spec.

Needs a real device:

  • iOS keyboard focus from the pencil tap (the flushSync path).
  • That pointerdown/mousedown preventDefault on dock buttons keeps the keyboard up on iOS Safari and Android Chrome.
  • Native long-press selection on a non-editable Lexical root, and the Comment bubble placement next to the system callout.
  • caretRangeFromPoint accuracy for the tapped caret position.

Follow-ups:

  • The dock's list button uses the text glyph "UL", the same one the bubble's block menu uses. A proper list icon means adding one to @chopin/icons, which needs a design-contract exception.
  • The comment-only reading bubble is still labelled "Text formatting". Dock phone selection actions and give the comment sheet detents #474's selection bar replaces it on phones.

Design-contract review

Reviewed and renewed after the rebase, in Renew design-contract review hashes for the reading-mode owners and add ListIcon. CI reported 35 entries, but they come from 6 files: bubble.tsx alone pins 20 cases under one file hash. Renewed: workspace.tsx, toolbar/bubble.tsx, toolbar/slash.tsx, plan-editor.tsx, resolved-layer.tsx, comment-layer.tsx and widgets/questionnaire.tsx. None of them adds dynamic style logic. Added a dynamic-packages.json entry for the new packages/icons/src/list.tsx.

Review fixes (after the first review)

  • Dock scope. The dock now needs four things at once: edit mode, the keyboard up, the Document destination visible, and focus inside this workspace's .plan-content. Chat, a comment reply and the title rename never get it. There are e2e tests for the chat composer and for rename.

  • Pencil from Chat. It now switches to the Document. The check uses planVisible, because Chat overlays the document without changing view. The e2e test covers both Chat and Decisions.

  • iOS focus. When a reader's tap already focused the locked root, entering edit mode blurs the root and then focuses it again, both inside the tap. This way a real focus event fires, which raises the iOS keyboard and announces the caret to peers. A comment in the code explains why. An e2e test clicks the pencil without moving focus and asserts that the root gets a focus event.

  • Dock layout. Hide keyboard is pinned outside a scrolling command row: Bold, Italic, Heading, List, Link, Comment (while text is selected), and Insert. The row's trailing edge fades while more buttons wait past it. Nothing is cut off on iPhone SE.

  • Insert block. When the caret's block has text, Insert starts a new paragraph after that block, so it never splits a word. The slash menu is clamped above the dock (toolbar/dock-clearance.ts).

  • List icon. ListIcon is a new file in @chopin/icons, used by the dock and the bubble's block menu.

  • Smaller fixes:

    • The title field takes focus back if a rename fails.
    • The phone top bar shows "Editing" under the title while editing.
    • Inert task boxes drop their shadow and pointer affordance while reading.
    • The comment-only bubble is labelled "Selection".
  • Final touches.

    • The "Editing" cue is now its own small pill in the top bar. The bar's title stays hidden at the top of the document, so the pill shows there instead and cross-fades with the title's subtitle once you scroll.
    • The dock row fades on both edges: on whichever side buttons are scrolled past it.
    • Insert inside a list item or a table cell still adds its paragraph after the whole list or table, on purpose. In the dialect a table cell holds only inline text, and the inserted components can't live inside a list item.

Dock v2: keyboard up (left), Insert's menu above the dock (middle), iPhone SE with Hide keyboard pinned (right).
Dock v2

🤖 Generated with Claude Code

@MaggieAppleton

Copy link
Copy Markdown
Collaborator Author

Review: Changes needed

The core model is right. Reading is local state kept apart from permission readOnly. documentEditMode returns undefined for viewers, archived documents and implementation-locked documents, so permission always wins, and the resolved-layer gate does not loosen permissions. Links follow in reading mode. Desktop at 1440 and 1280, an 820px tablet and iPad Mini all stay editable with no pencil. I checked it on :8838 with headless Playwright (iPhone 15 Pro, iPhone SE, desktop) using a fake visualViewport keyboard. Three behaviour bugs need fixing first.

Must fix

  1. The dock appears over the Chat composer (and any other input) while editing. room-workspace.tsx (the phoneToolbar= and formatDock= props) gates on editMode === "editing" && keyboardOpen only. Repro: enter edit mode, open Chat, tap the composer, and the keyboard comes up. The B/I/H/UL/link/+ dock then replaces the tab bar and sits on top of the composer. Its buttons act on the document, and Bold shows as pressed from the document's selection. The comment sheet's reply field and the title rename field get the same dock.
    Fix: also require that focus is inside the plan editor. For example, track focusin/focusout on .plan-content, or have useKeyboardOpen return the focused owner. Also require that the Document tab is the visible destination (planVisible). Add an e2e test: editing, then Chat, then focus the composer with the fake keyboard, and expect no Formatting toolbar.

  2. The pencil doesn't open the document from Chat. toggleEditing (room-workspace.tsx ~928) runs if (!editing && view !== "plan") selectDestination("plan"). While Chat is open, view stays "plan" because Chat is the separate set-chat state. So tapping the pencil on the Chat tab flips the button to "Done editing", but the screen stays on Chat and focus stays on the button. From Decisions it works.
    Fix: use !planVisible (or check chat open) instead of view !== "plan". Add Chat and Decisions cases to the "pencil edits" e2e test.

  3. Entering edit mode can produce no focus event, which affects iOS and presence. While reading, Lexical's ContentEditable gives the root tabIndex={-1} when it is not editable, so a reader's tap focuses .plan-content. I confirmed that activeElement is the root after a prose tap. iOS Safari doesn't move focus to a tapped <button>, so the root is still focused when the pencil fires. editor.getRootElement()?.focus() in plan-editor.tsx (the mode effect) is then a no-op.

    • I reproduced this with a focus-preserving button.click(): 0 focus events, though the editor became editable.
    • Consequence 1: Lexical never dispatches FOCUS_COMMAND, so awareness focusing stays false and peers don't see this writer's caret.
    • Consequence 2: iOS is unlikely to raise the keyboard, because nothing gained focus. That is the flushSync path the PR lists as unverified.
    • Chromium e2e can't catch this because a Playwright tap focuses the button first.
      Fix: in the reading→editing branch, blur the root if it is document.activeElement before calling focus(). Both calls are still inside the gesture. Alternatively, blur the root on focusin while reading. Add a test that clicks the pencil without moving focus and asserts a focus event on the root.

Should fix

  1. Hide keyboard is clipped off-screen. On iPhone 15 Pro (393px) the 7th button, Hide keyboard, sits past the dock's right edge. On iPhone SE, Insert block and Hide keyboard are both clipped. Nothing shows that the row scrolls. The prototype (jig/shots/12-edit-mode.png) fits 6 buttons with the keyboard button visible. Fix: drop to 6 buttons. Either fold Insert block into the slash key, or drop Hide keyboard: iOS's accessory bar has Done and the brand check ends editing. Or pin the last button outside the scroller.

  2. Insert block splits words. OPEN_SLASH_MENU_COMMAND (slash.tsx) inserts " /" at a mid-word caret. With the caret in "Member|s" I got "Member /s". Fix: move the caret to the end of the current word, or better, insert a new empty paragraph after the current block, then type /.

  3. The slash menu covers the dock. The menu opened from the dock overlays the B/I/H buttons (popover placement ignores the dock). Place it above the dock's top edge, or clamp it to the visual viewport minus the dock height.

  4. List button glyph. Replace the text "UL" in dock.tsx:100 and bubble.tsx:72 with a real icon. Add ListIcon to packages/icons/src (its own file is fine, re-exported from index.ts) in the 18-grid line style. For example, <path d="M6.75 4.75h8.5M6.75 9h8.5M6.75 13.25h8.5" /> plus three r=".75" dots at x=3.25. B/I/H could stay text, but match the prototype's weight.

Nice to have

  • The title hand-off now runs at save start on desktop too (Header.handOff). If the rename fails, focus has already gone to the body and the title field stays open with an error nobody is looking at. Consider returning focus to the field on onSavingChange(false) when an error occurs.
  • Task checkboxes are inert while reading. That is defensible (a stray tap could check off someone's task), but they still look tappable. Mute them or give the reader a cue to edit while reading, so a tap doesn't feel broken.
  • No editing indicator in the top bar. The prototype shows "Editing" in the top bar. Right now the only cue is the check button.
  • The selection bubble is still labelled "Text formatting". The comment-only bubble keeps that aria-label. Dock phone selection actions and give the comment sheet detents #474 replaces it, but if it stays, label it "Selection".

#474 integration (selectionOffer)

The proposed integration is correct: locked: options.editMode ? !!options.blocked : disabled and <SelectionBar disabled={offer === "none"} />.

  • Viewers have no mode, so they fall back to readOnly$ and get "none", which matches desktop today.
  • Reading gives "comment".
  • Editing with the dock gives "none", so SelectionBar's own keyboard-inset positioning never collides with the dock.
  • Editing without the keyboard gives "format", which SelectionBar renders as Comment/Copy. That's fine.

Two notes:

Verified OK

  • Tapping prose in reading mode places no caret (contenteditable=false).
  • An external link opens a new tab with no popover. In-app links rely on the shell's capture handler and defaultPrevented.
  • javascript: and data: links are rejected by the dialect at seed time. LinkNode sanitizes them on the client.
  • Dock buttons are 44×44, and Bold and list keep focus in the editor in Chromium.
  • Remote cursors render only while awareness focusing is true, so the explicit BLUR_COMMAND on Done is the right fix for ghost carets.
  • Desktop and tablet are unchanged.

@MaggieAppleton

Copy link
Copy Markdown
Collaborator Author

Review: Looks good

I re-checked the latest head (b3c5375) on :8838 with a fresh worktree and headless Playwright: iPhone 15 Pro, iPhone SE, desktop 1440/1280, an 820px tablet and iPad Mini. Every must-fix and should-fix from my first review is resolved.

CI: e2e and container pass. "format, lint, types, tests" fails only on the design contract: the 35 "reviewed dynamic owner changed" pins plus the new packages/icons/src/list.tsx exception. All are listed in the PR body under "Needs a design-contract review", so this is acceptable.

Verified fixed

  • Dock scope. With edit mode on, focusing the Chat composer with the keyboard up shows no Formatting toolbar. The docked gate (edit mode, keyboard, planVisible, focus in .plan-content within the frame) is right.
  • Pencil from Chat. It switches to the Document, and focus lands in the editable root. From Decisions it still works.
  • iOS focus. A pencil click() that leaves focus on the already-focused locked root now produces exactly one focus event on the root, so the caret is announced to peers and iOS has a real focus gain inside the tap.
  • Dock layout. Hide keyboard is pinned and fully visible on iPhone 15 Pro and iPhone SE. On SE the command row scrolls, and the trailing fade shows there is more. Targets are 44×44.
  • Insert block. It now opens a new paragraph after the block, so words are no longer split. The slash menu is clamped above the dock and no longer covers it.
  • ListIcon is used in the dock and the bubble's block menu.
  • Other first-review items: the "Selection" label, the dock's Comment button, title refocus on rename failure, and the inert checkbox styling are all in place.
  • Unchanged and still correct: reading taps place no caret, external links open a new tab with no popover, and desktop and tablet are always editable with no pencil.

Nice to have (non-blocking)

  • The "Editing" indicator is invisible when it matters most. It sits in the top bar's collapsing title block, so at the top of the document its effective opacity is 0. It only appears after the in-content title scrolls away, but people usually start editing near the top. The prototype shows "Editing" in the bar at all times while editing. Consider keeping the subtitle (or the whole bar title) visible whenever editingDocument is true.
  • Insert block in a list or table goes after the whole container. getTopLevelElement() there is the list or table, so the new paragraph lands after the entire list or table. That's acceptable, but it may surprise someone in the middle of a long list. Inserting after the current list item would match intent better.
  • The SE dock can open scrolled. After a press near the trailing end, the row stays scrolled and Bold sits partly under the leading edge. Consider a leading fade too (data-more for each side).

@MaggieAppleton
MaggieAppleton force-pushed the mobile/bottom-tabs branch 3 times, most recently from 1bae625 to a96ebe6 Compare October 11, 2026 10:55
Base automatically changed from mobile/bottom-tabs to main October 11, 2026 11:09
MaggieAppleton and others added 9 commits October 11, 2026 12:51
Phones now open a document in a local reading mode: the editor is not
editable, so tapping prose places no caret and raises no keyboard, links
follow on tap, and editing-only chrome (Show source, code language menus,
image editing, tab add/remove, table rails, slash menu) stays hidden.
Selection and commenting keep working, because reading is separate from the
permission-based readOnly lock, which still wins.

A pencil in the phone tab bar switches to editing inside the tap, placing the
caret where the reader last tapped or at the first block in view. While the
keyboard is up, a docked formatting toolbar (bold, italic, heading, list,
link, insert, hide keyboard) replaces the tabs and the selection bubble.
Empty and newly created documents open ready to write, and a new document's
title hands focus to the body as its rename starts, so keys typed meanwhile
are kept. The mode lasts across tab changes and reconnects and resets per
document. Desktop and viewers are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y layout

Reopen and discard on a resolved comment are sidecar actions, so reading mode
keeps them; only other locks hide them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w details

- The dock needs focus in the visible document's editor, so Chat, a comment
  reply or the title rename never get it.
- The pencil opens the document from Chat, which overlays it without changing
  the view.
- Entering edit mode blurs a root that a reader's tap already focused before
  focusing it, so a real focus event fires (iOS keyboard, presence).
- Hide keyboard is pinned outside a scrolling, edge-faded command row; a
  Comment button joins it while text is selected.
- Insert block starts a new paragraph instead of splitting a word, and the
  slash menu keeps above the dock.
- ListIcon in @chopin/icons for the dock and the block menu.
- Title focus returns to the field when a rename fails; an "Editing" cue in
  the phone top bar; inert task boxes look inert while reading; the
  comment-only bubble is labelled "Selection".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s split

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The top bar's small title waits for the heading to scroll away, which hid the
Editing subtitle with it; a standalone Editing pill now stands in while the
title is hidden. The dock's command row fades whichever edge has buttons
scrolled past it. Insert block keeps placing its paragraph after the whole
list or table, and says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The selection bar from #474 now takes its gate from selectionOffer(), so a
reader's lock no longer hides it; the reading spec asserts the bar itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e content

The interim phone checks from the mobile slices (coarse and at most 430px
for the comment sheet and selection bar, and a copy of the phone media
query for wide content and decisions) now all use usePhone() from
@chopin/editor/pointer, the class the shell mirrors as :root[data-phone].
The comment sheet and docked selection bar therefore apply to every phone,
landscape and phones up to 600px wide included. The toolbar spec that
needed a non-phone touch screen moves from 600px to 601px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dd ListIcon

- apps/web/src/workspace.tsx: passes a phoneToolbar node through to the tab
  bar; no class or style change.
- packages/editor/src/toolbar/bubble.tsx (20 pinned cases, one file):
  extracts convertBlock and adds a comment-only variant with a literal
  aria-label; the reviewed placement and class sites are unchanged.
- packages/editor/src/toolbar/slash.tsx: an open-slash command and a
  viewport clipped above the dock, numeric geometry only.
- packages/editor/src/plan-editor.tsx: reading mode, caret placement and a
  data-plan-reading flag; no new style expressions.
- packages/editor/src/resolved-layer.tsx and comment-layer.tsx: reading
  keeps sidecar actions, and the sheet follows the shared phone class.
- packages/editor/src/widgets/questionnaire.tsx: imports the shared phone
  hook; nothing else changed.
- packages/icons/src/list.tsx: new icon forwarding IconProps to LineIcon,
  like the reviewed sibling icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a heading

- wide-content and pwa: phones open documents for reading, so the specs that
  type into a table cell, show source, or measure the callout title field
  tap Edit document first.
- phone-read-mode: the Editing pill stands in for the bar's title only while
  the document's heading is in view; seed one so the test exercises it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- phone-read-mode: phones open document actions as a sheet (#478), so
  Rename is a button in that dialog, not a menu item.
- wide-content: entering editing puts the caret in the first block in view,
  which can already open the code's source; accept either state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaggieAppleton
MaggieAppleton merged commit 59f6a9c into main Oct 11, 2026
3 checks passed
@MaggieAppleton
MaggieAppleton deleted the mobile/read-mode branch October 11, 2026 12:36
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