diff --git a/frontend/src/components/Kbd.jsx b/frontend/src/components/Kbd.jsx
new file mode 100644
index 0000000..90251ed
--- /dev/null
+++ b/frontend/src/components/Kbd.jsx
@@ -0,0 +1,10 @@
+import { shortcutKeys } from '../utils/shortcuts.js'
+
+// Raccourci clavier affiché à droite d'une entrée de menu ; caché sur écran tactile (styles/pz.css, .pz-kbd)
+export default function Kbd({ mod, k }) {
+ return (
+
+ {shortcutKeys(mod, k).map((key) => {key})}
+
+ )
+}
diff --git a/frontend/src/components/navbar/ContextualNavbar.jsx b/frontend/src/components/navbar/ContextualNavbar.jsx
index a824f39..7dec10b 100644
--- a/frontend/src/components/navbar/ContextualNavbar.jsx
+++ b/frontend/src/components/navbar/ContextualNavbar.jsx
@@ -12,6 +12,7 @@ import { t } from '../../i18n/index.js'
import { PHONE_QUERY } from '../../utils/sheetDrag'
import './ContextualNavbar.css'
import { initialOf } from '../../utils/initial.js'
+import Kbd from '../Kbd.jsx'
const ANNOT_TOOLS = [
{ id: 'pointer', label: t('Sélection'), Icon: MousePointer2 },
@@ -211,9 +212,9 @@ function ContextualNavbar({
}
const fixItems = []
if (isFamily) {
- fixItems.push({ key: 'edit', icon: , label: editLabel, sub: editModeActive ? null : t('dates, noms, liens, le propriétaire relit'), onSelect: onEditClick })
+ fixItems.push({ key: 'edit', icon: , label: editLabel, sub: editModeActive ? null : t('dates, noms, liens, le propriétaire relit'), onSelect: onEditClick, shortcut: 'e' })
} else if (isOwner || isDemo) {
- fixItems.push({ key: 'edit', icon: , label: editLabel, onSelect: onEditClick })
+ fixItems.push({ key: 'edit', icon: , label: editLabel, onSelect: onEditClick, shortcut: 'e' })
}
if (addItems.length) addGroups.push({ label: t('Ajouter'), items: addItems })
if (fixItems.length) addGroups.push({ label: t('Corriger'), items: fixItems })
@@ -235,6 +236,7 @@ function ContextualNavbar({
label={item.label}
sub={item.sub}
onSelect={run(item.onSelect)}
+ right={item.shortcut ? : null}
/>
))}
diff --git a/frontend/src/components/search/ExpandableSearch.jsx b/frontend/src/components/search/ExpandableSearch.jsx
index b9c4012..9912c85 100644
--- a/frontend/src/components/search/ExpandableSearch.jsx
+++ b/frontend/src/components/search/ExpandableSearch.jsx
@@ -3,6 +3,7 @@ import { Search, X } from 'lucide-react'
import SearchResultsDropdown from './SearchResultsDropdown'
import './ExpandableSearch.css'
import { t } from '../../i18n/index.js'
+import { isCommandK, withShortcut } from '../../utils/shortcuts.js'
/**
* Recherche expandable haut-droite
@@ -80,6 +81,22 @@ function ExpandableSearch({
setExpanded(true)
}
+ // Ctrl+K (⌘K sur Mac), de n'importe où : ouvre la recherche, ou y remet le curseur si elle est déjà ouverte
+ useEffect(() => {
+ function handleCommandK(event) {
+ if (!isCommandK(event)) return
+ event.preventDefault()
+ if (!expanded) setExpanded(true)
+ else {
+ inputRef.current?.focus()
+ inputRef.current?.select()
+ }
+ }
+
+ document.addEventListener('keydown', handleCommandK)
+ return () => document.removeEventListener('keydown', handleCommandK)
+ }, [expanded])
+
const handleInputChange = (e) => {
const value = e.target.value
setQuery(value)
@@ -106,6 +123,7 @@ function ExpandableSearch({
className="search-icon-button"
onClick={handleExpand}
aria-label={t('Rechercher une personne')}
+ title={withShortcut(t('Rechercher une personne'), 'cmd', 'k')}
>
diff --git a/frontend/src/styles/pz.css b/frontend/src/styles/pz.css
index ea9ce45..c65ee73 100644
--- a/frontend/src/styles/pz.css
+++ b/frontend/src/styles/pz.css
@@ -277,3 +277,8 @@ button:disabled:has(.pz-busy.is-busy) { opacity: 1; cursor: progress; }
inset: min(0px, calc((100% - 44px) / 2)) min(0px, calc((100% - 44px) / 2));
}
}
+
+/* Raccourci clavier à droite d'une entrée de menu (components/Kbd.jsx) ; rien sur écran tactile */
+.pz-kbd { display: inline-flex; gap: 3px; margin-left: auto; padding-left: 12px; }
+.pz-kbd kbd { min-width: 20px; padding: 1px 5px; border: 1px solid var(--pz-line); border-radius: 5px; background: var(--pz-bg); font: 500 11px/16px var(--pz-sans); color: var(--pz-muted); text-align: center; }
+@media (pointer: coarse) { .pz-kbd { display: none; } }
diff --git a/frontend/src/utils/shortcuts.js b/frontend/src/utils/shortcuts.js
new file mode 100644
index 0000000..77d585f
--- /dev/null
+++ b/frontend/src/utils/shortcuts.js
@@ -0,0 +1,19 @@
+// Raccourcis clavier affichés dans l'interface (infobulles, menus). Sur Mac, Ctrl s'écrit ⌃ et Cmd ⌘.
+const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || '')
+
+// mod : 'ctrl' (Ctrl partout) ou 'cmd' (⌘ sur Mac, Ctrl ailleurs)
+export function shortcutKeys(mod, key) {
+ const modifier = mod === 'cmd' ? (isMac ? '⌘' : 'Ctrl') : (isMac ? '⌃' : 'Ctrl')
+ return [modifier, key.toUpperCase()]
+}
+
+// Texte d'une infobulle : « Rechercher une personne (Ctrl K) », « … (⌘K) » sur Mac
+export function withShortcut(label, mod, key) {
+ const keys = shortcutKeys(mod, key)
+ return `${label} (${isMac ? keys.join('') : keys.join(' ')})`
+}
+
+// Ctrl+K, ou ⌘K sur Mac
+export function isCommandK(event) {
+ return (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === 'k'
+}