60 lines
2.5 KiB
TypeScript
60 lines
2.5 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
import {
|
|
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
logEvent,
|
|
} from '../services/analytics/index.js'
|
|
import { useOptionalKeybindingContext } from './KeybindingContext.js'
|
|
import type { KeybindingContextName } from './types.js'
|
|
|
|
// TODO(keybindings-migration): Remove fallback parameter after migration is complete
|
|
// and we've confirmed no 'keybinding_fallback_used' events are being logged.
|
|
// The fallback exists as a safety net during migration - if bindings fail to load
|
|
// or an action isn't found, we fall back to hardcoded values. Once stable, callers
|
|
// should be able to trust that getBindingDisplayText always returns a value for
|
|
// known actions, and we can remove this defensive pattern.
|
|
|
|
/**
|
|
* Hook to get the display text for a configured shortcut.
|
|
* Returns the configured binding or a fallback if unavailable.
|
|
*
|
|
* @param action - The action name (e.g., 'app:toggleTranscript')
|
|
* @param context - The keybinding context (e.g., 'Global')
|
|
* @param fallback - Fallback text if keybinding context unavailable
|
|
* @returns The configured shortcut display text
|
|
*
|
|
* @example
|
|
* const expandShortcut = useShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o')
|
|
* // Returns the user's configured binding, or 'ctrl+o' as default
|
|
*/
|
|
export function useShortcutDisplay(
|
|
action: string,
|
|
context: KeybindingContextName,
|
|
fallback: string,
|
|
): string {
|
|
const keybindingContext = useOptionalKeybindingContext()
|
|
const resolved = keybindingContext?.getDisplayText(action, context)
|
|
const isFallback = resolved === undefined
|
|
const reason = keybindingContext ? 'action_not_found' : 'no_context'
|
|
|
|
// Log fallback usage once per mount (not on every render) to avoid
|
|
// flooding analytics with events from frequent re-renders.
|
|
const hasLoggedRef = useRef(false)
|
|
useEffect(() => {
|
|
if (isFallback && !hasLoggedRef.current) {
|
|
hasLoggedRef.current = true
|
|
logEvent('tengu_keybinding_fallback_used', {
|
|
action:
|
|
action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
context:
|
|
context as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
fallback:
|
|
fallback as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
reason:
|
|
reason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
})
|
|
}
|
|
}, [isFallback, action, context, fallback, reason])
|
|
|
|
return isFallback ? fallback : resolved
|
|
}
|