Skip to content

Improve/toolbar button states#23

Merged
Kripu77 merged 13 commits intomainfrom
improve/toolbar-button-states
Mar 7, 2026
Merged

Improve/toolbar button states#23
Kripu77 merged 13 commits intomainfrom
improve/toolbar-button-states

Conversation

@Kripu77
Copy link
Owner

@Kripu77 Kripu77 commented Mar 7, 2026

Summary by CodeRabbit

  • New Features

    • Tool change event system added to synchronize active tool state and enable UI reactions when tools change.
  • Style

    • Toolbar rebuilt to use per-tool buttons with clearer active-state visuals and a new selected-button style.
  • Refactor

    • Centralized storage keys and custom event names into shared constants for consistent persistence and event handling; grid and hand-drawn settings now use these keys.
  • Tests

    • Tests updated to assert the new active-state attribute for tool buttons and to accommodate the tool-change event behavior.

@vercel
Copy link

vercel bot commented Mar 7, 2026

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

Project Deployment Actions Updated (UTC)
thinkix Ready Ready Preview, Comment Mar 7, 2026 6:45am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Warning

Rate limit exceeded

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

⌛ 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.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 414c7fa3-8468-42d4-ad06-4aaefaf03259

📥 Commits

Reviewing files that changed from the base of the PR and between fb282c9 and 2b9382f.

📒 Files selected for processing (1)
  • features/toolbar/components/BoardToolbar.tsx
📝 Walkthrough

Walkthrough

Adds centralized CUSTOM_EVENTS and STORAGE_KEYS constants, a new withToolSync plugin that emits TOOL_CHANGE on pointer-type transitions, integrates the plugin into the canvas and tests, replaces hardcoded storage/event strings, refactors toolbar buttons to per-tool Buttons with SELECTED_BUTTON_CLASS, and updates styles and tests.

Changes

Cohort / File(s) Summary
Event & Storage Constants
shared/constants/events.ts, shared/constants/storage.ts, shared/constants/index.ts
Add CUSTOM_EVENTS.TOOL_CHANGE, STORAGE_KEYS (HANDDRAWN, GRID_BACKGROUND), corresponding types, and re-export the modules.
Tool Sync Plugin
features/board/plugins/with-tool-sync.ts
New withToolSync plugin patches BoardTransforms.updatePointerType, tracks previous pointer per-board (WeakMap), and dispatches CUSTOM_EVENTS.TOOL_CHANGE when tools change; includes error handling.
Canvas & Hooks Integration
features/board/components/BoardCanvas.tsx, features/board/hooks/use-board-state.tsx, features/board/plugins/with-sticky-note.ts
Integrate withToolSync into plugin pipeline; replace hardcoded 'thinkix:toolchange' and storage keys with CUSTOM_EVENTS and STORAGE_KEYS; remove redundant manual dispatch in sticky-note plugin.
Grid Storage Migration
features/board/grid/grid-plugin.ts
Replace local GRID_STORAGE_KEY usages with STORAGE_KEYS.GRID_BACKGROUND for get/set/remove localStorage operations.
Toolbar UI Refactor
features/toolbar/components/BoardToolbar.tsx
Replace ToggleGroup with per-tool Button components, add createToolHandler, apply SELECTED_BUTTON_CLASS for active tools, and standardize onPointerDown handlers.
Styling Constants
shared/constants/styles.ts
Simplify TOOLBAR_ITEM_CLASS, add cursor-pointer to BUTTON_CLASS, and add SELECTED_BUTTON_CLASS for active state styling.
Tests
tests/components/board-toolbar-mobile.test.tsx, tests/e2e/text-sticky.spec.ts, tests/unit/sticky-note.test.ts, tests/e2e/toolbar-selection-indicator.spec.ts
Update mocks to include CUSTOM_EVENTS and SELECTED_BUTTON_CLASS; change selection assertions from aria-checked to aria-pressed; adjust tests to initialize/apply with-tool-sync where needed.
Docs
CLAUDE.md
Document Tool Change Event System, usage via window.addEventListener(CUSTOM_EVENTS.TOOL_CHANGE, ...), lifecycle and implementation notes.

Sequence Diagram

sequenceDiagram
    actor User
    participant Toolbar as BoardToolbar
    participant Canvas as BoardCanvas
    participant Transforms as BoardTransforms
    participant Plugin as with-tool-sync
    participant Window as window

    User->>Toolbar: pointer down on tool button
    Toolbar->>Canvas: handleToolChange(toolId)
    Canvas->>Transforms: updatePointerType(newType)
    Transforms->>Plugin: (patched) intercept updatePointerType
    alt pointer type changed
        Plugin->>Plugin: update WeakMap(prevPointer)
        Plugin->>Window: dispatchEvent(CUSTOM_EVENTS.TOOL_CHANGE)
    end
    Window->>Window: notify listeners (use-board-state, UI)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped to the toolbar, buttons in a row—
