Skip to content

Add Google Slides parity keyboard shortcuts#238

Merged
hackerwins merged 5 commits into
mainfrom
feat/slides-keyboard-shortcuts
May 14, 2026
Merged

Add Google Slides parity keyboard shortcuts#238
hackerwins merged 5 commits into
mainfrom
feat/slides-keyboard-shortcuts

Conversation

@hackerwins

@hackerwins hackerwins commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add the Google Slides parity keyboard shortcuts the slides editor was missing — selection (Cmd+A, Esc, Tab/Shift+Tab, F2/Enter), slide navigation (Cmd+M, Cmd+Shift+D, Page Up/Page Down), present-mode entry (Cmd+Enter, Cmd+Shift+Enter), and a Cmd+/ shortcuts-help modal.

  • New keyRules land in the existing interactions/keyboard.ts array; SlidesEditorOptions gains three optional host callbacks (onStartPresentation, onShowShortcutsHelp, onLinkRequest).
  • shortcuts-catalog.ts is the single source of truth for shortcut metadata; the help modal renders directly from it.
  • Cmd+K (link) is plumbed end-to-end through docs/text-box-editor → slides text-box → SlidesEditor. The host wiring for a real link popover is deferred (requires expanding TextBoxEditorAPI with insertLink/getLinkAtCursor).
  • Present-mode wiring is also deferred — present UI isn't implemented in the slides package yet.

Design: docs/design/slides/slides-keyboard-shortcuts.md. Task: docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md.

Group/Ungroup and Find/Replace are explicitly out of scope (no group concept in the model; find/replace needs a deck-wide UX).

Test plan

  • pnpm verify:fast — green
  • 15 new unit tests in keyboard.test.ts (Cmd+A, Esc, Tab/Shift+Tab, F2/Enter, Cmd+M, Cmd+Shift+D, Page Up/Down, present callbacks, Cmd+/, interactive-widget gate)
  • 7 new tests in shortcuts-catalog.test.ts (invariants + formatCombo)
  • Manual browser smoke:
    • Cmd+/ opens the help modal with all shortcuts grouped by category
    • Tab/Enter inside the help modal use default focus behaviour (not hijacked by slides Tab-cycle / enter-edit rules)
    • Cmd+A selects every element on the current slide
    • Tab cycles through elements; Shift+Tab cycles backward; wraps
    • F2 / Enter on a single text-element selection enters edit mode
    • Cmd+M adds a new slide and switches to it
    • Cmd+Shift+D duplicates the current slide even when elements are selected
    • Page Up / Page Down navigate slides; no-op at boundaries
    • Esc clears selection; doesn't interfere with text-box edit or open dialogs
      🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added keyboard shortcuts for the slides editor, including selection (Cmd+A, Tab, Escape), slide navigation (Page Up/Down, Cmd+M to add slide, Cmd+Shift+D to duplicate), text editing (Enter/F2), presentation mode (Cmd+Enter), and link insertion (Cmd+K).
    • Added shortcuts help modal accessible via Cmd+/ to view all available keyboard shortcuts.
  • Documentation

    • Added design documentation for keyboard shortcuts parity implementation.
    • Added task documentation for the keyboard shortcuts feature rollout.

Review Change Stack

hackerwins and others added 4 commits May 14, 2026 20:37
The slides editor already covered nudge / undo-redo / delete / clipboard
/ duplicate / z-order. The remaining shortcuts a Google Slides user
reaches for on day one were missing: select all, Esc clear-selection,
Tab cycle, F2/Enter enter-edit, Cmd+M new slide, Cmd+Shift+D duplicate
slide, Page Up/Down navigate, Cmd+Enter present, Cmd+Shift+Enter present
from start, Cmd+/ shortcut help, and the Cmd+K link request inside
text-boxes.

This change adds all of them as keyRules in the existing
interactions/keyboard.ts array. The editor exposes three optional host
callbacks (onStartPresentation, onShowShortcutsHelp, onLinkRequest) so
the surrounding shell can route to its presentation entry, help modal,
and link popover. Group/ungroup and find/replace are intentionally
deferred — both require larger changes and are scoped to follow-up
designs.

A new shortcuts-catalog module exports the full list with a
platform-neutral key notation; it's the source of truth that the help
modal and any future doc pages will render from. Single rule-and-
catalog drift is the trade-off; comments call out the dual-edit
convention.

Design: docs/design/slides/slides-keyboard-shortcuts.md

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The editor exposes onShowShortcutsHelp; this commit adds the React
modal that consumes the SHORTCUTS catalog and renders categorized
key combos. Without a host-side wiring, Cmd+/ no-ops — the modal is
the only path users have to discover the parity shortcuts shipped
in the previous commit.

