Skip to content

feat(rust): markdown shortcuts in both desktop playgrounds - #161

Open
tmm wants to merge 3 commits into
rust-markdown-shortcutsfrom
rust-playground-markdown
Open

tmm wants to merge 3 commits into
rust-markdown-shortcutsfrom
rust-playground-markdown

Conversation

@tmm

@tmm tmm commented Nov 14, 2026

Copy link
Copy Markdown
Member

Description

Typing Markdown in the Rust playgrounds left it as text: the playground
editor never registered the markdown shortcuts, and Return called
insertParagraph directly, so the Enter-triggered transformers (code
fences) could not run.

  • lexical-playground-core registers register_markdown_shortcuts with
    the default transformers. Its script flags become
    ["richText","list","link","code","markdown"], so an exported session
    replays in JS with the same shortcuts. Return now dispatches
    KEY_ENTER_COMMAND, as a browser keydown does; rich text's handler
    then dispatches INSERT_PARAGRAPH_COMMAND unless the shortcuts or the
    code block took it.
  • GPUI draws code blocks with a monospace font, padding and a tinted
    background, so a typed code fence is visible. Its e2e run types a
    heading, bold text, a list item and a code fence.
  • Both READMEs list the shortcuts.
  • This branch merges rust-playground-blocks (feat(rust): headings, quotes, lists, links and indent in both playgrounds #141), which the
    playgrounds' block rendering needs.

Test plan

Before

$ cd rust/apps/lexical-playground-core && cargo test --locked markdown
thread 'tests::markdown_shortcuts_turn_typed_markdown_into_rich_text' panicked at apps/lexical-playground-core/src/tests.rs:497:5:
assertion `left == right` failed
  left: "paragraph"
 right: "h1"
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 19 filtered out

After

$ cd rust/apps/lexical-playground-core && cargo test --locked
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cd rust/apps/lexical-playground-rune && cargo test --locked   # macOS
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cd rust/apps/lexical-playground-gpui && cargo build --locked --features e2e && \
    LEXICAL_PLAYGROUND_E2E=/tmp/s.json ./target/debug/lexical-playground-gpui   # macOS
e2e: passed
$ cd packages/lexical-conformance && LEXICAL_REPLAY=/tmp/s.json pnpm exec vitest run src/replay.test.ts
 Test Files  1 passed (1)

On macOS, real keystrokes typed into GPUI through System Events (a
heading, lists, a quote, inline formats, a link, a code fence, then ⌘Z)
produced a 163-step session that replays in JS, and the in-app Replay
vectors shows 655/655. Real keystrokes could not be delivered to the Rune
app on the Mac satellite (it does not redraw for synthetic events, also
without this change), so Rune is covered by its runtime tests only.

Stacked on #157 (markdown shortcuts) and merges #141 (playground blocks), so it carries both until they land.

…unds

## Description

The Rune and GPUI playgrounds drew every block as a plain paragraph, read
only paragraphs at the root, and had no way to make a heading, quote, list
or link, though lexical-editor now has rich-text, list and link.

`lexical-playground-core` now:
- reads the tree recursively: list items with their list's type, depth,
  number and `checked`, headings, quotes, indents, and links' text runs with
  their URL; selection points map through undrawn elements (the root, lists),
  and the tree view shows lists above their items;
- registers rich-text, list, check list and link in the session, and sends
  Enter, Shift-Enter and formatting as the commands a browser dispatches;
- adds `set_block_type` (lists through their commands, the same list again
  makes paragraphs; other types through `$setBlocksType`), `indent`, `tab`
  (`$indentOverTab`), `toggle_link`, `toggle_checked`, `block_type_at`,
  `link_at` and `format_at`;
- gives the selection the format a browser's `selectionchange` gives it
  (`onSelectionChange`: a caret takes its text node's format and style, a
  range the format all its text shares) in the select step before an edit,
  and the toolbar reads it from the selection the app shows, so a click into
  bold text shows bold and typing there stays bold, as in a browser;
- gets an example with a heading, a bullet list with a link, a check list
  and a quote.

Rune maps list items to its field's bullets and numbers at their depth, a
quote to its quote block, links to `Mark::Link`, headings to larger, bolder
decorations and a checked item to strikethrough. GPUI shapes each line on
its own, at its block's size and indent, and draws list markers (•, 1., ☐/☑),
a quote's bar, links in the JS playground's blue, and checked items struck
through; a click on a box toggles it. Both get a second toolbar row (Normal,
H1-H3, Quote, the three lists, Link, Outdent, Indent, and a check toggle in a
check item), and Tab and Shift-Tab indent and outdent.

The GPUI end-to-end run now also makes a heading and a list, nests the item
with Tab, unnests it with Shift-Tab and ends the list; CI replays that
session in JS.

## Test plan

### Before

The block-menu test in `lexical-playground-core`, before `$setBlocksType`:

```
test tests::the_block_menu_converts_blocks_and_toggles_lists ... FAILED
```

Without the selectionchange format in the select step:

```
thread 'tests::a_click_gives_the_selection_the_format_there_as_selectionchange_does' panicked
  left: ("Ru", true)
 right: ("Ruyst", true)
```

### After

```
$ cargo test --workspace --all-features --locked   (rust/)
test result: ok. 19 passed; 0 failed   (lexical-playground-core)
$ cargo test --locked   (apps/lexical-playground-rune)
test result: ok. 9 passed; 0 failed
$ cargo build --locked --features e2e && LEXICAL_PLAYGROUND_E2E=/tmp/gpui-e2e.json ./target/debug/lexical-playground-gpui   (macOS 27, a real window)
e2e: passed
$ LEXICAL_REPLAY=/tmp/gpui-e2e.json pnpm exec vitest run src/replay.test.ts
      Tests  1 passed (1)
```

The GPUI session has 26 steps, including INSERT_UNORDERED_LIST_COMMAND,
INDENT_CONTENT_COMMAND and OUTDENT_CONTENT_COMMAND. A 26-step session from
`lexical-playground-core` (block menu, lists, Tab, links, a check box, undo
and redo) also replays in JS, and changing one character of one recorded
result makes the replay fail.

fmt, clippy `-D warnings` and rustdoc `-D warnings` pass for the workspace;
clippy passes for Rune and, on macOS, for GPUI with `--features e2e`.
Screenshots of both apps were inspected: Rune headless (`--snap`), GPUI on
macOS 27.

Amp-Thread-ID: https://ampcode.com/threads/T-16d45229-bfa8-417a-a091-d3e3888d830d
## Description

Typing Markdown in the Rust playgrounds left it as text: the playground
editor never registered the markdown shortcuts, and Return called
`insertParagraph` directly, so the Enter-triggered transformers (code
fences) could not run.

- `lexical-playground-core` registers `register_markdown_shortcuts` with
  the default transformers. Its script flags become
  `["richText","list","link","code","markdown"]`, so an exported session
  replays in JS with the same shortcuts. Return now dispatches
  `KEY_ENTER_COMMAND`, as a browser keydown does; rich text's handler
  then dispatches `INSERT_PARAGRAPH_COMMAND` unless the shortcuts or the
  code block took it.
- GPUI draws code blocks with a monospace font, padding and a tinted
  background, so a typed code fence is visible. Its e2e run types a
  heading, bold text, a list item and a code fence.
- Both READMEs list the shortcuts.
- This branch merges `rust-playground-blocks` (#141), which the
  playgrounds' block rendering needs.

## Test plan

### Before

```
$ cd rust/apps/lexical-playground-core && cargo test --locked markdown
thread 'tests::markdown_shortcuts_turn_typed_markdown_into_rich_text' panicked at apps/lexical-playground-core/src/tests.rs:497:5:
assertion `left == right` failed
  left: "paragraph"
 right: "h1"
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 19 filtered out
```

### After

```
$ cd rust/apps/lexical-playground-core && cargo test --locked
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cd rust/apps/lexical-playground-rune && cargo test --locked   # macOS
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cd rust/apps/lexical-playground-gpui && cargo build --locked --features e2e && \
    LEXICAL_PLAYGROUND_E2E=/tmp/s.json ./target/debug/lexical-playground-gpui   # macOS
e2e: passed
$ cd packages/lexical-conformance && LEXICAL_REPLAY=/tmp/s.json pnpm exec vitest run src/replay.test.ts
 Test Files  1 passed (1)
```

On macOS, real keystrokes typed into GPUI through System Events (a
heading, lists, a quote, inline formats, a link, a code fence, then ⌘Z)
produced a 163-step session that replays in JS, and the in-app Replay
vectors shows 655/655. Real keystrokes could not be delivered to the Rune
app on the Mac satellite (it does not redraw for synthetic events, also
without this change), so Rune is covered by its runtime tests only.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1daf1-150e-709b-aaed-996443a64459
@tmm
tmm force-pushed the rust-playground-markdown branch from 1730dea to 7bfbfca Compare November 14, 2026 18:15

This branch has not been deployed

No deployments
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