Skip to content

feat(floating-workspace): add a dedicated titlebar grab handle - #46

Merged
nplez1 merged 1 commit into
nplez1/mainfrom
nplez1/Workspace-Grab
Oct 1, 2026
Merged

nplez1 merged 1 commit into
nplez1/mainfrom
nplez1/Workspace-Grab

Conversation

@nplez1

@nplez1 nplez1 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​150 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​150
Prod 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​22 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​22

ELI5

The floating workspace window is moved by dragging its titlebar. When you have a lot of tabs open, the tabs cover the whole titlebar, so there is nowhere left to grab and the window feels stuck. This adds a small dedicated handle at the far left of the titlebar that you can always grab to move the window.

What Changed

Before: the floating workspace titlebar is the drag surface, but every press inside it is run through a "no-drag" selector, and tabs match that selector (a press on a tab starts a tab reorder). With enough tabs open, the entire titlebar is tab surface and the panel cannot be moved.

After: a fixed-width (24px) handle sits at the far-left edge of the titlebar, outside the tab strip. It shows a grip icon, uses a grab cursor, and always starts a panel move. It draws the same right border as the inter-tab dividers, so the strip reads as one continuous set of separators.

Mechanism: the handle is rendered as the first child of the titlebar in FloatingTerminalPanelSurface, before the TabBar. Pointer events bubble to the existing titlebar drag handlers (onPointerDown/Move/Up), so no new drag logic is added. It is a plain div — the no-drag selector includes button — and the tab strip is a flex sibling, so the handle's width is reserved up front and can never be covered by tabs.

Why

The window move gesture already exists; the only defect is that nothing is guaranteed to be left uncovered as the tab count grows. The alternatives were worse:

  • Pad the strip / shrink tabs — still fully covered once there are enough tabs; the failure just moves to a higher tab count.
  • A keyboard move path — does not help pointer users, and the panel already has shortcuts; this is a pointer affordance.
  • A handle inside the tab strip — competes with the strip's flex-[0_1_auto] layout and with dnd-kit, and could itself become a tab-adjacent reorder target.
  • A sibling handle outside the strip — reserves width deterministically, adds no drag state, and reuses the existing titlebar handlers. That is what this does.

Linked Issue

N/A — fork-local UI change; no tracking issue exists.

Visual Proof

Light mode, same titlebar with more tabs than fit. Swipe/tab between the two:

Before — strip reaches the left edge, nothing to grab After — grip handle owns the far-left edge
before light after light

Dark mode:

Before After
before dark after dark

Screenshots come from a headless Electron renderer (ORCA_BACKGROUND_LAUNCH=1, no visible window, no focus change).

Testing

  • pnpm test src/renderer/src/components/floating-terminal — 18 files / 148 tests pass, including the new handle unit test and the titlebar-order regression assertion.
  • tests/e2e/floating-workspace-titlebar-grab.spec.ts — hidden-renderer E2E that opens the floating workspace with 14 tabs, asserts the handle is visible and left of the tab strip, then drags it with real mouse input and asserts the panel moved (>30px).
  • pnpm tc, npx oxlint, oxfmt --check, and pnpm run check:code-quality:changed (0 new findings, design-system gate included).

Platforms: macOS (headless Electron E2E). The change is renderer-only DOM/CSS with no platform-specific behavior — no modifier-key handling, no file paths, no shells — so it is platform-neutral; SSH/remote is unaffected because the drag is in-page pointer handling over the overlay, not an Electron window API.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

AI Disclosure

Implemented with DeepSeek Flash via the pi coding agent.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: no new IPC, network, or filesystem access.
  • Cross-platform (Linux/Windows/Mac): pure Tailwind tokens and in-page pointer events; no platform branches.
  • Remote SSH / Mobile: unaffected; the panel's move is local overlay geometry.
  • Backwards compatibility: additive DOM/CSS only; no wire or persisted-state change.
  • Performance: one static 24px flex child; no new listeners or renders.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

The floating workspace titlebar doubles as its move target, but once enough
tabs fill the strip every press lands on a sortable tab — a no-drag target —
so there is nowhere left to grab and the panel cannot be moved.

Reserve a fixed-width handle at the far left of the titlebar, outside the tab
strip, so it stays pressable no matter how many tabs are open. It relies on
pointer events bubbling to the existing titlebar drag handlers, stays a plain
div because `button` is a no-drag selector, and draws the same right border as
the inter-tab dividers so the strip reads as one set of separators.

Covered by unit tests for the handle's drag-target classification and a hidden
renderer E2E that opens the panel with more tabs than fit, drags the handle,
and asserts the panel moves.
@nplez1
nplez1 merged commit a817aa8 into nplez1/main Oct 1, 2026
37 checks passed
nplez1 added a commit that referenced this pull request Oct 1, 2026
The floating workspace titlebar doubles as its move target, but once enough
tabs fill the strip every press lands on a sortable tab — a no-drag target —
so there is nowhere left to grab and the panel cannot be moved.

Reserve a fixed-width handle at the far left of the titlebar, outside the tab
strip, so it stays pressable no matter how many tabs are open. It relies on
pointer events bubbling to the existing titlebar drag handlers, stays a plain
div because `button` is a no-drag selector, and draws the same right border as
the inter-tab dividers so the strip reads as one set of separators.

Covered by unit tests for the handle's drag-target classification and a hidden
renderer E2E that opens the panel with more tabs than fit, drags the handle,
and asserts the panel moves.
nplez1 added a commit that referenced this pull request Oct 6, 2026
The floating workspace titlebar doubles as its move target, but once enough
tabs fill the strip every press lands on a sortable tab — a no-drag target —
so there is nowhere left to grab and the panel cannot be moved.

Reserve a fixed-width handle at the far left of the titlebar, outside the tab
strip, so it stays pressable no matter how many tabs are open. It relies on
pointer events bubbling to the existing titlebar drag handlers, stays a plain
div because `button` is a no-drag selector, and draws the same right border as
the inter-tab dividers so the strip reads as one set of separators.

Covered by unit tests for the handle's drag-target classification and a hidden
renderer E2E that opens the panel with more tabs than fit, drags the handle,
and asserts the panel moves.
@nplez1
nplez1 deleted the nplez1/Workspace-Grab branch October 24, 2026 03:23
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