Skip to content

Add mobile overflow menu to Docs formatting toolbar#132

Merged
hackerwins merged 1 commit into
mainfrom
docs-mobile-toolbar
Apr 14, 2026
Merged

Add mobile overflow menu to Docs formatting toolbar#132
hackerwins merged 1 commit into
mainfrom
docs-mobile-toolbar

Conversation

@hackerwins

@hackerwins hackerwins commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Apply the same mobile-responsive pattern used in Sheets toolbar to the Docs toolbar
  • On viewports narrower than 768px, show only core tools (undo/redo, bold, italic, underline, text/highlight color) inline
  • Collapse styles, insert, alignment, list, and export actions into a ⋮ overflow menu with labeled sections

Test plan

  • Open Docs editor, resize browser below 768px — verify only core tools visible + ⋮ button
  • Click ⋮ menu — verify Styles, Insert, Align, List, Export sections appear
  • Test each menu action works correctly (style changes, insert link/image/table, alignment, lists, export)
  • Resize above 768px — verify full desktop toolbar appears unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Mobile-responsive toolbar that adapts formatting options based on screen size
    • Added compact overflow menu on mobile devices consolidating styles, insert options, alignment, lists, and export features
    • Essential formatting controls (undo/redo, bold/italic/underline, text color, highlighting) remain visible across all devices

Apply the same mobile-responsive pattern used in Sheets: on viewports
narrower than 768px, show only core formatting tools (undo/redo, bold,
italic, underline, text/highlight color) inline and collapse styles,
insert, alignment, list, and export actions into a ⋮ overflow menu.

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

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds mobile-responsive behavior to the docs formatting toolbar using a useIsMobile() hook that conditionally renders content. Core formatting controls (Undo/Redo, Bold/Italic/Underline, Text Color, Highlight Color) remain visible on mobile, while additional features (Styles, Insert, Align, List, Export) are consolidated into an overflow dropdown menu triggered by a button. Desktop behavior is preserved.

Changes

Cohort / File(s) Summary
Task Documentation
docs/tasks/active/20260415-docs-mobile-toolbar-todo.md
Adds a task file documenting the implementation of mobile-responsive formatting toolbar with conditional rendering and overflow menu consolidation.
Mobile-Responsive Toolbar Component
packages/frontend/src/app/docs/docs-formatting-toolbar.tsx
Introduces useIsMobile() hook to conditionally render toolbar UI. Desktop displays original layout; mobile shows core controls inline with dropdown menu consolidating Styles, Insert (Link, Image, Table), Align, List, and Export actions. Includes file input handling for image uploads and DropdownMenuLabel/DropdownMenuSeparator for menu organization.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 With whiskers twitched and a hop so spry,
I've tucked away toolbars up in the sky—
When screens grow small, the menu expands,
With dots that reveal a mobile command,
Core tools stay near, no need to deny!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a mobile overflow menu to the Docs formatting toolbar, which aligns with the primary objective and changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs-mobile-toolbar

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.

@github-actions

github-actions Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 111.8s

