diff --git a/package.json b/package.json index c572c702b..27e0a43c0 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "dependencies": { "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", + "@floating-ui/dom": "1.7.6", "@linear/sdk": "^82.1.0", "@parcel/watcher": "^2.5.6", "@xterm/addon-serialize": "0.15.0-beta.285", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cccfc9f53..7454c81fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,9 @@ importers: '@electron-toolkit/utils': specifier: ^4.0.0 version: 4.0.0(electron@42.3.3) + '@floating-ui/dom': + specifier: 1.7.6 + version: 1.7.6 '@linear/sdk': specifier: ^82.1.0 version: 82.1.0(graphql@16.13.2) diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 063e42553..2116e35d4 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -3036,51 +3036,20 @@ html.onboarding-tour-start-transition::view-transition-new(root) { margin-top: 4px; } -/* Why: a tour panel must read as elevated above the chrome it is teaching about - without resorting to color. A 1px inner top highlight plus the documented - floating shadow keeps it above the surface in both light and dark modes. */ +/* Why: tours need a distinct item surface without highlighting the underlying target. */ .orca-contextual-tour-panel { - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--foreground) 4%, var(--popover)) 0%, - var(--popover) 12% - ); + --contextual-tour-panel-surface: color-mix(in srgb, var(--foreground) 7%, var(--popover)); + --contextual-tour-panel-border: color-mix(in srgb, var(--foreground) 14%, var(--border)); + background: var(--contextual-tour-panel-surface); + border-color: var(--contextual-tour-panel-border); box-shadow: inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 10%, transparent), 0 10px 24px rgba(0, 0, 0, 0.18); } .dark .orca-contextual-tour-panel { - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--foreground) 4%, var(--popover)) 0%, - var(--popover) 14% - ); - box-shadow: - inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 5%, transparent), - 0 10px 24px rgba(0, 0, 0, 0.18); -} - -/* Why: a tour panel must read as elevated above the chrome it is teaching about - without resorting to color. A 1px inner top highlight plus the documented - floating shadow keeps it above the surface in both light and dark modes. */ -.orca-contextual-tour-panel { - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--foreground) 4%, var(--popover)) 0%, - var(--popover) 12% - ); - box-shadow: - inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 10%, transparent), - 0 10px 24px rgba(0, 0, 0, 0.18); -} - -.dark .orca-contextual-tour-panel { - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--foreground) 4%, var(--popover)) 0%, - var(--popover) 14% - ); + --contextual-tour-panel-surface: color-mix(in srgb, var(--foreground) 10%, var(--popover)); + --contextual-tour-panel-border: color-mix(in srgb, var(--foreground) 16%, var(--border)); box-shadow: inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 5%, transparent), 0 10px 24px rgba(0, 0, 0, 0.18); diff --git a/src/renderer/src/components/contextual-tours/ContextualTourArrow.tsx b/src/renderer/src/components/contextual-tours/ContextualTourArrow.tsx index a0ff41729..349ddb1cd 100644 --- a/src/renderer/src/components/contextual-tours/ContextualTourArrow.tsx +++ b/src/renderer/src/components/contextual-tours/ContextualTourArrow.tsx @@ -1,66 +1,50 @@ -import type { CSSProperties, JSX } from 'react' -import type { ContextualTourPanelPlacement } from './contextual-tour-panel-position' +import type { CSSProperties, JSX, RefObject } from 'react' +import { + CONTEXTUAL_TOUR_ARROW_SIZE, + CONTEXTUAL_TOUR_PANEL_BORDER_WIDTH, + type ContextualTourPanelPlacement +} from './contextual-tour-floating-position' + +const ARROW_WIDTH = CONTEXTUAL_TOUR_ARROW_SIZE.width +const ARROW_HEIGHT = CONTEXTUAL_TOUR_ARROW_SIZE.height + +// Why: CSS rotation pivots on the svg center, so horizontal placements must +// also shift by (width - height) / 2 to keep the rotated arrow flush with the +// panel edge instead of half-swallowed by it. +const PLACEMENT_TRANSFORM = { + top: 'rotate(0deg)', + bottom: 'rotate(180deg)', + left: `translateX(${(ARROW_WIDTH - ARROW_HEIGHT) / 2}px) rotate(-90deg)`, + right: `translateX(${(ARROW_HEIGHT - ARROW_WIDTH) / 2}px) rotate(90deg)` +} satisfies Record export function ContextualTourArrow({ - placement + arrowRef, + placement, + style }: { + arrowRef: RefObject placement: ContextualTourPanelPlacement + style: CSSProperties }): JSX.Element { - // Why: a small triangle pointing at the target makes the panel/target - // relationship readable when the user's eye starts on the panel. - const offsetCss = 'var(--contextual-tour-arrow-offset, 50%)' - const horizontal = placement === 'top' || placement === 'bottom' - const longSide = 12 - const shortSide = 6 - const wrapperStyle: CSSProperties = horizontal - ? { - width: longSide, - height: shortSide, - left: offsetCss, - transform: 'translateX(-50%)', - ...(placement === 'top' ? { top: '100%' } : { bottom: '100%' }) - } - : { - width: shortSide, - height: longSide, - top: offsetCss, - transform: 'translateY(-50%)', - ...(placement === 'left' ? { left: '100%' } : { right: '100%' }) - } - const path = - placement === 'top' - ? 'M0 0 L6 6 L12 0' - : placement === 'bottom' - ? 'M0 6 L6 0 L12 6' - : placement === 'left' - ? 'M0 0 L6 6 L0 12' - : 'M6 0 L0 6 L6 12' - const maskPath = - placement === 'top' - ? 'M0 0 L12 0' - : placement === 'bottom' - ? 'M0 6 L12 6' - : placement === 'left' - ? 'M0 0 L0 12' - : 'M6 0 L6 12' return ( - + ) } diff --git a/src/renderer/src/components/contextual-tours/ContextualTourControl.tsx b/src/renderer/src/components/contextual-tours/ContextualTourControl.tsx index d85d2c72e..7898b5795 100644 --- a/src/renderer/src/components/contextual-tours/ContextualTourControl.tsx +++ b/src/renderer/src/components/contextual-tours/ContextualTourControl.tsx @@ -42,12 +42,6 @@ function AutoRenameBranchFromWorkControl(): JSX.Element { 'Auto-name from first message' )} -
- {translate( - 'auto.components.contextual.tours.ContextualTourControl.02e8373219', - 'Auto-generates a new name when you leave this text box empty.' - )} -