fix(desktop): reserve titlebar drag region
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
MACOS_TAHOE_DARWIN_MAJOR,
|
||||
TITLEBAR_CONTROL_OFFSET_X,
|
||||
TITLEBAR_CONTROL_SIZE,
|
||||
TITLEBAR_DRAG_HANDLE_WIDTH,
|
||||
TITLEBAR_EDGE_INSET,
|
||||
TITLEBAR_FALLBACK_WINDOW_BUTTON_X,
|
||||
TITLEBAR_ICON_SIZE,
|
||||
@@ -22,6 +23,10 @@ describe('titlebar sizing', () => {
|
||||
expect(titlebarIconSizeCss()).toBe('13.9px')
|
||||
})
|
||||
|
||||
it('reserves a fixed native drag target beside titlebar tabs', () => {
|
||||
expect(TITLEBAR_DRAG_HANDLE_WIDTH).toBe(48)
|
||||
})
|
||||
|
||||
it('reserves width from abutting hit targets only', () => {
|
||||
expect(titlebarToolsWidthCss(4)).toBe('calc(4 * var(--titlebar-control-size))')
|
||||
})
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { HermesConnection } from '@/global'
|
||||
|
||||
export const TITLEBAR_HEIGHT = 34
|
||||
/** Permanent native drag target beside a top-edge tab strip. */
|
||||
export const TITLEBAR_DRAG_HANDLE_WIDTH = 48
|
||||
export const MACOS_TRAFFIC_LIGHTS_HEIGHT = 14
|
||||
/** Titlebar tool hit target (both axes). */
|
||||
export const TITLEBAR_CONTROL_SIZE = 24
|
||||
|
||||
@@ -53,10 +53,61 @@ afterEach(() => {
|
||||
root = null
|
||||
container = null
|
||||
disposePane = null
|
||||
globalThis.document.querySelectorAll('[data-titlebar-cluster]').forEach(element => element.remove())
|
||||
vi.restoreAllMocks()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
describe('TreeGroup', () => {
|
||||
it('keeps a fixed native drag handle beside crowded top-edge tabs', () => {
|
||||
const paneIds = ['terminal', 'terminal-2', 'terminal-3', 'terminal-4', 'terminal-5']
|
||||
|
||||
const disposers = paneIds.map(id =>
|
||||
registry.register({
|
||||
area: 'panes',
|
||||
data: { placement: 'main' },
|
||||
id,
|
||||
render: () => <div>{id}</div>,
|
||||
title: id
|
||||
})
|
||||
)
|
||||
|
||||
disposePane = () => disposers.forEach(dispose => dispose())
|
||||
vi.stubGlobal('CSS', { escape: (value: string) => value })
|
||||
vi.stubGlobal(
|
||||
'ResizeObserver',
|
||||
class {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
)
|
||||
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function () {
|
||||
if (this.matches('[data-titlebar-cluster="left"]')) {
|
||||
return { left: 0, right: 100 } as DOMRect
|
||||
}
|
||||
|
||||
if (this.matches('[data-titlebar-cluster="right"]')) {
|
||||
return { left: 700, right: 800 } as DOMRect
|
||||
}
|
||||
|
||||
return { left: 0, right: 800, width: 800 } as DOMRect
|
||||
})
|
||||
const leftControls = globalThis.document.createElement('div')
|
||||
leftControls.dataset.titlebarCluster = 'left'
|
||||
const rightControls = globalThis.document.createElement('div')
|
||||
rightControls.dataset.titlebarCluster = 'right'
|
||||
globalThis.document.body.append(leftControls, rightControls)
|
||||
|
||||
render(<TreeGroup leftEdge node={{ ...terminalGroup(false), panes: paneIds }} rightEdge topEdge />)
|
||||
|
||||
const handle = container!.querySelector<HTMLElement>('[data-window-drag-handle]')!
|
||||
expect(container!.querySelectorAll('[data-tree-tab]').length).toBe(paneIds.length)
|
||||
expect(handle).toBeTruthy()
|
||||
expect(handle.style.width).not.toBe('')
|
||||
expect(handle.className).toContain('shrink-0')
|
||||
})
|
||||
|
||||
it('keeps a top-edge strip inside its panel and yields native drag while moving a pane', () => {
|
||||
disposePane = registry.register({
|
||||
area: 'panes',
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { type CSSProperties, Fragment, type ReactNode, type RefObject, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { TITLEBAR_HEIGHT } from '@/app/shell/titlebar'
|
||||
import { TITLEBAR_DRAG_HANDLE_WIDTH, TITLEBAR_HEIGHT } from '@/app/shell/titlebar'
|
||||
import { ActionsContextMenu, type MenuKit, renderActionItem } from '@/components/ui/actions-menu'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { DecodeText } from '@/components/ui/decode-text'
|
||||
@@ -696,11 +696,18 @@ export function TreeGroup({
|
||||
</PaneTabStrip>
|
||||
</ZoneMenu>
|
||||
) : null}
|
||||
{topEdge && (!headerVisible || tabsBelowControls) && (
|
||||
{topEdge && (
|
||||
<div
|
||||
className="min-w-0 flex-1 self-start [-webkit-app-region:drag]"
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'self-start [-webkit-app-region:drag]',
|
||||
headerVisible ? 'shrink-0' : 'min-w-0 flex-1'
|
||||
)}
|
||||
data-window-drag-handle=""
|
||||
style={{ height: TITLEBAR_HEIGHT }}
|
||||
style={{
|
||||
height: TITLEBAR_HEIGHT,
|
||||
width: headerVisible ? TITLEBAR_DRAG_HANDLE_WIDTH : undefined
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{topEdge && (
|
||||
|
||||
Reference in New Issue
Block a user