Lane Status Duration
sheets:build ✅ pass 12.8s
docs:build ✅ pass 7.8s
verify:fast ✅ pass 55.9s
frontend:build ✅ pass 14.1s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.5s
cli:build ✅ pass 1.7s
verify:entropy ✅ pass 14.8s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/tasks/active/20260415-docs-mobile-toolbar-todo.md`:
- Around line 1-253: Create a paired lessons file for the existing todo by
adding a new markdown file named 20260415-docs-mobile-toolbar-lessons.md
alongside the todo; in that file include a short summary of what was implemented
(mobile overflow menu for DocsFormattingToolbar), key implementation decisions
(useIsMobile hook, DropdownMenu sections, kept core tools inline), any gotchas
or edge-cases encountered (event handlers, file input for image upload, editor
focus calls), verification steps you ran (lint, verify:fast, manual
mobile/desktop checks) and a short checklist of follow-ups or known issues for
future work so reviewers have context for the change.

In `@packages/frontend/src/app/docs/docs-formatting-toolbar.tsx`:
- Around line 821-851: The mobile Insert dropdown removed the existing
image-by-URL and variable-size table flows and hard-coded disk upload + a 3×3
table; restore parity by replacing the inline image/file handler and fixed table
action with the same components/handlers used on desktop—use InsertImageDropdown
(or the same URL dialog flow used there) alongside the existing
insertImageFromFile logic (e.g., keep the file input branch) and replace the
fixed editor?.insertTable(3,3) call with the TableDropdown component or the same
table-size picker logic so users can choose table dimensions; ensure to call
editor?.focus() after insertion as before.
- Around line 888-892: Add a labeled "Export" section header before the export
menu item so the toolbar matches other labeled groups; insert a
DropdownMenuLabel (or the existing label component used for
"Styles"/"Insert"/"Align"/"List") immediately after the DropdownMenuSeparator
and above the DropdownMenuItem that calls handleExportDocx, keeping the disabled
and onClick props as-is so the export entry remains functional and visually
grouped.
🪄 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: efa16e96-4213-4734-9e24-cc4a2cde22a3

📥 Commits

Reviewing files that changed from the base of the PR and between 4f0ad83 and 11c835d.

📒 Files selected for processing (2)
  • docs/tasks/active/20260415-docs-mobile-toolbar-todo.md
  • packages/frontend/src/app/docs/docs-formatting-toolbar.tsx

Comment on lines +1 to +253
# Docs Mobile Toolbar Implementation Plan

> **For agentic workers:** Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add mobile-responsive overflow menu to the Docs formatting toolbar, matching the Sheets toolbar pattern.

**Architecture:** Add `useIsMobile()` hook to `DocsFormattingToolbar`. On mobile (<768px), show only core tools (Undo, Redo, Bold, Italic, Underline, Text Color, Highlight Color) inline, and collapse everything else (Styles dropdown, Link, Image, Table, Alignment, Lists, Indent, Export) into a single `⋮` overflow `DropdownMenu` with labeled sections and separators.

**Tech Stack:** React, Radix DropdownMenu, Tabler Icons, `useIsMobile()` hook

---

## File Map

- **Modify:** `packages/frontend/src/app/docs/docs-formatting-toolbar.tsx`
- Add `useIsMobile` import
- Wrap desktop-only items in `{!isMobile && (...)}`
- Add mobile overflow menu in `{isMobile && (...)}`

No new files needed. Single-file change following the established Sheets pattern.

---

### Task 1: Add mobile hook and hide desktop-only items

**Files:**
- Modify: `packages/frontend/src/app/docs/docs-formatting-toolbar.tsx`

- [ ] **Step 1: Add imports**

Add `useIsMobile` hook and new icons needed for the overflow menu:

```tsx
import { useIsMobile } from "@/hooks/use-mobile";
import {
IconDotsVertical,
} from "@tabler/icons-react";
```

Also add these imports from the dropdown-menu (already partially imported):

```tsx
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
DropdownMenuLabel, // NEW
DropdownMenuSeparator, // NEW
} from "@/components/ui/dropdown-menu";
```

- [ ] **Step 2: Call the hook**

Inside `DocsFormattingToolbar`, right after the existing state declarations:

```tsx
const isMobile = useIsMobile();
```

- [ ] **Step 3: Hide desktop-only items on mobile**

Wrap the following sections with `{!isMobile && (...)}`:

1. **Styles dropdown** (lines 501-535) — the `min-w-[110px]` block type dropdown
2. **The separator after Styles** (line 537)
3. **Insert group** — Link button, InsertImageDropdown, TableDropdown (lines 654-669)
4. **The separator after Insert** (line 671)
5. **Block styles group** — Alignment dropdown, Numbered list, Bulleted list, Indent decrease, Indent increase (lines 674-759)
6. **The separator before Export** (line 761)
7. **Export DOCX button** (lines 764-776)

Keep always visible: Undo, Redo, separator, Bold, Italic, Underline, Text Color, Highlight Color.

- [ ] **Step 4: Run lint to verify no syntax errors**

Run: `cd /Users/hackerwins/Development/wafflebase/wafflebase && pnpm lint`
Expected: PASS (no errors in docs-formatting-toolbar.tsx)

---

### Task 2: Add the mobile overflow menu

**Files:**
- Modify: `packages/frontend/src/app/docs/docs-formatting-toolbar.tsx`

- [ ] **Step 1: Add the overflow menu after the Highlight Color dropdown**

Right after the closing `</DropdownMenu>` for Highlight Color and before the `{!isMobile && (` block, add:

```tsx
{isMobile && (
<>
<ToolbarSeparator />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md text-sm hover:bg-muted"
aria-label="More formatting options"
>
<IconDotsVertical size={16} />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{/* ── Styles ── */}
<DropdownMenuLabel>Styles</DropdownMenuLabel>
{STYLE_OPTIONS.map((opt) => (
<DropdownMenuItem
key={opt.label}
onClick={() =>
handleBlockType(
opt.type,
opt.headingLevel
? { headingLevel: opt.headingLevel }
: undefined,
)
}
>
<span className={opt.className}>{opt.label}</span>
</DropdownMenuItem>
))}
<DropdownMenuSeparator />

{/* ── Insert ── */}
<DropdownMenuLabel>Insert</DropdownMenuLabel>
<DropdownMenuItem onClick={handleInsertLink}>
<IconLink size={16} className="mr-2" />
Link
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
/* Trigger file input for image upload */
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file && editor) {
await insertImageFromFile(editor, file);
}
};
input.click();
}}
>
<IconPhoto size={16} className="mr-2" />
Image
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
editor?.insertTable(3, 3);
editor?.focus();
}}
>
<IconTable size={16} className="mr-2" />
Table (3×3)
</DropdownMenuItem>
<DropdownMenuSeparator />

