Skip to content

feat(files): smarter bullet delete/indent and untitled-file title sync - #5971

Merged
waleedlatif1 merged 11 commits into
realtime-roomsfrom
files-untitled-rename-and-bullets
Jul 27, 2026
Merged

feat(files): smarter bullet delete/indent and untitled-file title sync#5971
waleedlatif1 merged 11 commits into
realtime-roomsfrom
files-untitled-rename-and-bullets

Conversation

@mzxchandra

@mzxchandra mzxchandra commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Two files-module improvements to the markdown editor. Based on realtime-rooms (not staging) so it integrates directly with the live co-editing work and lands together with it — the only overlap was additive prop wiring in files.tsx / file-viewer.tsx / rich-markdown-editor.tsx, resolved here so that branch inherits these fixes conflict-free.

Bullet delete/indent + round-trip fix (improvement)

  • Backspace at the start of a list item now outdents a nested item or clears a top-level item to a paragraph in place instead of deleting the row and jumping the caret to the previous block. Enter on an empty nested item outdents. Empty non-trailing top-level items still collapse cleanly (they can't round-trip as a lifted paragraph). Emptiness counts inline atoms (images/mentions), and an empty first block in a multi-block item removes only that block.
  • Fixes a data-corruption round-trip bug: a nested empty bullet ( -) serialized directly under a parent's text and re-parsed as a Setext heading underline, silently turning the parent line into an H2 and dropping the bullet. Serialize now strips nested empty list-item lines only; top-level empty items (placeholders, imported blanks) are preserved and round-trip faithfully.

Untitled file → filename sync (feat)

  • While a file is still named untitled(.md), typing a leading heading auto-renames the file after it (debounced 600ms). One-shot: coupling stops once the file has a real name.
  • The reverse direction (rename → auto-insert an H1) was implemented but removed during review: on the collaborative Files page every open client observed the rename and seeded into the shared doc, producing duplicate headings, and it could re-insert a heading a user had just deleted mid-rename. Seeding document content from an async rename transition is the wrong model on a shared editor; the safe, read-only direction (heading → filename) is kept.

Test Coverage

Round-trip-verified unit tests for every list Backspace/Enter branch and every stripEmptyListItemLines rule, plus the untitled→filename derivation helpers. New/updated: keymap.test.ts, markdown-fidelity.test.ts, untitled-title.test.ts, title-heading.test.ts. Full files-feature + editor-consumer suite: 652 tests pass on the rebased realtime-rooms base. Typecheck + Biome clean.

Review

Greptile 5/5. Cursor Bugbot findings across rounds — all resolved:

  • isEmpty counted only text, so a bullet holding an inline atom could be deleted → now uses the caret block's content.size.
  • an empty first block in a multi-block item lifted the whole item → now removes only that block.
  • collaborative duplicate-heading race + in-flight-rename re-insert → resolved by removing the name→heading seed (see Summary).

Test plan

  • Full files-feature + editor-consumer suite (652) on the rebased realtime-rooms base
  • Typecheck clean; Biome clean
  • Manual E2E: bullet clear/outdent, untitled→heading auto-rename

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 27, 2026 9:28pm

Request Review

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes affect live editing keymaps, markdown parse/serialize fidelity, and file rename on the collaborative Files surface; scope is large but heavily tested.

Overview
Rich markdown editor gets new list Backspace/Enter behavior via getListItemContext: nested items outdent, top-level items with text lift to a paragraph in place, empty trailing bullets clear on the same line instead of deleting the row, and empty non-trailing middle items still remove cleanly when lift would break markdown round-trip. Enter on empty nested items outdents; multi-block items only drop the empty first block.

Serialize/load fixes stop a Setext corruption bug by stripping only hazardous nested empty list markers in postProcessSerializedMarkdown, while keeping top-level empty bullets. Chunked parsing now routes multi-blank-line spacing through the whole-document parser, normalizes \r line endings, and strips trailing empty paragraphs so files ending in a blank line stay editable instead of flipping read-only.

Untitled files (untitled.md / untitled (n).md): while the name is still untitled, a debounced leading heading (local edits only; remote Yjs changes ignored) drives auto-rename through new untitled-title helpers and onDeriveTitleFromHeading wired from the Files page.

Reviewed by Cursor Bugbot for commit eae98df. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves markdown-file editing behavior.

  • Outdents or clears list items more predictably on Backspace and Enter.
  • Preserves markdown fidelity by removing nested empty list markers that can be parsed as Setext headings.
  • Derives a unique filename from the leading heading while a markdown file remains untitled.

Confidence Score: 4/5

The PR appears safe to merge, with the previously documented bounded concurrent auto-rename behavior still present.

Two collaborators editing the leading heading during the same untitled window can still submit ordinary rename requests from stale local metadata, allowing the later request to determine the filename; no additional eligible failure was identified.

Files Needing Attention: apps/sim/app/workspace/[workspaceId]/files/files.tsx; apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/keymap.ts Adds context-aware list-item clearing and outdenting behavior for Backspace and Enter.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-fidelity.ts Filters nested empty list-item marker lines during markdown post-processing to preserve round-trip structure.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-parse.ts Refines markdown parsing and empty-block reconstruction behavior.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx Debounces leading-heading extraction for locally edited untitled markdown documents.
apps/sim/app/workspace/[workspaceId]/files/files.tsx Wires heading-derived title callbacks into file rename handling and centralizes untitled-name generation.
apps/sim/app/workspace/[workspaceId]/files/untitled-title.ts Adds helpers for recognizing untitled names and deriving unique markdown filenames.

Reviews (11): Last reviewed commit: "docs(files): correct EMPTY_PARAGRAPH_SPA..." | Re-trigger Greptile

@mzxchandra mzxchandra changed the title feat(files): Notion-style bullet editing and untitled-file title sync feat(files): smarter bullet delete/indent and untitled-file title sync Jul 27, 2026
@mzxchandra
mzxchandra force-pushed the files-untitled-rename-and-bullets branch from 8a904f1 to c2357d6 Compare July 27, 2026 04:28
…llet heading corruption

Backspace at the start of a list item now outdents a nested item or clears a
top-level item to a paragraph in place instead of deleting the row and jumping
the caret to the previous block; Enter on an empty nested item outdents. Empty
non-trailing top-level items still collapse cleanly since they cannot round-trip
as a lifted paragraph.

Also strips nested empty list-item marker lines on serialize: a nested empty
bullet re-parsed as a Setext heading underline, silently turning its parent line
into an H2 and dropping the bullet. Top-level empty items are preserved.
While a file is still named untitled(.md), typing a leading heading auto-renames
the file after it (debounced), and renaming the file first seeds a leading H1
from the new name. One-shot: coupling stops once the file has a real name, and
the heading seed always prepends so existing content is never clobbered.
@mzxchandra
mzxchandra force-pushed the files-untitled-rename-and-bullets branch from c2357d6 to b5b30f6 Compare July 27, 2026 04:34
…ck items on Backspace

Addresses review findings on the list Backspace logic:
- Emptiness now uses the caret block's content.size (counts inline images/mentions),
  not textContent, so a bullet holding only a non-text atom is no longer treated as
  empty and deleted.
- An empty first block whose item has sibling blocks removes only that block instead
  of lifting the whole item out of the list.
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2d39f67. Configure here.

…me during editor load

The parent captures the file name at mount (before content/session finish loading) and
passes it as the transition baseline, so a rename that lands in the loading window is still
seen as an untitled to named transition and the leading heading seed is not skipped.
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cursor review

Removes the effect that inserted a leading H1 when an untitled file was renamed. On the
collaborative Files page every open client observed the untitled-to-named transition and
inserted into the shared doc, producing duplicate headings; it could also re-insert a heading
a user had just deleted while a rename was in flight. Seeding document content from an async
rename transition is the wrong model on a shared editor. The primary direction — typing a
leading heading renames a still-untitled file — is unaffected (it never mutates the doc).
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit f9b2f9b. Configure here.

@mzxchandra
mzxchandra requested a review from waleedlatif1 July 27, 2026 16:29
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

…Enter/Backspace symmetry

- markdown-parse: EMPTY_PARAGRAPH_SPACING/NON_CHUNKABLE tested the raw body, but a classic
  \r-only file (blank lines are \r) would miss the \n-anchored guard and still be chunked,
  dropping empties. Normalize line endings once up front so the routing guards, the chunker,
  and the parser all see the same \n. +CRLF/CR test cases.
- keymap: Enter on an empty first block of a multi-block item now removes only that block
  (removeEmptyWrappedBlock) instead of exiting the list, mirroring the Backspace hasSiblingBlocks
  case — the trailing check no longer swallows multi-block items. +test.
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

… rename, over-strip)