The onStartPresentation and onLinkRequest callbacks are left
unwired. Present mode UI isn't yet implemented in the slides
package, and a real link popover requires TextBoxEditorAPI to
expose insertLink / getLinkAtCursor first. Comments in
slides-view.tsx call this out so the next owner knows where to
pick up.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The runtime + help modal are in place. Two items remain as
documented deferrals: present-mode wiring (UI not yet implemented)
and a real link popover (needs TextBoxEditorAPI expansion). Both
have the editor-side hooks ready, so picking them up is contained.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The first round of selection-level shortcuts only checked
INPUT/TEXTAREA/SELECT/contenteditable for the editable-target gate.
That left a real UX bug: pressing Tab inside the shortcuts-help
dialog (a focused button inside role="dialog") would fire the
slides Tab-cycle rule and steal focus from the dialog. Enter on
any focused button would enter text-edit mode on a selected
text element instead of activating the button.

Extend isEditableTarget to also gate when the focused element is a
<button>, has a button/menuitem/option/tab role, or lives inside a
dialog/menu/listbox/combobox/tree/grid. Two regression tests lock
this in.

Also tighten the present-mode and Cmd+/ rules to require their host
callback be wired before matching — otherwise an unhandled
shortcut consumed the event silently. Document the Cmd+Enter
behaviour deviation (it respects the gate rather than bypassing
it like the original design said) and the rationale in the design
doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 4 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ef48780-85ae-49a0-b289-19ea10bf1a26

📥 Commits

Reviewing files that changed from the base of the PR and between 791d2f3 and 6cac83e.

📒 Files selected for processing (1)
  • docs/design/slides/slides-keyboard-shortcuts.md
📝 Walkthrough

Walkthrough

This PR implements keyboard shortcut parity with Google Slides by introducing a centralized shortcuts catalog, extending keyboard interaction rules and editor options, adding link-request callbacks throughout the text editing stack, and building a help modal UI integrated into the slides view.

Changes

Slides Keyboard Shortcuts Implementation

Layer / File(s) Summary
Shortcuts Catalog and Data Structures
packages/slides/src/view/editor/shortcuts-catalog.ts, packages/slides/src/view/editor/shortcuts-catalog.test.ts, packages/slides/src/index.ts
Introduces ShortcutCategory union type, ShortcutEntry interface, and SHORTCUTS read-only array as a single source of truth for all editor shortcuts. Exports formatCombo() to transform platform-neutral tokens (Mod, Shift, Alt) into platform-specific display symbols (⌘ on macOS, Ctrl elsewhere). Catalog tests validate structure, presence of key shortcuts, and combo formatting across platforms.
Keyboard Interaction Rules and Context
packages/slides/src/view/editor/interactions/keyboard.ts, packages/slides/src/view/editor/interactions/keyboard.test.ts
Extends KeyboardContext with setCurrentSlide() and enterEditMode() methods plus optional onStartPresentation and onShowShortcutsHelp callbacks. Adds ~220 lines of keyboard rules covering shortcuts help (Cmd+/), presentation start (Cmd+Enter/Shift+Enter), select all (Cmd+A), slide management (Cmd+M/Shift+D, PageUp/Down), element cycling (Tab), text edit mode (F2/Enter), and escape-to-clear. Strengthens isEditableTarget detection to gate rules for interactive widgets and ARIA role elements. Comprehensive test coverage includes selection, navigation, mode entry, focus gating, and callback invocation.
Editor Options and Keyboard Wiring
packages/slides/src/view/editor/editor.ts
Extends SlidesEditorOptions interface with onStartPresentation, onShowShortcutsHelp, and onLinkRequest callbacks. Wires presentation and help callbacks into buildKeyRules configuration. Mounts text-box editor with forwarded onLinkRequest option to enable link insertion during text editing.
Link Request Callback Plumbing
packages/docs/src/view/text-box-editor.ts, packages/slides/src/view/editor/text-box-editor.ts
Adds optional onLinkRequest callback to TextBoxEditorOptions (docs package) and MountSlidesTextBoxOptions (slides package). Slides text-box editor destructures and forwards the callback through initializeTextBox so Cmd/Ctrl+K requests can trigger host-provided link popover behavior.
Help Modal Component
packages/frontend/src/app/slides/slides-shortcuts-help.tsx
Renders a React dialog listing keyboard shortcuts by category in a responsive two-column grid. Memoizes platform detection and groups SHORTCUTS catalog entries by category, respecting a defined CATEGORY_ORDER. Displays each entry's description and formatted key combos in <kbd> elements using formatCombo() for platform-appropriate symbols.
Frontend Integration in SlidesView
packages/frontend/src/app/slides/slides-view.tsx
Integrates the help modal by importing SlidesShortcutsHelp, adding helpOpen state, wiring onShowShortcutsHelp callback to set helpOpen=true, and rendering the modal alongside the slides container.
Design and Task Documentation
docs/design/README.md, docs/design/slides/slides-keyboard-shortcuts.md, docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md
Design document specifies keyboard shortcut parity goals, architecture (selection, slide, presentation, help, link-request scopes), interface contracts, shortcuts catalog module shape, platform-neutral key tokens, link popover wiring, help modal UX, testing plan, and risk mitigations (catalog drift, tab ordering, enter ambiguity, page scroll, presentation side effects). Task document tracks rollout plan and implementation status. Design index updated to reference the new document.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit hops through shortcuts bright,
Cataloging keystrokes left and right,
From Cmd+A to Cmd+M, we align,
With Google Slides' design so fine,
Modal help shows the way—perfect by design! 🐰⌨️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding Google Slides keyboard shortcuts parity to the slides editor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/slides-keyboard-shortcuts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/frontend/src/app/slides/slides-shortcuts-help.tsx (1)

