feat: add visual grab tab to resize handle with rounded corners

Added a 28px × 64px grab tab that extends to the right of the resize
handle divider, providing a larger visual target when the sidebar is
collapsed or near-collapsed.

**Changes:**
- Thin 8px divider remains the draggable area
- Visual grab tab (28px × 64px) extends to right with:
  - Rounded corners on top-right and bottom-right (borderRadius)
  - Vertical knurled texture for grip appearance
  - Drop shadow for depth
  - Positioned absolutely, centered vertically
- Tab is visual decoration only (pointerEvents: none)
- Divider remains fully draggable

**Note:** Tab is not draggable itself (library limitation - child elements
outside parent bounds don't trigger drag). The 8px divider is the
interactive area. Tab provides visual affordance when sidebar is collapsed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-12 12:30:11 -06:00
parent 0770bf3e1a
commit 6e55d5add7
3 changed files with 80 additions and 67 deletions

View File

@ -0,0 +1,31 @@
# Resize Handle Tab Requirements
**CRITICAL: DO NOT MAKE THE ENTIRE HANDLE WIDE**
## What the user wants:
- A **thin 8px divider** for the resize handle (full height)
- A **small grab tab** (28px × 64px) that:
- Is centered vertically on the divider
- Extends to the RIGHT into the preview pane
- Has rounded corners on the top-right and bottom-right
- Has vertical knurled texture (ridges)
- Is DRAGGABLE
## What NOT to do:
❌ Make the entire PanelResizeHandle 36px wide
❌ Use clip-path to make a wide handle look thin
❌ Use pseudo-elements (_after) that extend outside the parent bounds (they don't participate in drag events)
## The problem:
- PanelResizeHandle only responds to drag events on itself
- Child elements positioned absolutely outside the parent's bounds don't trigger parent drag
- Pseudo-elements extending outside don't work either
## Possible solutions to try:
1. Check if PanelResizeHandle accepts hitAreaMargins prop (from library source)
2. Use a custom drag overlay that forwards events to the handle
3. Accept that the tab is visual-only and keep the 8px handle draggable
## User frustration level: HIGH
- This is the **THIRD TIME** making the handle wide when asked for a small tab
- User explicitly said "FUUUUUCK, no wide drag handles!"

View File

@ -1,68 +1,14 @@
{
"permissions": {
"allow": [
"Bash(npm run pre-commit:*)",
"Bash(git add:*)",
"Bash(git commit -m \"$(cat <<''EOF''\nfeat: add themed backgrounds and enhanced styling to 404 page\n\nTransform 404 page into a vibrant, playful experience with:\n\nThemed Backgrounds:\n- Each HTTP status code has custom gradient background\n- Animated radial glow effects that pulse with theme colors\n- Smooth transitions between themes (0.6s ease-in-out)\n- 14 unique color schemes matching easter egg personality\n\nEnhanced Typography:\n- Responsive font sizes (1.75rem mobile → 4rem desktop)\n- Black font weight for maximum impact\n- Dynamic text colors matching each theme\n- Glowing text shadows for easter egg modes\n- Tight letter spacing (-0.02em) for modern look\n\nNavigation Buttons:\n- Added emoji icons (🏠 🎮 ✨)\n- Lift-and-scale hover animation\n- Colored shadows matching button colors\n- Responsive sizing for mobile\n- Smooth cubic-bezier transitions\n\nResponsive Layout:\n- Increased spacing between abacus and text (2-4rem)\n- Mobile-optimized gaps and padding\n- Text pushed down to prevent overlap with large abacus\n- Smaller screens get appropriate scaling\n\nDynamic Hints:\n- Changes based on active easter egg\n- \"Try other codes...\" vs \"Click beads to discover more...\"\n- Themed text color and opacity\n- Italic, medium weight for subtle emphasis\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
"Bash(git push)",
"Bash(git commit:*)",
"Bash(git stash show:*)",
"Bash(npm run type-check:*)",
"Bash(git stash:*)",
"Bash(git pull:*)",
"Bash(git checkout:*)",
"Bash(pnpm remove:*)",
"Bash(git rm:*)",
"WebSearch",
"WebFetch(domain:platform.openai.com)",
"Bash(git rev-parse:*)",
"Bash(npx drizzle-kit:*)",
"Bash(npm run db:migrate:*)",
"Bash(ssh:*)",
"Bash(git log:*)",
"Bash(gh run list:*)",
"Bash(gh api:*)",
"Bash(curl:*)",
"Bash(gh run view:*)",
"Bash(npm install:*)",
"Bash(pnpm add:*)",
"WebFetch(domain:www.dynamsoft.com)",
"Bash(npm run format:*)",
"Bash(npm run lint:fix:*)",
"Bash(npm run lint)",
"Bash(find:*)",
"Bash(for i in {0..10})",
"Bash(do echo \"=== Checking stash@{$i} ===\")",
"Bash(done)",
"mcp__sqlite__list_tables",
"mcp__sqlite__describe_table",
"Bash(npm test:*)",
"mcp__sqlite__read_query",
"Bash(cat:*)",
"Bash(npm run lint:*)",
"Bash(git reset:*)",
"Bash(npx tsx:*)",
"Bash(npx tsc:*)",
"Bash(npx @pandacss/dev)",
"Bash(npm view:*)",
"Bash(xargs sed:*)",
"Bash(wc:*)",
"Bash(git push:*)",
"Bash(git cherry-pick:*)",
"Bash(pnpm install)",
"Bash(npx @biomejs/biome check:*)",
"Bash(node -e:*)",
"Bash(sqlite3:*)",
"Bash(npm run format:check:*)",
"Bash(npx biome:*)",
"Bash(git restore:*)",
"Bash(mcp__sqlite__describe_table:*)",
"Bash(~45 unique available. Consider: • Reduce to 1 pages • Increase digit range)",
"Bash(• Lower regrouping %\"\n\n**Next:** Problem space indicator in config panel showing live estimate\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")"
"WebFetch(domain:github.com)",
"WebFetch(domain:react-resizable-panels.vercel.app)"
],
"deny": [],
"ask": []
},
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": ["sqlite"]
"enabledMcpjsonServers": [
"sqlite"
]
}

View File

@ -77,19 +77,22 @@ export function AdditionWorksheetClient({
})
}
// Resize handle styles
// Resize handle - thin 8px divider
const resizeHandleStyles = css({
width: '8px',
bg: isDark ? 'gray.700' : 'gray.200',
height: '100%',
position: 'relative',
cursor: 'col-resize',
transition: 'background 0.2s',
zIndex: 10,
background: isDark ? 'gray.700' : 'gray.200',
_hover: {
bg: isDark ? 'brand.600' : 'brand.400',
background: isDark ? 'brand.600' : 'brand.400',
},
_active: {
bg: 'brand.500',
background: 'brand.500',
},
// Vertical grip dots
_before: {
content: '""',
position: 'absolute',
@ -98,14 +101,45 @@ export function AdditionWorksheetClient({
transform: 'translate(-50%, -50%)',
width: '3px',
height: '20px',
bg: isDark ? 'gray.500' : 'gray.400',
bg: isDark ? 'gray.400' : 'gray.500',
borderRadius: 'full',
boxShadow: isDark
? '0 -8px 0 0 rgb(107, 114, 128), 0 8px 0 0 rgb(107, 114, 128)'
: '0 -8px 0 0 rgb(156, 163, 175), 0 8px 0 0 rgb(156, 163, 175)',
? '0 -8px 0 0 rgb(156, 163, 175), 0 8px 0 0 rgb(156, 163, 175)'
: '0 -8px 0 0 rgb(107, 114, 128), 0 8px 0 0 rgb(107, 114, 128)',
pointerEvents: 'none',
zIndex: 2,
},
})
// Visual grab tab with rounded corners (non-interactive decoration)
const grabTabStyles = css({
position: 'absolute',
top: 'calc(50% - 32px)',
left: '8px',
width: '28px',
height: '64px',
background: isDark ? 'rgb(75, 85, 99)' : 'rgb(209, 213, 219)',
borderRadius: '0 8px 8px 0',
boxShadow: isDark ? '2px 2px 8px rgba(0, 0, 0, 0.3)' : '2px 2px 8px rgba(0, 0, 0, 0.15)',
pointerEvents: 'none',
// Knurled texture
backgroundImage: isDark
? `repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.1) 0px,
rgba(255, 255, 255, 0.1) 1px,
transparent 1px,
transparent 3px
)`
: `repeating-linear-gradient(
90deg,
rgba(0, 0, 0, 0.08) 0px,
rgba(0, 0, 0, 0.08) 1px,
transparent 1px,
transparent 3px
)`,
})
return (
<PageWithNav navTitle={t('navTitle')} navEmoji="📝">
<WorksheetConfigProvider formState={formState} updateFormState={updateFormState}>
@ -131,7 +165,9 @@ export function AdditionWorksheetClient({
<ConfigSidebar isSaving={isSaving} lastSaved={lastSaved} />
</Panel>
<PanelResizeHandle className={resizeHandleStyles} />
<PanelResizeHandle className={resizeHandleStyles}>
<div className={grabTabStyles} data-element="grab-tab" />
</PanelResizeHandle>
{/* Center Panel: Preview */}
<Panel defaultSize={75} minSize={60}>