fix(desktop): titlebar clusters — macOS Y nudge, 24px targets, 13.9px icons
Left cluster gets a macOS-only translate to sit on the traffic-light row. All titlebar tools use 24×24 hit areas with 13.9px Codicons (inline size beats unlayered codicon.css). Clusters share one flex shell with no gap — buttons abut and the hit target is the spacing.
This commit is contained in:
@@ -2,6 +2,7 @@ import type * as React from 'react'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { TitlebarIcon } from '@/app/shell/titlebar-icon'
|
||||
import { ZoomableImage } from '@/components/chat/zoomable-image'
|
||||
import { PageLoader } from '@/components/page-loader'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -28,7 +29,7 @@ import {
|
||||
urlSlugTitleLabel,
|
||||
useLinkTitle
|
||||
} from '@/lib/external-link'
|
||||
import { FileImage, FileText, FolderOpen, Link2, Loader2, RefreshCw } from '@/lib/icons'
|
||||
import { FileImage, FileText, FolderOpen, Link2 } from '@/lib/icons'
|
||||
import { downloadGatewayMediaFile, isRemoteGateway } from '@/lib/media'
|
||||
import { normalize } from '@/lib/text'
|
||||
import { fmtDayTime } from '@/lib/time'
|
||||
@@ -329,7 +330,7 @@ export function ArtifactsView({ setStatusbarItemGroup: _setStatusbarItemGroup, .
|
||||
size="icon-titlebar"
|
||||
variant="ghost"
|
||||
>
|
||||
{refreshing ? <Loader2 className="animate-spin" /> : <RefreshCw />}
|
||||
{refreshing ? <TitlebarIcon name="loading" spinning /> : <TitlebarIcon name="refresh" />}
|
||||
</Button>
|
||||
</Tip>
|
||||
}
|
||||
|
||||
@@ -746,13 +746,13 @@ export function ContribController() {
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-y-0 left-[calc(var(--titlebar-controls-left,14px)+(var(--titlebar-control-size,1.25rem)*2)+0.75rem)] right-[calc(var(--titlebar-tools-right,0.75rem)+var(--titlebar-tools-width,5.5rem)+0.75rem)] [-webkit-app-region:drag]"
|
||||
className="pointer-events-none absolute inset-y-0 left-[calc(var(--titlebar-controls-left,14px)+(var(--titlebar-control-size,24px)*2)+0.75rem)] right-[calc(var(--titlebar-tools-right,0.75rem)+var(--titlebar-tools-width,5.5rem)+0.75rem)] [-webkit-app-region:drag]"
|
||||
/>
|
||||
<TitlebarSlot
|
||||
area="titleBar.left"
|
||||
className="pointer-events-auto absolute z-10 flex w-max items-center gap-2 [-webkit-app-region:no-drag]"
|
||||
style={{
|
||||
left: 'max(calc(var(--workspace-left, 0px) + 0.5rem), calc(var(--titlebar-controls-left, 14px) + 2 * var(--titlebar-control-size, 1.25rem) + 1rem))'
|
||||
left: 'max(calc(var(--workspace-left, 0px) + 0.5rem), calc(var(--titlebar-controls-left, 14px) + 2 * var(--titlebar-control-size, 24px) + 1rem))'
|
||||
}}
|
||||
/>
|
||||
<TitlebarSlot
|
||||
@@ -764,7 +764,7 @@ export function ContribController() {
|
||||
className="pointer-events-auto absolute z-10 flex w-max items-center gap-2 [-webkit-app-region:no-drag]"
|
||||
style={{
|
||||
right:
|
||||
'max(calc(var(--workspace-right, 0px) + 0.5rem), calc(var(--titlebar-tools-right, 0.75rem) + 4 * (var(--titlebar-control-size, 1.25rem) + 0.25rem) + 0.5rem))'
|
||||
'max(calc(var(--workspace-right, 0px) + 0.5rem), calc(var(--titlebar-tools-right, 0.75rem) + 4 * var(--titlebar-control-size, 24px) + 0.5rem))'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -112,7 +112,7 @@ import { useSessionStateCache } from '../session/hooks/use-session-state-cache'
|
||||
import { startWorkspaceSession } from '../session/workspace-session-target'
|
||||
import { useOverlayRouting } from '../shell/hooks/use-overlay-routing'
|
||||
import { useWindowControlsOverlayWidth } from '../shell/hooks/use-window-controls-overlay-width'
|
||||
import { titlebarControlsPosition } from '../shell/titlebar'
|
||||
import { titlebarControlsPosition, titlebarControlsYNudge, titlebarToolsWidthCss } from '../shell/titlebar'
|
||||
import { TitlebarControls } from '../shell/titlebar-controls'
|
||||
import { UpdatesOverlay } from '../updates-overlay'
|
||||
|
||||
@@ -967,11 +967,6 @@ export function ContribWiring({ children }: { children: ReactNode }) {
|
||||
const rightTitlebarTools = useTitlebarToolContributions('right')
|
||||
const connection = useStore($connection)
|
||||
const controlsPos = titlebarControlsPosition(connection?.windowButtonPosition, Boolean(connection?.isFullscreen))
|
||||
// Exact vertical centering: titlebarControlsPosition() returns
|
||||
// (TITLEBAR_HEIGHT - TITLEBAR_CONTROL_HEIGHT) / 2, but TitlebarControls
|
||||
// also applies a hard translate-y-0.5 (+2px) to its clusters. Cancel that
|
||||
// constant so cluster center == bar center — measured, not eyeballed.
|
||||
const controlsTranslateY = 2
|
||||
// Windows/WSLg reserve native min/max/close on the right (AppShell parity:
|
||||
// prefer the live WCO measurement, fall back to the static reservation).
|
||||
const measuredOverlayWidth = useWindowControlsOverlayWidth()
|
||||
@@ -982,11 +977,11 @@ export function ContribWiring({ children }: { children: ReactNode }) {
|
||||
// sits ABOVE the grid, so AppShell's pane-width anchoring doesn't apply.
|
||||
const SYSTEM_TOOL_COUNT = 4
|
||||
const paneToolCount = rightTitlebarTools.filter(tool => !tool.hidden).length
|
||||
const systemToolsWidth = `calc(${SYSTEM_TOOL_COUNT} * (var(--titlebar-control-size) + 0.25rem))`
|
||||
const systemToolsWidth = titlebarToolsWidthCss(SYSTEM_TOOL_COUNT)
|
||||
|
||||
const titlebarToolsWidth =
|
||||
paneToolCount > 0
|
||||
? `calc(${systemToolsWidth} + ${paneToolCount} * (var(--titlebar-control-size) + 0.25rem))`
|
||||
? `calc(${systemToolsWidth} + ${titlebarToolsWidthCss(paneToolCount)})`
|
||||
: systemToolsWidth
|
||||
|
||||
return (
|
||||
@@ -996,7 +991,8 @@ export function ContribWiring({ children }: { children: ReactNode }) {
|
||||
style={
|
||||
{
|
||||
'--titlebar-controls-left': `${controlsPos.left}px`,
|
||||
'--titlebar-controls-top': `${controlsPos.top - controlsTranslateY}px`,
|
||||
'--titlebar-controls-top': `${controlsPos.top}px`,
|
||||
'--titlebar-controls-y-nudge': titlebarControlsYNudge(connection?.windowButtonPosition),
|
||||
'--titlebar-tools-right': titlebarToolsRight,
|
||||
'--titlebar-tools-width': titlebarToolsWidth,
|
||||
'--shell-preview-toolbar-gap': systemToolsWidth
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useStore } from '@nanostores/react'
|
||||
import { type CSSProperties, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { TitlebarIcon } from '@/app/shell/titlebar-icon'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { chatMessageText } from '@/lib/chat-messages'
|
||||
@@ -390,7 +390,7 @@ export function HudShell() {
|
||||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<Codicon name="screen-normal" />
|
||||
<TitlebarIcon name="screen-normal" />
|
||||
</Button>
|
||||
</Tip>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type CSSProperties, type ReactNode, useEffect } from 'react'
|
||||
|
||||
import { TITLEBAR_HEIGHT } from '@/app/shell/titlebar'
|
||||
import { TitlebarIcon } from '@/app/shell/titlebar-icon'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { translateNow } from '@/i18n'
|
||||
import { ESCAPE_PRIORITY, isTopEscapeLayer, pushEscapeLayer } from '@/lib/escape-layers'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
@@ -113,7 +113,7 @@ export function OverlayView({
|
||||
size="icon-titlebar"
|
||||
variant="ghost"
|
||||
>
|
||||
<Codicon name="close" size="1rem" />
|
||||
<TitlebarIcon name="close" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { hudTargetSessionId } from '@/app/hud/handoff'
|
||||
import { toggleLayoutEditMode } from '@/components/pane-shell/edit-mode'
|
||||
import { resetLayoutTree } from '@/components/pane-shell/tree/store'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { Tip, TipKeybindLabel } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
@@ -23,7 +22,8 @@ import {
|
||||
|
||||
import { appViewForPath, isOverlayView } from '../routes'
|
||||
|
||||
import { titlebarButtonClass } from './titlebar'
|
||||
import { titlebarButtonClass, titlebarIconSizeCss, titlebarToolClusterClass, TITLEBAR_ICON_BADGE_SCALE } from './titlebar'
|
||||
import { TitlebarIcon } from './titlebar-icon'
|
||||
|
||||
export interface TitlebarTool {
|
||||
id: string
|
||||
@@ -61,12 +61,12 @@ function LayoutGlyph({ modHeld }: { modHeld: boolean }) {
|
||||
return (
|
||||
<>
|
||||
<span className={cn('inline-flex', modHeld && 'group-hover/tool:hidden')}>
|
||||
<Codicon name="layout" />
|
||||
<TitlebarIcon name="layout" />
|
||||
</span>
|
||||
<span className={cn('relative hidden', modHeld && 'group-hover/tool:inline-flex')}>
|
||||
<Codicon name="layout" />
|
||||
<TitlebarIcon name="layout" />
|
||||
<span className="absolute -bottom-1 -right-1.5 grid place-items-center rounded-full bg-(--ui-bg-chrome) p-px">
|
||||
<Codicon className="-scale-x-100" name="refresh" size="0.5625rem" />
|
||||
<TitlebarIcon className="-scale-x-100" name="refresh" size={titlebarIconSizeCss(TITLEBAR_ICON_BADGE_SCALE)} />
|
||||
</span>
|
||||
</span>
|
||||
</>
|
||||
@@ -128,7 +128,7 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
const leftToolbarTools: TitlebarTool[] = [
|
||||
{
|
||||
actionId: 'view.toggleSidebar',
|
||||
icon: <Codicon name="layout-sidebar-left" />,
|
||||
icon: <TitlebarIcon name="layout-sidebar-left" />,
|
||||
id: 'sidebar',
|
||||
label: leftEdge.open ? t.titlebar.hideSidebar : t.titlebar.showSidebar,
|
||||
onSelect: () => {
|
||||
@@ -138,7 +138,7 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
},
|
||||
{
|
||||
actionId: 'view.flipPanes',
|
||||
icon: <Codicon name="arrow-swap" />,
|
||||
icon: <TitlebarIcon name="arrow-swap" />,
|
||||
id: 'flip-panes',
|
||||
label: t.titlebar.swapSidebarSides,
|
||||
onSelect: () => {
|
||||
@@ -151,7 +151,7 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
|
||||
const rightSidebarTool: TitlebarTool = {
|
||||
actionId: 'view.toggleRightSidebar',
|
||||
icon: <Codicon name="layout-sidebar-right" />,
|
||||
icon: <TitlebarIcon name="layout-sidebar-right" />,
|
||||
id: 'right-sidebar',
|
||||
label: rightEdge.open ? t.titlebar.hideRightSidebar : t.titlebar.showRightSidebar,
|
||||
onSelect: () => {
|
||||
@@ -188,7 +188,7 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
// crowds the ⌘⇧H hint off the tooltip. Label only — the hint is appended
|
||||
// from the action registry, same as every other tool here.
|
||||
actionId: 'view.toggleHud',
|
||||
icon: <Codicon name="comment-discussion" />,
|
||||
icon: <TitlebarIcon name="comment-discussion" />,
|
||||
id: 'hud',
|
||||
label: t.titlebar.enterHud,
|
||||
onSelect: () => {
|
||||
@@ -198,14 +198,14 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
},
|
||||
{
|
||||
active: hapticsMuted,
|
||||
icon: <Codicon name={hapticsMuted ? 'mute' : 'unmute'} />,
|
||||
icon: <TitlebarIcon name={hapticsMuted ? 'mute' : 'unmute'} />,
|
||||
id: 'haptics',
|
||||
label: hapticsMuted ? t.titlebar.unmuteHaptics : t.titlebar.muteHaptics,
|
||||
onSelect: toggleHaptics
|
||||
},
|
||||
{
|
||||
actionId: 'nav.settings',
|
||||
icon: <Codicon name="settings-gear" />,
|
||||
icon: <TitlebarIcon name="settings-gear" />,
|
||||
id: 'settings',
|
||||
label: t.titlebar.openSettings,
|
||||
onSelect: () => {
|
||||
@@ -230,7 +230,10 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
<>
|
||||
<div
|
||||
aria-label={t.shell.windowControls}
|
||||
className="fixed left-(--titlebar-controls-left) top-(--titlebar-controls-top) z-70 flex translate-y-0.5 flex-row items-center gap-x-1 pointer-events-auto select-none [-webkit-app-region:no-drag]"
|
||||
className={cn(
|
||||
titlebarToolClusterClass,
|
||||
'left-(--titlebar-controls-left) top-(--titlebar-controls-top) translate-y-(--titlebar-controls-y-nudge)'
|
||||
)}
|
||||
>
|
||||
{leftToolbarTools
|
||||
.filter(tool => !tool.hidden)
|
||||
@@ -250,7 +253,10 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
{visiblePaneTools.length > 0 && (
|
||||
<div
|
||||
aria-label={t.shell.paneControls}
|
||||
className="fixed top-[calc(var(--titlebar-controls-top)+var(--right-rail-top-inset,0px))] right-[calc(var(--titlebar-tools-right)+var(--shell-preview-toolbar-gap,0))] z-70 flex flex-row items-center gap-x-1 pointer-events-auto select-none [-webkit-app-region:no-drag]"
|
||||
className={cn(
|
||||
titlebarToolClusterClass,
|
||||
'top-[calc(var(--titlebar-controls-top)+var(--right-rail-top-inset,0px))] right-[calc(var(--titlebar-tools-right)+var(--shell-preview-toolbar-gap,0))]'
|
||||
)}
|
||||
>
|
||||
{visiblePaneTools.map(tool => (
|
||||
<TitlebarToolButton key={tool.id} navigate={navigate} tool={tool} />
|
||||
@@ -260,7 +266,7 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }:
|
||||
|
||||
<div
|
||||
aria-label={t.shell.appControls}
|
||||
className="fixed right-(--titlebar-tools-right) top-(--titlebar-controls-top) z-70 flex flex-row items-center justify-end gap-x-1 pointer-events-auto select-none [-webkit-app-region:no-drag]"
|
||||
className={cn(titlebarToolClusterClass, 'right-(--titlebar-tools-right) top-(--titlebar-controls-top)')}
|
||||
>
|
||||
{visibleSystemTools.map(tool => (
|
||||
<TitlebarToolButton key={tool.id} navigate={navigate} tool={tool} />
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import { titlebarIconSizeCss } from './titlebar'
|
||||
|
||||
/** Titlebar-cluster glyph — 13.9px inline (see titlebarIconSizeCss). */
|
||||
export function TitlebarIcon({
|
||||
className,
|
||||
name,
|
||||
size = titlebarIconSizeCss(),
|
||||
spinning
|
||||
}: {
|
||||
className?: string
|
||||
name: string
|
||||
size?: number | string
|
||||
spinning?: boolean
|
||||
}) {
|
||||
return <Codicon className={cn('leading-none', className)} name={name} size={size} spinning={spinning} />
|
||||
}
|
||||
@@ -2,11 +2,29 @@ import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
TITLEBAR_CONTROL_OFFSET_X,
|
||||
TITLEBAR_CONTROL_SIZE,
|
||||
TITLEBAR_EDGE_INSET,
|
||||
TITLEBAR_FALLBACK_WINDOW_BUTTON_X,
|
||||
titlebarControlsPosition
|
||||
TITLEBAR_ICON_SIZE,
|
||||
TITLEBAR_MAC_TRAFFIC_LIGHTS_Y_NUDGE,
|
||||
titlebarControlsPosition,
|
||||
titlebarControlsYNudge,
|
||||
titlebarIconSizeCss,
|
||||
titlebarToolsWidthCss
|
||||
} from './titlebar'
|
||||
|
||||
describe('titlebar sizing', () => {
|
||||
it('uses 24×24 hit targets and 13.9px glyphs', () => {
|
||||
expect(TITLEBAR_CONTROL_SIZE).toBe(24)
|
||||
expect(TITLEBAR_ICON_SIZE).toBe(13.9)
|
||||
expect(titlebarIconSizeCss()).toBe('13.9px')
|
||||
})
|
||||
|
||||
it('reserves width from abutting hit targets only', () => {
|
||||
expect(titlebarToolsWidthCss(4)).toBe('calc(4 * var(--titlebar-control-size))')
|
||||
})
|
||||
})
|
||||
|
||||
describe('titlebarControlsPosition', () => {
|
||||
it('offsets controls from visible traffic lights', () => {
|
||||
expect(titlebarControlsPosition({ x: 24, y: 10 }).left).toBe(24 + TITLEBAR_CONTROL_OFFSET_X)
|
||||
@@ -24,3 +42,17 @@ describe('titlebarControlsPosition', () => {
|
||||
expect(titlebarControlsPosition(undefined).left).toBe(TITLEBAR_FALLBACK_WINDOW_BUTTON_X + TITLEBAR_CONTROL_OFFSET_X)
|
||||
})
|
||||
})
|
||||
|
||||
describe('titlebarControlsYNudge', () => {
|
||||
it('nudges the left cluster on macOS when traffic lights are visible', () => {
|
||||
expect(titlebarControlsYNudge({ x: 24, y: 10 })).toBe(TITLEBAR_MAC_TRAFFIC_LIGHTS_Y_NUDGE)
|
||||
})
|
||||
|
||||
it('stays flat on Windows/Linux and macOS fullscreen', () => {
|
||||
expect(titlebarControlsYNudge(null)).toBe('0px')
|
||||
})
|
||||
|
||||
it('nudges while macOS window-button position is still unknown', () => {
|
||||
expect(titlebarControlsYNudge(undefined)).toBe(TITLEBAR_MAC_TRAFFIC_LIGHTS_Y_NUDGE)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,22 +2,51 @@ import type { HermesConnection } from '@/global'
|
||||
|
||||
export const TITLEBAR_HEIGHT = 34
|
||||
export const MACOS_TRAFFIC_LIGHTS_HEIGHT = 14
|
||||
export const TITLEBAR_ICON_SIZE = 12
|
||||
/** Titlebar tool hit target (both axes). */
|
||||
export const TITLEBAR_CONTROL_SIZE = 24
|
||||
/** Codicon glyph box in titlebar clusters — optical match to traffic-light row. */
|
||||
export const TITLEBAR_ICON_SIZE = 13.9
|
||||
export const TITLEBAR_ICON_BADGE_SCALE = 0.65
|
||||
export const TITLEBAR_CONTROL_OFFSET_X = 74
|
||||
export const TITLEBAR_CONTROL_HEIGHT = 22
|
||||
export const TITLEBAR_CONTROL_HEIGHT = TITLEBAR_CONTROL_SIZE
|
||||
export const TITLEBAR_CONTROLS_TOP = (TITLEBAR_HEIGHT - TITLEBAR_CONTROL_HEIGHT) / 2
|
||||
|
||||
/** Inline font-size for titlebar Codicons — beats unlayered codicon.css `font: 16px`. */
|
||||
export function titlebarIconSizeCss(scale = 1): string {
|
||||
return `${TITLEBAR_ICON_SIZE * scale}px`
|
||||
}
|
||||
export const TITLEBAR_FALLBACK_WINDOW_BUTTON_X = 24
|
||||
// Edge inset used when no left-side native controls take up that space —
|
||||
// Windows/Linux (native overlay is on the right) and macOS fullscreen
|
||||
// (traffic lights are hidden). Matches the right-cluster's 0.75rem padding.
|
||||
export const TITLEBAR_EDGE_INSET = 14
|
||||
|
||||
// macOS traffic-light row only: nudge the left toolbar cluster down to sit on
|
||||
// the same optical center as the native buttons. null windowButtonPosition means
|
||||
// Windows/Linux (controls on the right) or macOS fullscreen (lights hidden).
|
||||
export const TITLEBAR_MAC_TRAFFIC_LIGHTS_Y_NUDGE = 'calc(var(--spacing) * 0.9)'
|
||||
|
||||
export function titlebarControlsYNudge(
|
||||
windowButtonPosition: HermesConnection['windowButtonPosition'] | undefined
|
||||
): string {
|
||||
return windowButtonPosition !== null ? TITLEBAR_MAC_TRAFFIC_LIGHTS_Y_NUDGE : '0px'
|
||||
}
|
||||
|
||||
// Titlebar palette only. All sizing/radius/cursor/centering come from the
|
||||
// shared <Button size="icon-titlebar"> (used polymorphically via asChild) —
|
||||
// Button is the single source of button styling.
|
||||
export const titlebarButtonClass =
|
||||
'text-muted-foreground/85 hover:bg-(--ui-control-hover-background) hover:text-foreground'
|
||||
|
||||
/** Shared flex shell for left/right/pane titlebar tool rows — no gap; 24px buttons abut. */
|
||||
export const titlebarToolClusterClass =
|
||||
'fixed z-70 flex flex-row items-center pointer-events-auto select-none [-webkit-app-region:no-drag]'
|
||||
|
||||
/** Width reserved for N abutting titlebar tool buttons. */
|
||||
export function titlebarToolsWidthCss(toolCount: number): string {
|
||||
return `calc(${toolCount} * var(--titlebar-control-size))`
|
||||
}
|
||||
|
||||
export const titlebarHeaderBaseClass =
|
||||
'pointer-events-none relative z-3 flex h-(--titlebar-height) w-full min-w-0 shrink-0 items-center justify-start gap-3 overflow-hidden border-b border-(--ui-stroke-tertiary) bg-(--ui-chat-surface-background) px-[max(0.75rem,var(--titlebar-content-inset,0rem))] pr-[calc(var(--titlebar-tools-right,0.75rem)+var(--titlebar-tools-width,0px)+0.75rem)]'
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const buttonVariants = cva(
|
||||
'icon-sm': 'size-8 rounded-[4px]',
|
||||
'icon-lg': 'size-10 rounded-[4px]',
|
||||
'icon-titlebar':
|
||||
'h-(--titlebar-control-height) w-(--titlebar-control-size) rounded-[4px] [&_.codicon]:text-[0.875rem]'
|
||||
'titlebar-icon-button h-(--titlebar-control-height) w-(--titlebar-control-size) rounded-[4px] [&_svg:not([class*="size-"])]:size-(--titlebar-icon-size)'
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
|
||||
@@ -14,7 +14,7 @@ export function Codicon({ className, name, size, spinning, style, ...props }: Co
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className={cn('codicon', `codicon-${name}`, spinning && 'codicon-modifier-spin', className)}
|
||||
style={{ fontSize: size, ...style }}
|
||||
style={{ ...(size != null && size !== '' ? { fontSize: size } : {}), ...style }}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
@import 'tw-shimmer';
|
||||
@import 'katex/dist/katex.min.css';
|
||||
@import '@vscode/codicons/dist/codicon.css';
|
||||
|
||||
/* Titlebar clusters: 24×24 hit targets, 13.9px glyphs. codicon.css is
|
||||
unlayered (`font: 16px/1`); Tailwind utilities in @layer lose that fight. */
|
||||
.titlebar-icon-button .codicon[class*='codicon-'] {
|
||||
font-size: var(--titlebar-icon-size);
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
/* Blanket reduced-motion override: kill ALL CSS animations and transitions
|
||||
@@ -468,8 +475,9 @@
|
||||
|
||||
--sidebar-width: 14.8125rem;
|
||||
--chat-min-width: 28rem;
|
||||
--titlebar-control-size: 1.25rem;
|
||||
--titlebar-control-height: 1.375rem;
|
||||
--titlebar-control-size: 24px;
|
||||
--titlebar-control-height: 24px;
|
||||
--titlebar-icon-size: 13.9px;
|
||||
--sidebar-content-inline-padding: 1rem;
|
||||
|
||||
--sidebar: var(--dt-sidebar-bg);
|
||||
|
||||
Reference in New Issue
Block a user