A 4-agent independent audit (UX vs inkeep + SOTA, cleanliness, adversarial correctness)
surfaced these:

- HIGH regression: files ending in a blank line opened READ-ONLY. The empty-paragraph
  routing preserved a TRAILING empty paragraph, but postProcess collapses trailing newlines
  → serialize/parse non-idempotent → isRoundTripSafe flipped the file read-only. A trailing
  empty paragraph can't be serialized stably, so parseMarkdownToDoc now strips trailing empty
  paragraphs and the guard no longer routes on trailing blanks. Interior/leading empties are
  unaffected. +regression tests.
- Medium: the debounced untitled→filename rename fired on remote Yjs edits too, so every peer
  renamed and could rename from a not-yet-synced heading. Gate on isChangeOrigin (local edits
  only; false for non-collab surfaces).
- Medium: stripEmptyListItemLines over-stripped a nested empty item that follows a same-indent
  sibling (a real placeholder the parser keeps). Narrowed to the actual Setext hazard — an empty
  item DIRECTLY under a shallower parent line — matching the function's own docstring intent.
  Probe-verified. +test.
- Low: corrected untitled-title.ts docstring that described a reverse name→heading coupling
  removed during review.
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

The isChangeOrigin gate cleared the debounce timer BEFORE bailing on a remote update, so
a peer's edit arriving within the 600ms window cancelled the local user's pending rename.
Bail on isChangeOrigin first, before touching the timer; only local edits clear/reschedule it.
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

Comment thread apps/sim/app/workspace/[workspaceId]/files/files.tsx
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 29176a9. Configure here.

…-strip

The stacked trailing-empty-paragraph strip made the older comment overstate a
correctness necessity it no longer owns, mislabel trailing runs of 2+ blanks,
and advertise dead CRLF handling. Reword to match what the code actually does.
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit eae98df. Configure here.

@waleedlatif1
waleedlatif1 merged commit cddcb73 into realtime-rooms Jul 27, 2026
5 checks passed
@waleedlatif1
waleedlatif1 deleted the files-untitled-rename-and-bullets branch July 27, 2026 21:35
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.

2 participants