Skip to content

Find and Replace: Replace row, Replace all and match toggles (backlog, lowest priority) #394

Description

@HMarzban

Backlog. Lowest priority. Do not start before #393 ships.

Parent: #393. The maintainer moved Replace and the match toggles out of #393 on 2026-10-05. This issue starts only after the #393 Find bar ships. It was slices 7 and 8 there.

Scope

  • A chevron button at the left of the Find bar. It has aria-expanded and aria-controls. It shows the Replace row.
  • Replace row: a replace input, then Replace and Replace all. Replace changes the current match, then steps to the next one.
  • Toggles on the find input: Match case, Whole word and Regex. Each is a ToolbarButton with aria-pressed and the is-active recipe.
  • Toggle defaults stay off. The literal, case-insensitive search from Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 does not change.
  • Whole word uses Unicode letter classes (\p{L} with the u flag). A plain \b does not see Persian or Arabic letters.
  • An invalid regex shows an inline error and paints nothing. It never throws.

Risks and rules

  • One transaction. Replace all scans the whole document at click time, with no 200 cap. It applies every change in one ProseMirror transaction, in reverse document order, with tr.insertText. That is one Yjs update, so collaborators see one change. One Mod-z undoes it all.
  • Read-only pads and visitors. Show the chevron and the Replace row only when the editor is editable. Hide them under Read-only for a non-owner, under the editing lock (selectDocumentEditingLocked), and in History. Find itself stays available to every viewer.
  • Headings, sections and chatrooms. Each heading is a section with its own chatroom, keyed by toc-id. A match never crosses a textblock, so a replace never joins or splits blocks. The heading node and its toc-id must survive. A test pins this.
  • Heading text. A replace inside a heading changes the TOC title and the ?h= slug trail. Links use id=, so they still resolve.
  • Link edges. insertText takes the marks at the match start. A match that crosses a hyperlink edge can lose part of the link. See 393-replace-link-edge.
  • Comment anchors. scrollToCommentAnchor finds a text comment by its quoted text. A replace that changes that text sends the jump to the heading fallback.
  • Title. Replace may empty Title. Title must stay a heading.
  • Regex cost. A slow pattern blocks the main thread of the user's own tab. Cap the scan and fail closed.
  • Concurrent edits. A peer who types inside a match during Replace all merges through Yjs. Accept that.

Open decisions

Each needs a maintainer ruling. The recommendation comes from #393.

  1. 393-case-default (Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 decision 4). Recommend: case-insensitive, as today. Match case arrives as a toggle.
  2. 393-toggles-timing (Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 decision 9). Recommend: ship the toggles with Replace, not in the Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 Find bar. Find text in the open pad #250 ruled a literal search.
  3. 393-replace-all-auth (Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 decision 10). Recommend: Replace all needs a signed-in user (isVisitor(), then openInlineSignInDialog()). Single Replace follows the normal edit rule.
  4. 393-replace-chord (Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 decision 10). Recommend: ⌥⌘F on macOS, as VS Code uses. On Windows and Linux, choose between Ctrl+H and Ctrl+Alt+F. Ctrl+H is the browser's History key. Ctrl+Alt+F is AltGr on some layouts. Mod-Shift-h is already Highlight (@tiptap/extension-highlight).
  5. 393-replace-link-edge (Find in document: minimal floating Find bar (⌘F), then Find and Replace #393 Risks, Marks). Decide whether to refuse a match that crosses a hyperlink edge, or to keep the first mark. No recommendation was given in Find in document: minimal floating Find bar (⌘F), then Find and Replace #393.

Before any code, the maintainer approves the Replace row look from a visual preview, per AGENTS.md §Workflow And Review Expectations.

Acceptance criteria

  • The maintainer rules on the five open decisions, and approves a visual preview.
  • The chevron opens and closes the Replace row. aria-expanded follows it.
  • Replace changes the current match and steps to the next one.
  • Replace all changes every match in one transaction. One Mod-z undoes it. A collaborator sees one change.
  • After Replace all, every heading keeps its node and its toc-id. Each section keeps its chatroom.
  • A Read-only viewer, a visitor and a History view never see the chevron or the Replace row. They can still find.
  • Match case, Whole word and Regex work, and default to off. Whole word matches Persian and Arabic words.
  • An invalid regex shows an inline error, paints nothing and throws nothing.
  • A match across a hyperlink edge follows the 393-replace-link-edge ruling.
  • Title stays a heading after a replace empties it.
  • The look uses only the tokens and recipes in .cursor/docs/design-system.md, in every theme.
  • Cypress: Replace all is one undo step, and every heading keeps its toc-id. Extend apps/webapp/cypress/e2e/editor/find/caret-find.cy.ts.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions