fix: use platform shortcut labels in feature wall (#3811)

This commit is contained in:
Jinwoo Hong 2026-05-30 14:13:38 -04:00 committed by GitHub
parent 17d8d079c9
commit 01a214cc59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 72 additions and 8 deletions

View File

@ -2,6 +2,7 @@
import { useEffect, useRef, useState, type JSX, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
import { ClaudeIcon } from '@/components/status-bar/icons'
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
import { FeatureWallClickRing } from './FeatureWallClickRing'
// Why: this animation tells the full Orca story end-to-end — the user opens a
@ -152,6 +153,7 @@ const TERM_ENTRIES: readonly { entry: TermEntry; minPhase: Phase }[] = [
export function BrowserAnimatedVisual(props: { reducedMotion: boolean }): JSX.Element {
const { reducedMotion } = props
const newBrowserShortcutLabel = useShortcutLabel('tab.newBrowser')
const [phase, setPhase] = useState<Phase>('idle')
const [typedChars, setTypedChars] = useState(0)
@ -524,7 +526,9 @@ export function BrowserAnimatedVisual(props: { reducedMotion: boolean }): JSX.El
<GlobeGlyph />
</span>
<span className="text-[11.5px] text-foreground">New Browser Tab</span>
<span className="font-mono text-[10.5px] text-muted-foreground">B</span>
<span className="font-mono text-[10.5px] text-muted-foreground">
{newBrowserShortcutLabel}
</span>
</div>
<DropdownSkeletonRow widthPct={52} />
</div>

View File

@ -2,6 +2,7 @@
import { useEffect, useRef } from 'react'
import type { JSX, ReactNode } from 'react'
import { cn } from '@/lib/utils'
import { getShortcutPlatform } from '@/hooks/useShortcutLabel'
// Why: the visual leans on direct DOM mutation (typing into a node, swapping
// classes, anchoring a floating menu by measured rect) so the loop reads
@ -273,6 +274,9 @@ function SlashRow(props: {
export function EditorAnimatedVisual(props: { reducedMotion: boolean }): JSX.Element {
const { reducedMotion } = props
const editorShortcutPrefix = getShortcutPlatform() === 'darwin' ? '⌘' : 'Ctrl+'
const boldShortcutLabel = `${editorShortcutPrefix}B`
const italicShortcutLabel = `${editorShortcutPrefix}I`
const docRef = useRef<HTMLDivElement | null>(null)
const activeLineRef = useRef<HTMLDivElement | null>(null)
@ -714,8 +718,8 @@ export function EditorAnimatedVisual(props: { reducedMotion: boolean }): JSX.Ele
shape. */}
<div className="border-t border-border bg-card px-3 py-2 text-[11px] text-muted-foreground">
Type <kbd className={KBD_CLASS_DOC}>/</kbd> for blocks ·{' '}
<kbd className={KBD_CLASS_DOC}>B</kbd> bold · <kbd className={KBD_CLASS_DOC}>I</kbd>{' '}
italic
<kbd className={KBD_CLASS_DOC}>{boldShortcutLabel}</kbd> bold ·{' '}
<kbd className={KBD_CLASS_DOC}>{italicShortcutLabel}</kbd> italic
</div>
{/* Why: the imperative loop adds .slash-active and toggles

View File

@ -1,6 +1,7 @@
import { useEffect, useRef } from 'react'
import type { ComponentType, JSX, ReactNode } from 'react'
import { Files, GitBranch, ListChecks, MessageSquare, Search } from 'lucide-react'
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
import { ReviewPRViewVisualStyles } from './review-animated-visual-pr-view-styles'
import { CheckTinyIcon, ChevDownIcon, CursorIcon } from './review-animated-visual-shared'
@ -18,6 +19,10 @@ const SIDEBAR_TABS: readonly {
]
function SidebarTabs(props: { active: SidebarTabId; interactiveChecks?: boolean }): JSX.Element {
const checksShortcutLabel = useShortcutLabel('sidebar.checks.toggle')
const checksTooltip =
checksShortcutLabel === 'Unassigned' ? 'Checks' : `Checks (${checksShortcutLabel})`
return (
<div className="ravpr-tabs">
{SIDEBAR_TABS.map((tab) => {
@ -38,7 +43,7 @@ function SidebarTabs(props: { active: SidebarTabId; interactiveChecks?: boolean
})}
{props.interactiveChecks ? (
<span className="ravpr-tooltip" data-checks-tooltip>
Checks (K)
{checksTooltip}
</span>
) : null}
</div>

View File

@ -2,6 +2,7 @@
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import type { JSX } from 'react'
import { cn } from '@/lib/utils'
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
import { FeatureWallClickRing } from './FeatureWallClickRing'
// Why: the right-click menu needs the same icons as the real Orca menu so the
@ -126,6 +127,8 @@ const RESPONSE_WIDTHS = [72, 88, 64, 78] as const
export function WorkbenchAnimatedVisual(props: { reducedMotion: boolean }): JSX.Element {
const { reducedMotion } = props
const splitRightShortcutLabel = useShortcutLabel('terminal.splitRight')
const splitDownShortcutLabel = useShortcutLabel('terminal.splitDown')
const panelRef = useRef<HTMLDivElement | null>(null)
const leftPaneRef = useRef<HTMLDivElement | null>(null)
const splitRowRef = useRef<HTMLDivElement | null>(null)
@ -392,6 +395,8 @@ export function WorkbenchAnimatedVisual(props: { reducedMotion: boolean }): JSX.
shown={menuShown}
splitRowActive={splitRowActive}
splitRowRef={splitRowRef}
splitRightShortcutLabel={splitRightShortcutLabel}
splitDownShortcutLabel={splitDownShortcutLabel}
/>
</div>
@ -435,8 +440,8 @@ export function WorkbenchAnimatedVisual(props: { reducedMotion: boolean }): JSX.
{/* Standalone keyboard hint stays inside the visual so the tour copy can
remain a single subheader line. */}
<div className="border-t border-border bg-card px-3 py-2 text-[11px] text-muted-foreground">
Same pane: <kbd className={KBD_CLASS}>D</kbd> splits right ·{' '}
<kbd className={KBD_CLASS}>D</kbd> splits down
Same pane: <kbd className={KBD_CLASS}>{splitRightShortcutLabel}</kbd> splits right ·{' '}
<kbd className={KBD_CLASS}>{splitDownShortcutLabel}</kbd> splits down
</div>
</div>
)
@ -494,6 +499,8 @@ function ContextMenu(props: {
shown: boolean
splitRowActive: boolean
splitRowRef: React.RefObject<HTMLDivElement | null>
splitRightShortcutLabel: string
splitDownShortcutLabel: string
}): JSX.Element {
return (
<div
@ -519,14 +526,18 @@ function ContextMenu(props: {
<SplitRightIcon />
</span>
<span className="whitespace-nowrap leading-none">Split Terminal Right</span>
<span className="font-mono text-[11px] text-muted-foreground">D</span>
<span className="font-mono text-[11px] text-muted-foreground">
{props.splitRightShortcutLabel}
</span>
</div>
<div className="grid h-[22px] grid-cols-[18px_1fr_auto] items-center gap-2 rounded-[5px] px-1.5 py-1 pl-1.5">
<span className="inline-flex items-center justify-center text-muted-foreground">
<SplitDownIcon />
</span>
<span className="whitespace-nowrap leading-none">Split Terminal Down</span>
<span className="font-mono text-[11px] text-muted-foreground">D</span>
<span className="font-mono text-[11px] text-muted-foreground">
{props.splitDownShortcutLabel}
</span>
</div>
<CtxSeparator />
<CtxSkeleton width={64} />

View File

@ -0,0 +1,40 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { afterEach, describe, expect, it } from 'vitest'
import { BrowserAnimatedVisual } from './BrowserAnimatedVisual'
import { EditorAnimatedVisual } from './EditorAnimatedVisual'
import { ReviewPRViewAnimatedVisual } from './ReviewPRViewAnimatedVisual'
import { WorkbenchAnimatedVisual } from './WorkbenchAnimatedVisual'
const originalUserAgent = navigator.userAgent
function setUserAgent(userAgent: string): void {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: userAgent
})
}
describe('feature wall shortcut labels', () => {
afterEach(() => {
setUserAgent(originalUserAgent)
})
it('renders Windows shortcut copy in workbench and browser visuals', () => {
setUserAgent('Windows NT 10.0')
const html = [
renderToStaticMarkup(<BrowserAnimatedVisual reducedMotion />),
renderToStaticMarkup(<WorkbenchAnimatedVisual reducedMotion />),
renderToStaticMarkup(<EditorAnimatedVisual reducedMotion />),
renderToStaticMarkup(<ReviewPRViewAnimatedVisual reducedMotion />)
].join('\n')
expect(html).toContain('Ctrl+Shift+B')
expect(html).toContain('Ctrl+Shift+D')
expect(html).toContain('Alt+Shift+D')
expect(html).toContain('Ctrl+B')
expect(html).toContain('Ctrl+I')
expect(html).toContain('Checks')
expect(html).not.toContain('⌘')
})
})