mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
UX (#5143)
This commit is contained in:
@@ -273,6 +273,17 @@ describe('SidebarNav', () => {
|
||||
expect(mocks.updateSettings).toHaveBeenCalledWith({ showMobileButton: false })
|
||||
})
|
||||
|
||||
it('hides task source shortcuts until the Tasks row is hovered or focused', async () => {
|
||||
const container = await renderSidebarNav()
|
||||
|
||||
const tasksButton = getButtonByText(container, 'Tasks')
|
||||
const shortcuts = tasksButton.querySelector('[aria-label="Open GitHub tasks"]')?.parentElement
|
||||
|
||||
expect(shortcuts?.className).toContain('hidden')
|
||||
expect(shortcuts?.className).toContain('group-hover:flex')
|
||||
expect(shortcuts?.className).toContain('group-focus-within:flex')
|
||||
})
|
||||
|
||||
it('hides available Tasks from its sidebar context menu', async () => {
|
||||
const container = await renderSidebarNav()
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ function HideSidebarMenu({ onHide }: { onHide: () => void }): React.JSX.Element
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem onSelect={onHide}>
|
||||
<EyeOff className="size-3.5" />
|
||||
{translate("auto.components.sidebar.SidebarNav.d599269755", "Hide from sidebar")}</ContextMenuItem>
|
||||
{translate('auto.components.sidebar.SidebarNav.d599269755', 'Hide from sidebar')}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
)
|
||||
}
|
||||
@@ -237,7 +238,7 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
aria-current={tasksActive ? 'page' : undefined}
|
||||
data-contextual-tour-target="sidebar-tasks"
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] font-medium tracking-tight transition-colors',
|
||||
'group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] font-medium tracking-tight transition-colors',
|
||||
tasksActive
|
||||
? 'bg-worktree-sidebar-accent text-worktree-sidebar-accent-foreground'
|
||||
: 'text-worktree-sidebar-foreground/60 hover:bg-worktree-sidebar-foreground/8',
|
||||
@@ -251,12 +252,17 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
)}
|
||||
strokeWidth={tasksActive ? 2.25 : 1.75}
|
||||
/>
|
||||
<span className="flex-1">{translate("auto.components.sidebar.SidebarNav.fee535205b", "Tasks")}</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="flex-1">
|
||||
{translate('auto.components.sidebar.SidebarNav.fee535205b', 'Tasks')}
|
||||
</span>
|
||||
<span className="hidden items-center gap-1 group-hover:flex group-focus-within:flex">
|
||||
{visibleTaskProviders.includes('github') ? (
|
||||
<TaskProviderShortcut
|
||||
canBrowseTasks={canBrowseTasks}
|
||||
label={translate("auto.components.sidebar.SidebarNav.0ccba862b8", "Open GitHub tasks")}
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarNav.0ccba862b8',
|
||||
'Open GitHub tasks'
|
||||
)}
|
||||
onOpen={() => {
|
||||
openTaskPage({ taskSource: 'github' })
|
||||
}}
|
||||
@@ -267,7 +273,10 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
{visibleTaskProviders.includes('gitlab') ? (
|
||||
<TaskProviderShortcut
|
||||
canBrowseTasks={canBrowseTasks}
|
||||
label={translate("auto.components.sidebar.SidebarNav.196c1b5362", "Open GitLab tasks")}
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarNav.196c1b5362',
|
||||
'Open GitLab tasks'
|
||||
)}
|
||||
onOpen={() => {
|
||||
openTaskPage({ taskSource: 'gitlab' })
|
||||
}}
|
||||
@@ -278,7 +287,10 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
{visibleTaskProviders.includes('linear') ? (
|
||||
<TaskProviderShortcut
|
||||
canBrowseTasks={canBrowseTasks}
|
||||
label={translate("auto.components.sidebar.SidebarNav.c39ab10000", "Open Linear tasks")}
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarNav.c39ab10000',
|
||||
'Open Linear tasks'
|
||||
)}
|
||||
onOpen={() => {
|
||||
openTaskPage({ taskSource: 'linear' })
|
||||
}}
|
||||
@@ -289,7 +301,10 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
{visibleTaskProviders.includes('jira') ? (
|
||||
<TaskProviderShortcut
|
||||
canBrowseTasks={canBrowseTasks}
|
||||
label={translate("auto.components.sidebar.SidebarNav.e7ad3c540d", "Open Jira tasks")}
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarNav.e7ad3c540d',
|
||||
'Open Jira tasks'
|
||||
)}
|
||||
onOpen={() => {
|
||||
openTaskPage({ taskSource: 'jira' })
|
||||
}}
|
||||
@@ -324,7 +339,9 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
)}
|
||||
strokeWidth={automationsActive ? 2.25 : 1.75}
|
||||
/>
|
||||
<span className="flex-1">{translate("auto.components.sidebar.SidebarNav.f323383e9a", "Automations")}</span>
|
||||
<span className="flex-1">
|
||||
{translate('auto.components.sidebar.SidebarNav.f323383e9a', 'Automations')}
|
||||
</span>
|
||||
</button>
|
||||
</ContextMenuTrigger>
|
||||
<HideSidebarMenu onHide={hideAutomationsButton} />
|
||||
@@ -349,7 +366,9 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
)}
|
||||
strokeWidth={activityActive ? 2.25 : 1.75}
|
||||
/>
|
||||
<span className="flex-1">{translate("auto.components.sidebar.SidebarNav.9c95e1ce91", "Agents")}</span>
|
||||
<span className="flex-1">
|
||||
{translate('auto.components.sidebar.SidebarNav.9c95e1ce91', 'Agents')}
|
||||
</span>
|
||||
{activityUnreadCount > 0 ? (
|
||||
<span className="rounded-full bg-primary px-1.5 py-px text-[10px] font-semibold text-primary-foreground">
|
||||
{activityUnreadCount}
|
||||
@@ -381,10 +400,13 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
)}
|
||||
strokeWidth={mobileActive ? 2.25 : 1.75}
|
||||
/>
|
||||
<span className="flex-1">{translate("auto.components.sidebar.SidebarNav.1b5c41caee", "Orca Mobile")}</span>
|
||||
<span className="flex-1">
|
||||
{translate('auto.components.sidebar.SidebarNav.1b5c41caee', 'Orca Mobile')}
|
||||
</span>
|
||||
{mobileOnboardingBadge.visible ? (
|
||||
<span className="rounded-full bg-primary px-1.5 py-px text-[10px] font-semibold text-primary-foreground">
|
||||
{translate("auto.components.sidebar.SidebarNav.c86d83b5c3", "New")}</span>
|
||||
{translate('auto.components.sidebar.SidebarNav.c86d83b5c3', 'New')}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
</ContextMenuTrigger>
|
||||
@@ -394,14 +416,19 @@ const SidebarNav = React.memo(function SidebarNav() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openModal('worktree-palette')}
|
||||
aria-label={translate("auto.components.sidebar.SidebarNav.0c3395fd32", "Search worktrees and browser tabs")}
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.SidebarNav.0c3395fd32',
|
||||
'Search worktrees and browser tabs'
|
||||
)}
|
||||
className="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] font-medium tracking-tight text-worktree-sidebar-foreground/60 transition-colors hover:bg-worktree-sidebar-foreground/8"
|
||||
>
|
||||
<Search
|
||||
className="size-4 shrink-0 text-worktree-sidebar-foreground/30"
|
||||
strokeWidth={1.75}
|
||||
/>
|
||||
<span className="flex-1">{translate("auto.components.sidebar.SidebarNav.80611a8b10", "Search")}</span>
|
||||
<span className="flex-1">
|
||||
{translate('auto.components.sidebar.SidebarNav.80611a8b10', 'Search')}
|
||||
</span>
|
||||
<kbd className="hidden rounded border border-border/60 bg-background/40 px-1.5 py-px font-mono text-[10px] font-medium text-muted-foreground group-hover:inline-flex items-center">
|
||||
{worktreePaletteShortcut}
|
||||
</kbd>
|
||||
|
||||
@@ -4,12 +4,19 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { SidebarSettingsHelpMenu } from './SidebarSettingsHelpMenu'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
openModal: vi.fn(),
|
||||
openSettingsPage: vi.fn(),
|
||||
openSettingsTarget: vi.fn(),
|
||||
appRestart: vi.fn(),
|
||||
updaterCheck: vi.fn(),
|
||||
shellOpenUrl: vi.fn(),
|
||||
useShortcutLabel: vi.fn()
|
||||
useShortcutLabel: vi.fn(),
|
||||
setupProgress: {
|
||||
ready: true,
|
||||
coreDoneCount: 2,
|
||||
coreTotal: 5,
|
||||
stepDone: {}
|
||||
}
|
||||
}))
|
||||
|
||||
let updateStatus = { state: 'idle' } as const
|
||||
@@ -17,6 +24,7 @@ let updateStatus = { state: 'idle' } as const
|
||||
vi.mock('@/store', () => ({
|
||||
useAppStore: (selector: (state: unknown) => unknown) =>
|
||||
selector({
|
||||
openModal: mocks.openModal,
|
||||
openSettingsPage: mocks.openSettingsPage,
|
||||
openSettingsTarget: mocks.openSettingsTarget,
|
||||
updateStatus
|
||||
@@ -31,6 +39,18 @@ vi.mock('@/hooks/useMountedRef', () => ({
|
||||
useMountedRef: () => ({ current: true })
|
||||
}))
|
||||
|
||||
vi.mock('../onboarding/show-onboarding-event', () => ({
|
||||
showOnboardingFromRenderer: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('../setup-guide/use-setup-guide-progress', () => ({
|
||||
useSetupGuideProgress: () => mocks.setupProgress
|
||||
}))
|
||||
|
||||
vi.mock('../setup-guide/SetupGuideProgressRing', () => ({
|
||||
SetupGuideProgressRing: () => <span data-testid="setup-guide-progress-ring" />
|
||||
}))
|
||||
|
||||
vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
DropdownMenu: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
DropdownMenuContent: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
@@ -79,10 +99,15 @@ describe('SidebarSettingsHelpMenu', () => {
|
||||
vi.clearAllMocks()
|
||||
mocks.useShortcutLabel.mockReturnValue('⌘,')
|
||||
updateStatus = { state: 'idle' }
|
||||
mocks.setupProgress = {
|
||||
ready: true,
|
||||
coreDoneCount: 2,
|
||||
coreTotal: 5,
|
||||
stepDone: {}
|
||||
}
|
||||
})
|
||||
|
||||
it('renders the help button with correct aria-label', () => {
|
||||
updateStatus = { state: 'idle' }
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('Help')
|
||||
})
|
||||
@@ -102,6 +127,28 @@ describe('SidebarSettingsHelpMenu', () => {
|
||||
expect(html).toContain('Keyboard Shortcuts')
|
||||
})
|
||||
|
||||
it('renders Milestones with progress when setup is incomplete', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('Milestones')
|
||||
expect(html).toContain('data-testid="setup-guide-progress-ring"')
|
||||
})
|
||||
|
||||
it('hides Milestones when setup is complete', () => {
|
||||
mocks.setupProgress = {
|
||||
ready: true,
|
||||
coreDoneCount: 5,
|
||||
coreTotal: 5,
|
||||
stepDone: {}
|
||||
}
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).not.toContain('Milestones')
|
||||
})
|
||||
|
||||
it('hides the Onboarding admin entry by default', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).not.toContain('Onboarding')
|
||||
})
|
||||
|
||||
it('renders Docs link', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('Docs')
|
||||
@@ -122,6 +169,11 @@ describe('SidebarSettingsHelpMenu', () => {
|
||||
expect(html).toContain('Discord')
|
||||
})
|
||||
|
||||
it('renders X link', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('>X<')
|
||||
})
|
||||
|
||||
it('renders Check for Updates menu item', () => {
|
||||
const html = renderToStaticMarkup(<SidebarSettingsHelpMenu />)
|
||||
expect(html).toContain('Check for Updates')
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
BookOpen,
|
||||
CircleHelp,
|
||||
ExternalLink,
|
||||
Github,
|
||||
Keyboard,
|
||||
Loader2,
|
||||
MessageSquareText,
|
||||
RefreshCw,
|
||||
RotateCw,
|
||||
School,
|
||||
ScrollText,
|
||||
Settings
|
||||
} from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import logo from '../../../../../resources/logo.svg'
|
||||
import { useAppStore } from '@/store'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
@@ -21,6 +27,9 @@ import {
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
|
||||
import { showOnboardingFromRenderer } from '../onboarding/show-onboarding-event'
|
||||
import { SetupGuideProgressRing } from '../setup-guide/SetupGuideProgressRing'
|
||||
import { useSetupGuideProgress } from '../setup-guide/use-setup-guide-progress'
|
||||
import { SidebarFeedbackDialog } from './SidebarFeedbackDialog'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
@@ -28,6 +37,7 @@ const DOCS_URL = 'https://www.onorca.dev/docs'
|
||||
const CHANGELOG_URL = 'https://onorca.dev/changelog'
|
||||
const GITHUB_URL = 'https://github.com/stablyai/orca'
|
||||
const DISCORD_URL = 'https://discord.gg/fzjDKHxv8Q'
|
||||
const X_URL = 'https://x.com/orca_build'
|
||||
|
||||
function openExternalUrl(url: string): void {
|
||||
void window.api.shell.openUrl(url)
|
||||
@@ -41,9 +51,26 @@ function DiscordIcon(): React.JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
function ExternalMenuItem({ label, url }: { label: string; url: string }): React.JSX.Element {
|
||||
function XIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" className="size-3.5 fill-current">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ExternalMenuItem({
|
||||
label,
|
||||
url,
|
||||
icon
|
||||
}: {
|
||||
label: string
|
||||
url: string
|
||||
icon: React.ReactNode
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(url)}>
|
||||
{icon}
|
||||
{label}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
@@ -51,17 +78,23 @@ function ExternalMenuItem({ label, url }: { label: string; url: string }): React
|
||||
}
|
||||
|
||||
export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
const openModal = useAppStore((s) => s.openModal)
|
||||
const openSettingsPage = useAppStore((s) => s.openSettingsPage)
|
||||
const openSettingsTarget = useAppStore((s) => s.openSettingsTarget)
|
||||
const updateStatus = useAppStore((s) => s.updateStatus)
|
||||
const setupProgress = useSetupGuideProgress(true, false, false)
|
||||
|
||||
const settingsShortcut = useShortcutLabel('app.settings')
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const [feedbackOpen, setFeedbackOpen] = useState(false)
|
||||
const [showAdminOptions, setShowAdminOptions] = useState(false)
|
||||
const [isRestartingOrca, setIsRestartingOrca] = useState(false)
|
||||
const lastShowOnboardingAtRef = React.useRef(0)
|
||||
const mountedRef = useMountedRef()
|
||||
|
||||
const showMilestones =
|
||||
setupProgress.ready && setupProgress.coreDoneCount < setupProgress.coreTotal
|
||||
|
||||
const handleMenuOpenChange = (open: boolean): void => {
|
||||
setMenuOpen(open)
|
||||
if (!open) {
|
||||
@@ -70,9 +103,20 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
}
|
||||
|
||||
const revealAdminOptions = (altKey: boolean): void => {
|
||||
// Why: onboarding replay and restart stay off the default Help menu; holding
|
||||
// Option/Alt before opening is an intentional power-user affordance.
|
||||
setShowAdminOptions(altKey)
|
||||
}
|
||||
|
||||
const handleShowOnboarding = (): void => {
|
||||
const now = Date.now()
|
||||
if (now - lastShowOnboardingAtRef.current < 500) {
|
||||
return
|
||||
}
|
||||
lastShowOnboardingAtRef.current = now
|
||||
void showOnboardingFromRenderer()
|
||||
}
|
||||
|
||||
const handleRestartOrca = (): void => {
|
||||
if (isRestartingOrca) {
|
||||
return
|
||||
@@ -107,6 +151,10 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
void window.api.updater.check({ includePrerelease: shiftKey })
|
||||
}
|
||||
|
||||
const openMilestones = (): void => {
|
||||
openModal('setup-guide', { telemetrySource: 'help_menu' })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
||||
@@ -133,12 +181,20 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.2991a0106c', 'Help')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent side="top" align="start" sideOffset={8} className="w-48">
|
||||
<DropdownMenuContent side="top" align="start" sideOffset={8} className="w-52">
|
||||
<DropdownMenuItem onSelect={openSettingsPage}>
|
||||
<Settings className="size-3.5" />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.a428c25998', 'Settings')}
|
||||
<span className="ml-auto text-xs tracking-wide opacity-60">{settingsShortcut}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={openShortcutsSettings}>
|
||||
<Keyboard className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.e565171a7c',
|
||||
'Keyboard Shortcuts'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={() => setFeedbackOpen(true)}>
|
||||
<MessageSquareText className="size-3.5" />
|
||||
{translate(
|
||||
@@ -146,17 +202,43 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
'Send Feedback'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={openShortcutsSettings}>
|
||||
<ExternalLink className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.e565171a7c',
|
||||
'Keyboard Shortcuts'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
{showMilestones ? (
|
||||
<DropdownMenuItem onSelect={openMilestones}>
|
||||
<img
|
||||
src={logo}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="size-3.5 object-contain invert opacity-55 dark:invert-0"
|
||||
/>
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.f8a2c91d4e',
|
||||
'Milestones'
|
||||
)}
|
||||
<SetupGuideProgressRing
|
||||
done={setupProgress.coreDoneCount}
|
||||
total={setupProgress.coreTotal}
|
||||
sizeClassName="size-4"
|
||||
className="ml-auto"
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
{showAdminOptions ? (
|
||||
<DropdownMenuItem
|
||||
className="whitespace-nowrap"
|
||||
onClick={handleShowOnboarding}
|
||||
onSelect={handleShowOnboarding}
|
||||
>
|
||||
<School className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.b7e4d2a19c',
|
||||
'Onboarding'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<ExternalMenuItem
|
||||
label={translate('auto.components.sidebar.SidebarSettingsHelpMenu.cdc87f897e', 'Docs')}
|
||||
url={DOCS_URL}
|
||||
icon={<BookOpen className="size-3.5" />}
|
||||
/>
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
@@ -164,19 +246,27 @@ export function SidebarSettingsHelpMenu(): React.JSX.Element {
|
||||
'Changelog'
|
||||
)}
|
||||
url={CHANGELOG_URL}
|
||||
icon={<ScrollText className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<ExternalMenuItem
|
||||
label={translate(
|
||||
'auto.components.sidebar.SidebarSettingsHelpMenu.5687ab246a',
|
||||
'GitHub'
|
||||
)}
|
||||
url={GITHUB_URL}
|
||||
icon={<Github className="size-3.5" />}
|
||||
/>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(DISCORD_URL)}>
|
||||
<DiscordIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.eb9884e55b', 'Discord')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => openExternalUrl(X_URL)}>
|
||||
<XIcon />
|
||||
{translate('auto.components.sidebar.SidebarSettingsHelpMenu.c4f8e1b72a', 'X')}
|
||||
<ExternalLink className="ml-auto size-3 text-muted-foreground" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
disabled={updateStatus.state === 'checking' || updateStatus.state === 'downloading'}
|
||||
|
||||
Reference in New Issue
Block a user