A TOOL_CHANGE hooray! now the listeners know.
WeakMaps keep memory of tools I let go,
Events on the window help the UI grow.
Thump-thump, constants tidy, off we go! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title is vague and generic—'Improve/toolbar button states' uses non-specific phrasing that doesn't clearly convey what was actually changed in the pull request. Use a more specific title that describes the actual change, such as 'Replace toolbar ToggleGroup with Button components for tool selection' or 'Add Tool Change Event System and refactor toolbar buttons'.
✅ Passed checks (1 passed)
Check name Status Explanation
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve/toolbar-button-states

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

@github-actions
Copy link

github-actions bot commented Mar 7, 2026

⚠️ Test Results

Metric Coverage
Lines 0%
Functions 93.3%
Branches 78.86%
Statements 91.26%
Average 65.9%

📦 Download Coverage Report

How to view coverage report
  1. Download the coverage artifact from the link above
  2. Extract the ZIP file
  3. Open index.html in your browser

Commit: 93963854d16ee1fd27d9d39763824351f7e08656

@Kripu77 Kripu77 marked this pull request as ready for review March 7, 2026 06:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
features/toolbar/components/BoardToolbar.tsx (3)

130-141: Consider adding aria-pressed for accessibility consistency.

Other tool buttons have aria-pressed to indicate active state, but the shapes dropdown trigger is missing this attribute. While it's a dropdown trigger, it still represents an active tool state when a shape is selected.

♿ Suggested accessibility enhancement
              <Button
                variant="ghost"
                size="icon"
                className={`${BUTTON_CLASS} ${buttonSizeClass} flex items-center justify-center ${isShapeActive ? SELECTED_BUTTON_CLASS : ''}`}
                aria-label="Shapes"
+               aria-pressed={isShapeActive}
                data-testid="shapes-dropdown-trigger"
              >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/toolbar/components/BoardToolbar.tsx` around lines 130 - 141, Add an
aria-pressed attribute to the shapes dropdown trigger Button so screen readers
can detect the active tool state: update the DropdownMenuTrigger/Button
(symbols: DropdownMenuTrigger, Button, BUTTON_CLASS, buttonSizeClass) to include
aria-pressed={isShapeActive} (ensuring it reflects the isShapeActive boolean and
remains consistent with SELECTED_BUTTON_CLASS usage and
data-testid="shapes-dropdown-trigger").

232-245: Add aria-label and aria-pressed for accessibility.

The handdrawn toggle button is missing aria-label and aria-pressed attributes, unlike other toolbar buttons.

♿ Suggested accessibility enhancement
              <Button
                variant="ghost"
                size="icon"
                className={`${BUTTON_CLASS} ${buttonSizeClass} flex items-center justify-center ${handdrawn ? SELECTED_BUTTON_CLASS : ''}`}
+               aria-label={handdrawn ? 'Disable Handdrawn Mode' : 'Enable Handdrawn Mode'}
+               aria-pressed={handdrawn}
                onPointerDown={(e: React.PointerEvent) => {
                  e.preventDefault();
                  e.stopPropagation();
                  toggleHanddrawn();
                }}
              >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/toolbar/components/BoardToolbar.tsx` around lines 232 - 245, The