{/* ── Align ── */}
<DropdownMenuLabel>Align</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleAlign("left")}>
<IconAlignLeft size={16} className="mr-2" />
Left
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleAlign("center")}>
<IconAlignCenter size={16} className="mr-2" />
Center
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleAlign("right")}>
<IconAlignRight size={16} className="mr-2" />
Right
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleAlign("justify")}>
<IconAlignJustified size={16} className="mr-2" />
Justify
</DropdownMenuItem>
<DropdownMenuSeparator />

{/* ── List ── */}
<DropdownMenuLabel>List</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => {
editor?.toggleList("ordered");
editor?.focus();
}}
>
<IconListNumbers size={16} className="mr-2" />
Numbered list
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
editor?.toggleList("unordered");
editor?.focus();
}}
>
<IconList size={16} className="mr-2" />
Bulleted list
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
editor?.outdent();
editor?.focus();
}}
>
<IconIndentDecrease size={16} className="mr-2" />
Decrease indent
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
editor?.indent();
editor?.focus();
}}
>
<IconIndentIncrease size={16} className="mr-2" />
Increase indent
</DropdownMenuItem>
<DropdownMenuSeparator />

{/* ── Export ── */}
<DropdownMenuItem onClick={handleExportDocx} disabled={!editor || exporting}>
<IconFileDownload size={16} className="mr-2" />
Export as DOCX
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
)}
```

- [ ] **Step 2: Run verify:fast**

Run: `cd /Users/hackerwins/Development/wafflebase/wafflebase && pnpm verify:fast`
Expected: PASS

- [ ] **Step 3: Manual test — resize browser to < 768px**

Open the Docs editor, resize browser width below 768px:
- Verify: Only Undo, Redo, Bold, Italic, Underline, Text Color, Highlight Color, and ⋮ button visible
- Verify: ⋮ menu opens and shows Styles, Insert, Align, List, Export sections
- Verify: Each menu item triggers the correct action
- Verify: Desktop (>768px) shows all items as before

- [ ] **Step 4: Commit**

```bash
git add packages/frontend/src/app/docs/docs-formatting-toolbar.tsx
git commit -m "Add mobile overflow menu to Docs formatting toolbar

Apply the same mobile-responsive pattern used in Sheets: on viewports
narrower than 768px, show only core formatting tools (undo/redo, bold,
italic, underline, text/highlight color) inline and collapse styles,
insert, alignment, list, and export actions into a ⋮ overflow menu."
```

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

Add the paired lessons file for this task.

This is a non-trivial task, but only the -todo.md file was added. Please add docs/tasks/active/20260415-docs-mobile-toolbar-lessons.md alongside it.

As per coding guidelines, "For non-trivial tasks, use paired files in docs/tasks/active/: YYYYMMDD-<slug>-todo.md and YYYYMMDD-<slug>-lessons.md."

🧰 Tools
🪛 LanguageTool

[style] ~72-~72: The adverb ‘always’ is usually put before the verb ‘Keep’.
Context: ...Export DOCX button (lines 764-776) Keep always visible: Undo, Redo, separator, Bold, I...

(ADVERB_WORD_ORDER)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/tasks/active/20260415-docs-mobile-toolbar-todo.md` around lines 1 - 253,
Create a paired lessons file for the existing todo by adding a new markdown file
named 20260415-docs-mobile-toolbar-lessons.md alongside the todo; in that file
include a short summary of what was implemented (mobile overflow menu for
DocsFormattingToolbar), key implementation decisions (useIsMobile hook,
DropdownMenu sections, kept core tools inline), any gotchas or edge-cases
encountered (event handlers, file input for image upload, editor focus calls),
verification steps you ran (lint, verify:fast, manual mobile/desktop checks) and
a short checklist of follow-ups or known issues for future work so reviewers
have context for the change.

