Skip to content

feat: add quick label selection mode for one-click annotations#272

Merged
backnotprop merged 6 commits intobacknotprop:mainfrom
grubmanItay:feat/quick-label-mode
Mar 12, 2026
Merged

feat: add quick label selection mode for one-click annotations#272
backnotprop merged 6 commits intobacknotprop:mainfrom
grubmanItay:feat/quick-label-mode

Conversation

@grubmanItay
Copy link
Contributor

Description

Follow-up to #268 — implements the "quick label selection mode" suggested by @backnotprop.


⚡ New Mode: Quick Label

Adds a 4th editor mode alongside Markup, Comment, and Redline. When active:

  • Drag-select text → floating label picker appears immediately (no toolbar intermediary)
  • Click a label → annotation created instantly
  • Alt+1..8 → apply label via keyboard
  • Escape / click outside → dismiss picker, remove highlight

Works with both input methods (Select and Pinpoint), including code blocks.


How It Works

Input Method Behavior
Select (drag) Highlight text → label picker appears above selection
Pinpoint (click) Click element → label picker appears above it
Code blocks Both input methods show picker anchored to the block

Mode persists across sessions via cookie storage (same as other modes).


Files Changed

File Change
packages/ui/types.ts Add 'quickLabel' to EditorMode union
packages/ui/utils/editorMode.ts Recognize new mode in persistence
packages/ui/components/QuickLabelDropdown.tsx NEW — extracted shared dropdown component
packages/ui/components/FloatingQuickLabelPicker.tsx NEW — portal-rendered floating picker with positioning, keyboard shortcuts, click-outside dismiss
packages/ui/components/AnnotationToolbar.tsx Import shared QuickLabelDropdown
packages/ui/components/Viewer.tsx quickLabel branch in CREATE handler + pinpoint handler + state management
packages/ui/components/AnnotationToolstrip.tsx New "Label" button (⚡) + warning color entry
packages/ui/components/KeyboardShortcuts.tsx Updated shortcut hint

grubmanItay and others added 2 commits March 10, 2026 13:48
Add preset label chips (Needs tests, Security concern, Break this up, etc.)
that allow instant annotation without typing. Includes ⚡ toolbar button,
Alt+1..8 keyboard shortcuts, label customization in Settings, and label
summary in export output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@backnotprop
Copy link
Owner

@grubmanItay really enjoying this feature. Great job.

I'm changing up some of the quick label orders. I'm happy to meet with you to discuss. We're also going to start a discussion.

#278

- Redesign FloatingQuickLabelPicker as a vertical context-menu style list
  with cursor-anchored positioning (appears at mouseup point, not selection center)
- Unify label dropdown: toolbar and quick-label mode now share the same
  FloatingQuickLabelPicker component (removed duplicate InlineQuickLabelDropdown)
- Fix above/below flip positioning (follow CommentPopover pattern with
  conditional translateY)
- Add label tips: optional instruction text on QuickLabel that gets injected
  into agent feedback as a blockquote below the label
- Add tip editor in Settings with three visual states (empty/editing/filled)
- Add "Missing overview" default label with a tip for requesting narrative context
- Extend keyboard shortcuts from Alt+1-8 to Alt+1-9
- Suppress input method toggle (Alt) when label picker is open
- Reorder default labels: Clarify this, Needs tests, Consider edge cases,
  Missing overview, Security concern, Break this up, Wrong order, Discuss first,
  Nice approach

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@backnotprop
Copy link
Owner

I'm gonna fix all these merge conflicts by the way.

Finalize the 10 default quick labels based on user feedback data:
clarify, overview, verify, example, patterns, alternatives, regression,
out-of-scope, tests, nice-approach. Each label gets a unique color
(added cyan and amber to the palette). Bare digit keys (1-0) now apply
labels when the picker is open, Alt+N still works everywhere. Tip editor
cursor starts at beginning for readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@backnotprop
Copy link
Owner

I added label tips because it's helpful for additional context in the feedback that the agent gets.

image

# Conflicts:
#	bun.lock
#	packages/ui/components/AnnotationToolbar.tsx
#	packages/ui/components/KeyboardShortcuts.tsx
#	packages/ui/components/Settings.tsx
#	packages/ui/components/Viewer.tsx
#	packages/ui/types.ts
#	packages/ui/utils/parser.ts
#	packages/ui/utils/quickLabels.ts
@backnotprop
Copy link
Owner

The positioning bug from the code review has been fixed — FloatingQuickLabelPicker was fully rewritten with correct conditional translateY(-100%) flipping (68a9bb8). Merge conflicts also resolved (54d167d).

@backnotprop
Copy link
Owner

Code review

Found 1 issue:

  1. Duplicate annotation on digit keypress when quick label picker is opened from toolbar -- When the picker is launched via the zap button in AnnotationToolbar, two independent window.addEventListener('keydown') handlers are active simultaneously. AnnotationToolbar's handler fires because showQuickLabels === true (line 119) and calls onQuickLabel. FloatingQuickLabelPicker's handler also fires (line 78) and calls onSelect, which chains to onQuickLabel again. preventDefault() does not prevent other window-level listeners from receiving the event -- only stopImmediatePropagation() would. The result is the label callback being invoked twice per keypress, creating a duplicate annotation.

    Fix: either skip digit handling in AnnotationToolbar when showQuickLabels is true (let the picker own it), or have the picker call stopImmediatePropagation().

    // Quick label by digit — Alt+N always works, bare digit when picker is open
    const isDigit = (e.code >= 'Digit1' && e.code <= 'Digit9') || e.code === 'Digit0';
    if (isDigit && !e.ctrlKey && !e.metaKey && (e.altKey || showQuickLabels)) {
    e.preventDefault();
    const digit = parseInt(e.code.slice(5), 10);
    const index = digit === 0 ? 9 : digit - 1;
    if (index < quickLabels.length) {
    onQuickLabel?.(quickLabels[index]);
    }
    return;
    }

    // Accept bare digit or Alt+digit — picker is open so digits mean labels
    const isDigit = (e.code >= 'Digit1' && e.code <= 'Digit9') || e.code === 'Digit0';
    if (isDigit && !e.ctrlKey && !e.metaKey) {
    e.preventDefault();
    const digit = parseInt(e.code.slice(5), 10);
    const index = digit === 0 ? 9 : digit - 1;
    if (index < quickLabels.length) {
    onSelect(quickLabels[index]);
    }
    }

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

…nd picker handlers

When the quick label picker is open from the toolbar's zap button,
let FloatingQuickLabelPicker own all keyboard input instead of both
components handling the same keypress.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@backnotprop backnotprop merged commit 9a23c24 into backnotprop:main Mar 12, 2026
5 checks passed
@grubmanItay grubmanItay deleted the feat/quick-label-mode branch March 12, 2026 09:03
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