50-52: ⚡ Quick win

Render uncategorized shortcut groups instead of dropping them silently.

The current loop only renders categories listed in CATEGORY_ORDER, so new categories added to SHORTCUTS won’t appear in the help modal.

Proposed diff
-        <div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mt-2">
-          {CATEGORY_ORDER.map((category) => {
+        <div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mt-2">
+          {[...CATEGORY_ORDER, ...Array.from(grouped.keys()).filter((c) => !CATEGORY_ORDER.includes(c))].map((category) => {
             const entries = grouped.get(category);
             if (!entries || entries.length === 0) return null;
             return (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/frontend/src/app/slides/slides-shortcuts-help.tsx` around lines 50 -
52, The rendering loop currently iterates only CATEGORY_ORDER, dropping any
other groups from grouped derived from SHORTCUTS; update the iteration in
slides-shortcuts-help.tsx to include uncategorized groups by computing the
remaining categories (e.g. Array.from(grouped.keys()).filter(k =>
!CATEGORY_ORDER.includes(k))) and concatenating them to CATEGORY_ORDER (or sort
grouped entries using CATEGORY_ORDER as a priority) before mapping; ensure you
still skip empty entries (the existing entries check) and reference
CATEGORY_ORDER, grouped, and SHORTCUTS when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md`:
- Line 11: Change the task section headings from level 3 to level 2 to fix
markdownlint MD001: replace each "### Task X: ..." heading with "## Task X: ..."
(e.g., update the "### Task 1: Shortcuts catalog" heading and the other
occurrences noted at lines 22, 45, 56, 77, and 85) so the headings increment
correctly under the top-level H1.

In `@packages/slides/src/view/editor/shortcuts-catalog.ts`:
- Line 78: The top-line comment "// History" is inconsistent with the shortcuts
below which use category 'Help' for the undo/redo entries; update one to match
the other: either change the comment to "// Help" or rename the category string
for the undo/redo shortcuts to 'History' (look for the undo/redo shortcut
objects or the shortcuts array in shortcuts-catalog.ts, e.g., entries with
identifiers or labels like "undo"/"redo" or methods registering Undo/Redo) so
the category and comment describe the same group.

---

Nitpick comments:
In `@packages/frontend/src/app/slides/slides-shortcuts-help.tsx`:
- Around line 50-52: The rendering loop currently iterates only CATEGORY_ORDER,
dropping any other groups from grouped derived from SHORTCUTS; update the
iteration in slides-shortcuts-help.tsx to include uncategorized groups by
computing the remaining categories (e.g. Array.from(grouped.keys()).filter(k =>
!CATEGORY_ORDER.includes(k))) and concatenating them to CATEGORY_ORDER (or sort
grouped entries using CATEGORY_ORDER as a priority) before mapping; ensure you
still skip empty entries (the existing entries check) and reference
CATEGORY_ORDER, grouped, and SHORTCUTS when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa8b710d-1345-4f3c-8d85-d04523c19a6d

📥 Commits

Reviewing files that changed from the base of the PR and between d9951f6 and 791d2f3.

📒 Files selected for processing (13)
  • docs/design/README.md
  • docs/design/slides/slides-keyboard-shortcuts.md
  • docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md
  • packages/docs/src/view/text-box-editor.ts
  • packages/frontend/src/app/slides/slides-shortcuts-help.tsx
  • packages/frontend/src/app/slides/slides-view.tsx
  • packages/slides/src/index.ts
  • packages/slides/src/view/editor/editor.ts
  • packages/slides/src/view/editor/interactions/keyboard.test.ts
  • packages/slides/src/view/editor/interactions/keyboard.ts
  • packages/slides/src/view/editor/shortcuts-catalog.test.ts
  • packages/slides/src/view/editor/shortcuts-catalog.ts
  • packages/slides/src/view/editor/text-box-editor.ts


---

### Task 1: Shortcuts catalog

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix heading-level increment to satisfy markdownlint (MD001).

These ### headings are directly under an H1, which violates heading increment rules. Use ## for each task section.

Proposed fix
-### Task 1: Shortcuts catalog
+## Task 1: Shortcuts catalog
...
-### Task 2: Extend `KeyboardContext` + add key rules
+## Task 2: Extend `KeyboardContext` + add key rules
...
-### Task 3: Link callback plumbing
+## Task 3: Link callback plumbing
...
-### Task 4: Frontend wiring
+## Task 4: Frontend wiring
...
-### Task 5: Verify and commit
+## Task 5: Verify and commit
...
-### Task 6: Archive
+## Task 6: Archive

Also applies to: 22-22, 45-45, 56-56, 77-77, 85-85

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 11-11: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md` at line 11,
Change the task section headings from level 3 to level 2 to fix markdownlint
MD001: replace each "### Task X: ..." heading with "## Task X: ..." (e.g.,
update the "### Task 1: Shortcuts catalog" heading and the other occurrences
noted at lines 22, 45, 56, 77, and 85) so the headings increment correctly under
the top-level H1.

{ category: 'Present', keys: ['Mod+Enter'], description: 'Start presentation from the current slide' },
{ category: 'Present', keys: ['Mod+Shift+Enter'], description: 'Start presentation from the first slide' },

// History --------------------------------------------------------------

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update comment to match the category.

The comment says "History" but the shortcuts below use category 'Help'. Consider either changing the comment to // Help or renaming the category to 'History' if that better describes Undo/Redo.

📝 Proposed fix
-  // History --------------------------------------------------------------
+  // Help (includes history operations) -----------------------------------
   { category: 'Help', keys: ['Mod+Z'],          description: 'Undo' },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// History --------------------------------------------------------------
// Help (includes history operations) -----------------------------------
{ category: 'Help', keys: ['Mod+Z'], description: 'Undo' },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/slides/src/view/editor/shortcuts-catalog.ts` at line 78, The
top-line comment "// History" is inconsistent with the shortcuts below which use
category 'Help' for the undo/redo entries; update one to match the other: either
change the comment to "// Help" or rename the category string for the undo/redo
shortcuts to 'History' (look for the undo/redo shortcut objects or the shortcuts
array in shortcuts-catalog.ts, e.g., entries with identifiers or labels like
"undo"/"redo" or methods registering Undo/Redo) so the category and comment
describe the same group.

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 189.1s

Lane Status Duration
sheets:build ✅ pass 13.1s
docs:build ✅ pass 12.2s
slides:build ✅ pass 10.6s
verify:fast ✅ pass 108.0s
frontend:build ✅ pass 18.9s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.9s
cli:build ✅ pass 2.1s
verify:entropy ✅ pass 19.1s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/docs/src/view/text-box-editor.ts 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

The doc had accumulated PR-scope language and step-by-step task
content. A design doc outlives the PR that lands it, so:

- Drop the 4-step "Link popover wiring" procedure; replace with a
  one-paragraph statement of the callback flow.
- Drop the Help modal's UI-implementation bullets; keep only the
  architectural fact that it renders from the catalog.
- Remove the Testing section — test layout is a task artifact.
- Remove duplicate "Catalog drift" risk and the post-implementation
  "Categories expanded" note that crept in during review.
- Bump target-version to 0.4.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins merged commit 2c319e5 into main May 14, 2026
4 checks passed
@hackerwins
hackerwins deleted the feat/slides-keyboard-shortcuts branch May 14, 2026 11:59
hackerwins added a commit that referenced this pull request May 18, 2026
These seven slides + PPTX tasks shipped (PRs #238, #240, #241, #255,
#259, #260, #261) but their checklist boxes were never ticked during
execution, so the archive script left them in active/. Sweep the
checkboxes to reflect what actually landed, add lessons files for the
two that were missing (keyboard-shortcuts, presentation-mode), and
convert the keyboard-shortcuts onLinkRequest popover bullet from a
checkbox to a "Deferred (follow-up)" note so the gate stops blocking on
work that is intentionally out of scope.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
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