handdrawn toggle Button (the Button element wrapping HANDRAWN_ICON) lacks
accessibility attributes; add an appropriate aria-label (e.g., "Toggle
hand-drawn mode") and aria-pressed bound to the handdrawn state
(aria-pressed={handdrawn}) on that Button so screen readers recognize it as a
toggle, keeping the existing onPointerDown handler and classes; follow the same
attribute pattern used by other toolbar buttons in this component (see Button,
toggleHanddrawn, handdrawn, HANDRAWN_ICON).

264-306: Add aria-label to selection action buttons.

The Duplicate and Delete buttons are missing aria-label attributes for screen reader accessibility.

♿ Suggested accessibility enhancement
                  <Button
                    variant="ghost"
                    size="icon"
                    className={`${BUTTON_CLASS} ${buttonSizeClass} flex items-center justify-center`}
+                   aria-label="Duplicate"
                    onPointerDown={(e: React.PointerEvent) => {
                  <Button
                    variant="ghost"
                    size="icon"
                    className={`${BUTTON_CLASS} ${buttonSizeClass} flex items-center justify-center hover:bg-destructive/10 hover:text-destructive`}
+                   aria-label="Delete"
                    onPointerDown={(e: React.PointerEvent) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/toolbar/components/BoardToolbar.tsx` around lines 264 - 306, The
Duplicate and Delete icon-only action Buttons in BoardToolbar (the Button
elements wrapping Copy and Trash2 icons) lack aria-labels; add
aria-label="Duplicate" to the Button that calls duplicateElements(board) and
aria-label="Delete" to the Button that calls deleteFragment(board) so screen
readers can announce the action, keeping the existing onPointerDown handlers and
Tooltip/TooltipTrigger structure intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@features/board/plugins/with-sticky-note.ts`:
- Around line 11-12: Remove the duplicate CUSTOM_EVENTS.TOOL_CHANGE emission
that occurs after sticky-note completion: locate the sticky-note completion
logic in with-sticky-note (the code that switches pointer via
BoardTransforms.updatePointerType(board, PlaitPointerType.selection) and then
dispatches CUSTOM_EVENTS.TOOL_CHANGE again) and delete that extra dispatch; rely
on BoardTransforms.updatePointerType (and the installed withToolSync) to emit
the TOOL_CHANGE event automatically. Ensure no other code in with-sticky-note
still manually dispatches CUSTOM_EVENTS.TOOL_CHANGE when calling
BoardTransforms.updatePointerType.

---

Nitpick comments:
In `@features/toolbar/components/BoardToolbar.tsx`:
- Around line 130-141: Add an aria-pressed attribute to the shapes dropdown
trigger Button so screen readers can detect the active tool state: update the
DropdownMenuTrigger/Button (symbols: DropdownMenuTrigger, Button, BUTTON_CLASS,
buttonSizeClass) to include aria-pressed={isShapeActive} (ensuring it reflects
the isShapeActive boolean and remains consistent with SELECTED_BUTTON_CLASS
usage and data-testid="shapes-dropdown-trigger").
- Around line 232-245: The handdrawn toggle Button (the Button element wrapping
HANDRAWN_ICON) lacks accessibility attributes; add an appropriate aria-label
(e.g., "Toggle hand-drawn mode") and aria-pressed bound to the handdrawn state
(aria-pressed={handdrawn}) on that Button so screen readers recognize it as a
toggle, keeping the existing onPointerDown handler and classes; follow the same
attribute pattern used by other toolbar buttons in this component (see Button,
toggleHanddrawn, handdrawn, HANDRAWN_ICON).
- Around line 264-306: The Duplicate and Delete icon-only action Buttons in
BoardToolbar (the Button elements wrapping Copy and Trash2 icons) lack
aria-labels; add aria-label="Duplicate" to the Button that calls
duplicateElements(board) and aria-label="Delete" to the Button that calls
deleteFragment(board) so screen readers can announce the action, keeping the
existing onPointerDown handlers and Tooltip/TooltipTrigger structure intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e6c3f2e-83d8-4604-bc6a-17c07f6153be

📥 Commits

Reviewing files that changed from the base of the PR and between dc1f337 and e2c5f08.

📒 Files selected for processing (14)
  • CLAUDE.md
  • features/board/components/BoardCanvas.tsx
  • features/board/grid/grid-plugin.ts
  • features/board/hooks/use-board-state.tsx
  • features/board/plugins/with-sticky-note.ts
  • features/board/plugins/with-tool-sync.ts
  • features/toolbar/components/BoardToolbar.tsx
  • shared/constants/events.ts
  • shared/constants/index.ts
  • shared/constants/storage.ts
  • shared/constants/styles.ts
  • tests/components/board-toolbar-mobile.test.tsx
  • tests/e2e/text-sticky.spec.ts
  • tests/unit/sticky-note.test.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@tests/e2e/toolbar-selection-indicator.spec.ts`:
- Around line 75-79: The test currently exits without assertion when
imageButton.count() === 0; update the test to fail or be skipped explicitly
instead of silently passing: assert that imageButton.count() is greater than 0
(e.g., expect(await imageButton.count()).toBeGreaterThan(0)) before interacting,
or call test.skip/test.fixme under the same feature flag that hides the Image
tool; ensure you modify the block referencing imageButton and the surrounding
test so the absence of the Image button causes a deterministic skip or a failing
assertion rather than a silent pass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 660951a3-7e22-4d5b-be62-c5066dc89154

📥 Commits

Reviewing files that changed from the base of the PR and between e2c5f08 and 4788869.

📒 Files selected for processing (1)
  • tests/e2e/toolbar-selection-indicator.spec.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
features/toolbar/components/BoardToolbar.tsx (2)

271-275: ⚠️ Potential issue | 🟠 Major

Don’t execute duplicate/delete on press-down.

Running these mutations in onPointerDown removes the normal “press, drag away, release to cancel” behavior and makes accidental duplicate/delete much easier, especially on touch. Fire them on onClick instead.

Suggested fix
-                    onPointerDown={(e: React.PointerEvent) => {
-                      e.preventDefault();
-                      e.stopPropagation();
-                      duplicateElements(board);
-                    }}
+                    onPointerDown={suppressBoardPointerDown}
+                    onClick={() => duplicateElements(board)}
...
-                    onPointerDown={(e: React.PointerEvent) => {
-                      e.preventDefault();
-                      e.stopPropagation();
-                      deleteFragment(board);
-                    }}
+                    onPointerDown={suppressBoardPointerDown}
+                    onClick={() => deleteFragment(board)}

Also applies to: 294-298

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

In `@features/toolbar/components/BoardToolbar.tsx` around lines 271 - 275, The
pointer handlers for the duplicate/delete actions in BoardToolbar.tsx are
currently invoking duplicateElements(board) and deleteElements(board) inside
onPointerDown, which prevents the normal drag-away-to-cancel UX; change those
handlers to fire on onClick instead (replace onPointerDown with onClick for the
duplicateElements and deleteElements invocations) and remove the
preventDefault/stopPropagation calls from the pointer-down handlers so the
actions only run on click/tap release; apply the same change to the other
similar block (the duplicate/delete pair around the other handler region).

232-245: ⚠️ Potential issue | 🟠 Major

Add accessible name and pressed state to the handdrawn toggle.

This is an icon-only toggle, but it has no aria-label or aria-pressed. Screen readers currently can't tell what the control does or whether handdrawn mode is enabled.

Suggested fix
               <Button
                 variant="ghost"
                 size="icon"
                 className={`${BUTTON_CLASS} ${buttonSizeClass} flex items-center justify-center ${handdrawn ? SELECTED_BUTTON_CLASS : ''}`}
+                aria-label={handdrawn ? 'Disable Handdrawn Mode' : 'Enable Handdrawn Mode'}
+                aria-pressed={handdrawn}
                 onPointerDown={(e: React.PointerEvent) => {
                   e.preventDefault();
                   e.stopPropagation();
                   toggleHanddrawn();
                 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/toolbar/components/BoardToolbar.tsx` around lines 232 - 245, The
handdrawn icon-only Button lacks accessible labeling and state; update the
Button (inside TooltipTrigger) to include an explicit accessible name and
pressed state by adding an aria-label (e.g., aria-label="Toggle handdrawn mode"
or a localized string) and aria-pressed={handdrawn} so screen readers know the
control purpose and whether handdrawn mode is enabled; keep the existing
onPointerDown/toggleHanddrawn handler and class toggles (BUTTON_CLASS,
SELECTED_BUTTON_CLASS, iconSizeClass, HANDRAWN_ICON) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@features/toolbar/components/BoardToolbar.tsx`:
- Around line 61-65: The tool buttons currently change state inside the pointer
event handler (createToolHandler) which prevents keyboard activation; refactor
so that createToolHandler only prevents board pointer propagation (keep
e.preventDefault()/e.stopPropagation() on onPointerDown) but move the actual
call to handleToolChange(toolId) into an onClick handler attached to the same
button; update every tool button that uses createToolHandler (and the
occurrences flagged in the review) so pointer-down only suppresses board events
and onClick performs the tool state change.

---

Outside diff comments:
In `@features/toolbar/components/BoardToolbar.tsx`:
- Around line 271-275: The pointer handlers for the duplicate/delete actions in
BoardToolbar.tsx are currently invoking duplicateElements(board) and
deleteElements(board) inside onPointerDown, which prevents the normal
drag-away-to-cancel UX; change those handlers to fire on onClick instead
(replace onPointerDown with onClick for the duplicateElements and deleteElements
invocations) and remove the preventDefault/stopPropagation calls from the
pointer-down handlers so the actions only run on click/tap release; apply the
same change to the other similar block (the duplicate/delete pair around the
other handler region).
- Around line 232-245: The handdrawn icon-only Button lacks accessible labeling
and state; update the Button (inside TooltipTrigger) to include an explicit
accessible name and pressed state by adding an aria-label (e.g.,
aria-label="Toggle handdrawn mode" or a localized string) and
aria-pressed={handdrawn} so screen readers know the control purpose and whether
handdrawn mode is enabled; keep the existing onPointerDown/toggleHanddrawn
handler and class toggles (BUTTON_CLASS, SELECTED_BUTTON_CLASS, iconSizeClass,
HANDRAWN_ICON) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4516b281-0d15-49f8-b8e9-7f62c286009c

📥 Commits

Reviewing files that changed from the base of the PR and between 4788869 and f717c0f.

📒 Files selected for processing (2)
  • features/board/plugins/with-sticky-note.ts
  • features/toolbar/components/BoardToolbar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • features/board/plugins/with-sticky-note.ts

@Kripu77 Kripu77 merged commit d1525f5 into main Mar 7, 2026
9 checks passed
@Kripu77 Kripu77 deleted the improve/toolbar-button-states branch March 7, 2026 06:54
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