Comment on lines +821 to +851
<DropdownMenuLabel>Insert</DropdownMenuLabel>
<DropdownMenuItem onClick={handleInsertLink}>
<IconLink size={16} className="mr-2" />
Link
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file && editor) {
await insertImageFromFile(editor, file);
}
};
input.click();
}}
>
<IconPhoto size={16} className="mr-2" />
Image
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
editor?.insertTable(3, 3);
editor?.focus();
}}
>
<IconTable size={16} className="mr-2" />
Table (3×3)
</DropdownMenuItem>

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 | 🟠 Major

Keep mobile Insert actions at feature parity with desktop.

Below 768px this stops exposing two existing flows: image-by-URL and variable table sizing. The desktop toolbar still supports both via InsertImageDropdown and TableDropdown, but the mobile menu now only uploads from disk and inserts a fixed 3×3 table. That is a behavior regression, not just a responsive rearrangement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/src/app/docs/docs-formatting-toolbar.tsx` around lines 821
- 851, The mobile Insert dropdown removed the existing image-by-URL and
variable-size table flows and hard-coded disk upload + a 3×3 table; restore
parity by replacing the inline image/file handler and fixed table action with
the same components/handlers used on desktop—use InsertImageDropdown (or the
same URL dialog flow used there) alongside the existing insertImageFromFile
logic (e.g., keep the file input branch) and replace the fixed
editor?.insertTable(3,3) call with the TableDropdown component or the same
table-size picker logic so users can choose table dimensions; ensure to call
editor?.focus() after insertion as before.

Comment on lines +888 to +892
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleExportDocx} disabled={!editor || exporting}>
<IconFileDownload size={16} className="mr-2" />
Export as DOCX
</DropdownMenuItem>

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

Add the missing Export section header.

The overflow has labeled Styles / Insert / Align / List sections, but Export is just a trailing item after a separator. The PR objective/test plan calls for an Export section as well.

✏️ Proposed fix
               <DropdownMenuSeparator />
+              <DropdownMenuLabel>Export</DropdownMenuLabel>
               <DropdownMenuItem onClick={handleExportDocx} disabled={!editor || exporting}>
                 <IconFileDownload size={16} className="mr-2" />
                 Export as DOCX
📝 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
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleExportDocx} disabled={!editor || exporting}>
<IconFileDownload size={16} className="mr-2" />
Export as DOCX
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Export</DropdownMenuLabel>
<DropdownMenuItem onClick={handleExportDocx} disabled={!editor || exporting}>
<IconFileDownload size={16} className="mr-2" />
Export as DOCX
</DropdownMenuItem>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/src/app/docs/docs-formatting-toolbar.tsx` around lines 888
- 892, Add a labeled "Export" section header before the export menu item so the
toolbar matches other labeled groups; insert a DropdownMenuLabel (or the
existing label component used for "Styles"/"Insert"/"Align"/"List") immediately
after the DropdownMenuSeparator and above the DropdownMenuItem that calls
handleExportDocx, keeping the disabled and onClick props as-is so the export
entry remains functional and visually grouped.

@hackerwins

Copy link
Copy Markdown
Collaborator Author

Regarding the review comments:

Lessons file — Skipping for this small change.

Mobile Insert parity (image-by-URL, variable table sizing) — Intentionally simplified for mobile in this PR. The grid picker is hover-based (not touch-friendly) and image-by-URL is a rare mobile use case. Will address in a follow-up if needed.

Export section label — Export is a single item, so the label adds visual noise. Will reconsider if more export options are added.

@hackerwins
hackerwins merged commit 80e6c06 into main Apr 14, 2026
4 checks passed
@hackerwins
hackerwins deleted the docs-mobile-toolbar branch April 14, 2026 23:35
hackerwins added a commit that referenced this pull request Apr 19, 2026
- random-axis-id (#127)
- axis-id-selection (#130)
- docs-image-editing Phase 1 (#121, #123)
- nested-tables (#128, #129)
- docs-mobile-toolbar (#132)
- release-v0.3.3 (#131)
- docs-image-editing lessons
@hackerwins hackerwins mentioned this pull request Apr 19, 2026
1 task
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