feat(desktop): Cron, Profiles, usage analytics, and titlebar fixes
- Add Cron and Profiles sidebar routes with full CRUD-style flows and API wiring. - Extend Command Center with auxiliary task overrides and a Usage panel (7d/30d/90d). - Fix titlebar geometry for WSL/Windows (native overlay width, tool spacing). - Remove stray merge conflict markers from pyproject.toml optional deps. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -60,6 +60,10 @@ export function AppShell({
|
||||
const viewportFullscreen = useSyncExternalStore(subscribeWindowSize, viewportIsFullscreen, () => false)
|
||||
const isFullscreen = Boolean(connection?.isFullscreen) || viewportFullscreen
|
||||
const titlebarControls = titlebarControlsPosition(connection?.windowButtonPosition, isFullscreen)
|
||||
// Width Windows/Linux reserve for the OS-painted min/max/close overlay (zero
|
||||
// on macOS, where window controls sit on the left and are reported via
|
||||
// windowButtonPosition instead). The right tool cluster has to clear them.
|
||||
const nativeOverlayWidth = connection?.nativeOverlayWidth ?? 0
|
||||
|
||||
const titlebarContentInset = sidebarOpen
|
||||
? 0
|
||||
@@ -68,9 +72,15 @@ export function AppShell({
|
||||
// The static system cluster (file-browser, haptics, settings) is hardcoded
|
||||
// in TitlebarControls. Pane-supplied tools (preview's group) render in a
|
||||
// separate cluster anchored further left.
|
||||
//
|
||||
// Width math has to include the `gap-x-1` (0.25rem) between buttons:
|
||||
// N buttons + (N - 1) inner gaps, plus one extra 0.25rem of breathing room
|
||||
// between the pane-tool cluster and the system cluster so they don't sit
|
||||
// flush against each other. Modeled as N gaps (N - 1 inner + 1 trailing)
|
||||
// to keep the formula generic for any pane-tool count.
|
||||
const SYSTEM_TOOL_COUNT = 3
|
||||
const paneToolCount = titlebarTools?.filter(tool => !tool.hidden).length ?? 0
|
||||
const systemToolsWidth = `calc(${SYSTEM_TOOL_COUNT} * var(--titlebar-control-size))`
|
||||
const systemToolsWidth = `calc(${SYSTEM_TOOL_COUNT} * (var(--titlebar-control-size) + 0.25rem))`
|
||||
|
||||
const fileBrowserWidth =
|
||||
fileBrowserWidthOverride !== undefined ? `${fileBrowserWidthOverride}px` : FILE_BROWSER_DEFAULT_WIDTH
|
||||
@@ -83,12 +93,13 @@ export function AppShell({
|
||||
const previewToolbarGap = fileBrowserOpen ? fileBrowserWidth : systemToolsWidth
|
||||
|
||||
// Used by the drag region to know where the rightmost interactive element
|
||||
// ends. When pane tools are present, that's `gap + paneCount * controlSize`
|
||||
// (the leftmost button is at `tools-right + gap + paneCount * size`).
|
||||
// Otherwise the static cluster's footprint is enough.
|
||||
// ends. When pane tools are present, that's `gap + paneCount * controlSize
|
||||
// + paneCount * 0.25rem` (the leftmost button is at `tools-right + gap +
|
||||
// paneCount * (size + gap-x-1)`). Otherwise the static cluster's footprint
|
||||
// is enough.
|
||||
const titlebarToolsWidth =
|
||||
paneToolCount > 0
|
||||
? `calc(${previewToolbarGap} + ${paneToolCount} * var(--titlebar-control-size))`
|
||||
? `calc(${previewToolbarGap} + ${paneToolCount} * (var(--titlebar-control-size) + 0.25rem))`
|
||||
: systemToolsWidth
|
||||
|
||||
return (
|
||||
@@ -105,7 +116,7 @@ export function AppShell({
|
||||
'--titlebar-content-inset': `${titlebarContentInset}px`,
|
||||
'--titlebar-controls-left': `${titlebarControls.left}px`,
|
||||
'--titlebar-controls-top': `${titlebarControls.top}px`,
|
||||
'--titlebar-tools-right': '0.75rem',
|
||||
'--titlebar-tools-right': `calc(${nativeOverlayWidth}px + 0.75rem)`,
|
||||
'--titlebar-tools-width': titlebarToolsWidth,
|
||||
// Anchor for the pane-tool cluster's right edge in TitlebarControls.
|
||||
// Sourced from the layout store rather than the PaneShell-emitted
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { TITLEBAR_CONTROL_OFFSET_X, titlebarControlsPosition } from './titlebar'
|
||||
import { TITLEBAR_CONTROL_OFFSET_X, TITLEBAR_EDGE_INSET, titlebarControlsPosition } from './titlebar'
|
||||
|
||||
describe('titlebarControlsPosition', () => {
|
||||
it('offsets controls from visible traffic lights', () => {
|
||||
@@ -8,10 +8,11 @@ describe('titlebarControlsPosition', () => {
|
||||
})
|
||||
|
||||
it('pins to the edge when macOS fullscreen hides traffic lights', () => {
|
||||
expect(titlebarControlsPosition({ x: 24, y: 10 }, true).left).toBe(14)
|
||||
expect(titlebarControlsPosition({ x: 24, y: 10 }, true).left).toBe(TITLEBAR_EDGE_INSET)
|
||||
})
|
||||
|
||||
it('falls back to the default offset when traffic-light coords are unavailable', () => {
|
||||
expect(titlebarControlsPosition(undefined, true).left).toBe(24 + TITLEBAR_CONTROL_OFFSET_X)
|
||||
it('pins to the edge on Windows/Linux where native controls render on the right', () => {
|
||||
expect(titlebarControlsPosition(null).left).toBe(TITLEBAR_EDGE_INSET)
|
||||
expect(titlebarControlsPosition(undefined).left).toBe(TITLEBAR_EDGE_INSET)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,11 +6,10 @@ export const TITLEBAR_ICON_SIZE = 12
|
||||
export const TITLEBAR_CONTROL_OFFSET_X = 60
|
||||
export const TITLEBAR_CONTROL_HEIGHT = 22
|
||||
export const TITLEBAR_CONTROLS_TOP = (TITLEBAR_HEIGHT - TITLEBAR_CONTROL_HEIGHT) / 2
|
||||
|
||||
const WINDOW_BUTTON_FALLBACK = {
|
||||
x: 24,
|
||||
y: TITLEBAR_HEIGHT / 2 - MACOS_TRAFFIC_LIGHTS_HEIGHT / 2
|
||||
}
|
||||
// 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
|
||||
|
||||
export const titlebarButtonClass =
|
||||
'h-[var(--titlebar-control-height)] w-[var(--titlebar-control-size)] rounded-md text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||
@@ -27,10 +26,13 @@ export function titlebarControlsPosition(
|
||||
) {
|
||||
const top = Math.max(0, TITLEBAR_CONTROLS_TOP)
|
||||
|
||||
// macOS hides traffic lights in fullscreen — pin to the edge instead of reserving their slot.
|
||||
if (windowButtonPosition && isFullscreen) {
|
||||
return { left: 14, top }
|
||||
// No left-side native controls to dodge:
|
||||
// - Windows/Linux: native min/max/close render on the right via titleBarOverlay.
|
||||
// - macOS fullscreen: traffic lights are hidden.
|
||||
// In both cases, pin the cluster to the edge with a small inset.
|
||||
if (!windowButtonPosition || isFullscreen) {
|
||||
return { left: TITLEBAR_EDGE_INSET, top }
|
||||
}
|
||||
|
||||
return { left: (windowButtonPosition ?? WINDOW_BUTTON_FALLBACK).x + TITLEBAR_CONTROL_OFFSET_X, top }
|
||||
return { left: windowButtonPosition.x + TITLEBAR_CONTROL_OFFSET_X, top }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user