+
diff --git a/src/lib/keyboard-shortcuts.ts b/src/lib/keyboard-shortcuts.ts
new file mode 100644
index 00000000..e8a0a556
--- /dev/null
+++ b/src/lib/keyboard-shortcuts.ts
@@ -0,0 +1,22 @@
+/** Shared guards for app-wide keyboard shortcuts (help, focus panes, etc.). */
+
+export function isRadixDialogOpen(): boolean {
+ return !!document.querySelector('[data-radix-dialog-content][data-state="open"]')
+}
+
+export function shouldIgnoreKeyboardShortcutEvent(target: EventTarget | null): boolean {
+ if (!(target instanceof HTMLElement)) return false
+ const el = target
+ const tag = el.tagName
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true
+ if (el.isContentEditable) return true
+ if (el.closest('[contenteditable="true"]')) return true
+ if (el.closest('.ProseMirror')) return true
+ if (el.getAttribute('role') === 'textbox') return true
+ return false
+}
+
+export const FOCUS_PRIMARY_SCROLL_SHORTCUT_KEY = 'f'
+export const FOCUS_SECONDARY_SCROLL_SHORTCUT_KEY = 's'
+/** Shift+Alt+N — open new note composer (handled in KeyboardShortcutsHelpProvider). */
+export const OPEN_NEW_POST_SHORTCUT_KEY = 'n'
diff --git a/src/pages/primary/MePage/index.tsx b/src/pages/primary/MePage/index.tsx
index 827f0c2b..0d4c12ee 100644
--- a/src/pages/primary/MePage/index.tsx
+++ b/src/pages/primary/MePage/index.tsx
@@ -21,7 +21,7 @@ import {
UserRound,
Wallet
} from 'lucide-react'
-import { forwardRef, HTMLProps, useState } from 'react'
+import { forwardRef, HTMLProps, useState, type KeyboardEvent, type MouseEvent } from 'react'
import { useTranslation } from 'react-i18next'
const MePage = forwardRef((_, ref) => {
@@ -117,6 +117,8 @@ function Item({
children,
className,
hideChevron = false,
+ onClick,
+ onKeyDown,
...props
}: HTMLProps
& { hideChevron?: boolean }) {
return (
@@ -126,6 +128,16 @@ function Item({
className
)}
{...props}
+ role="button"
+ tabIndex={0}
+ onClick={onClick}
+ onKeyDown={(e: KeyboardEvent) => {
+ onKeyDown?.(e)
+ if (!e.defaultPrevented && (e.key === 'Enter' || e.key === ' ')) {
+ e.preventDefault()
+ onClick?.(e as unknown as MouseEvent)
+ }
+ }}
>
{children}
{!hideChevron && }
diff --git a/src/pages/primary/NoteListPage/index.tsx b/src/pages/primary/NoteListPage/index.tsx
index c2a01474..3f7aeba2 100644
--- a/src/pages/primary/NoteListPage/index.tsx
+++ b/src/pages/primary/NoteListPage/index.tsx
@@ -21,6 +21,7 @@ import React, {
} from 'react'
import { useTranslation } from 'react-i18next'
import FeedButton from './FeedButton'
+import { KeyboardShortcutsHelpButton } from '@/components/KeyboardShortcutsHelp'
import ExploreButton from '@/components/Titlebar/ExploreButton'
import AccountButton from '@/components/Titlebar/AccountButton'
import FollowingFeed from './FollowingFeed'
@@ -185,6 +186,7 @@ function NoteListPageTitlebar({
)}
+