add first letter navigation, switch channels with ctrl k and not ctrl shift c
This commit is contained in:
@@ -40,6 +40,11 @@ const props = defineProps<Props>()
|
|||||||
const containerRef = ref<HTMLElement>()
|
const containerRef = ref<HTMLElement>()
|
||||||
const focusedChannelIndex = ref(0)
|
const focusedChannelIndex = ref(0)
|
||||||
|
|
||||||
|
// For alphanumeric navigation
|
||||||
|
const lastSearchChar = ref('')
|
||||||
|
const lastSearchTime = ref(0)
|
||||||
|
const searchResetDelay = 1000 // Reset after 1 second
|
||||||
|
|
||||||
// Handle individual channel events
|
// Handle individual channel events
|
||||||
const handleChannelSelect = (channelId: number) => {
|
const handleChannelSelect = (channelId: number) => {
|
||||||
emit('select-channel', channelId)
|
emit('select-channel', channelId)
|
||||||
@@ -101,8 +106,15 @@ const handleChannelKeydown = (event: KeyboardEvent, channelIndex: number) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// Handle alphanumeric navigation (a-z, 0-9)
|
||||||
|
const char = event.key.toLowerCase()
|
||||||
|
if (/^[a-z0-9]$/.test(char)) {
|
||||||
|
event.preventDefault()
|
||||||
|
handleAlphanumericNavigation(char, channelIndex)
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +134,41 @@ const focusChannel = (index: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleAlphanumericNavigation = (char: string, currentIndex: number) => {
|
||||||
|
if (props.channels.length === 0) return
|
||||||
|
|
||||||
|
const now = Date.now()
|
||||||
|
const sameChar = lastSearchChar.value === char && (now - lastSearchTime.value) < searchResetDelay
|
||||||
|
|
||||||
|
lastSearchChar.value = char
|
||||||
|
lastSearchTime.value = now
|
||||||
|
|
||||||
|
// Find channels starting with the character
|
||||||
|
const matchingIndices: number[] = []
|
||||||
|
props.channels.forEach((channel, index) => {
|
||||||
|
if (channel.name.toLowerCase().startsWith(char)) {
|
||||||
|
matchingIndices.push(index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (matchingIndices.length === 0) return
|
||||||
|
|
||||||
|
// If pressing the same character repeatedly, cycle through matches
|
||||||
|
if (sameChar) {
|
||||||
|
// Find the next match after current index
|
||||||
|
const nextMatch = matchingIndices.find(index => index > currentIndex)
|
||||||
|
if (nextMatch !== undefined) {
|
||||||
|
focusChannel(nextMatch)
|
||||||
|
} else {
|
||||||
|
// Wrap around to the first match
|
||||||
|
focusChannel(matchingIndices[0])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New character: jump to first match
|
||||||
|
focusChannel(matchingIndices[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Watch for channels changes and adjust focus
|
// Watch for channels changes and adjust focus
|
||||||
watch(() => props.channels.length, (newLength) => {
|
watch(() => props.channels.length, (newLength) => {
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ export function useKeyboardShortcuts() {
|
|||||||
|
|
||||||
if (shortcut) {
|
if (shortcut) {
|
||||||
// Allow certain shortcuts to work globally, even in input fields
|
// Allow certain shortcuts to work globally, even in input fields
|
||||||
const isGlobalShortcut = (shortcut.ctrlKey && shortcut.shiftKey) ||
|
const isGlobalShortcut = (shortcut.ctrlKey && shortcut.shiftKey) ||
|
||||||
shortcut.altKey ||
|
shortcut.altKey ||
|
||||||
shortcut.key === 'escape'
|
shortcut.key === 'escape' ||
|
||||||
|
(shortcut.ctrlKey && shortcut.key === 'k')
|
||||||
|
|
||||||
// Skip shortcuts that shouldn't work in input fields
|
// Skip shortcuts that shouldn't work in input fields
|
||||||
if (isInInputField && !isGlobalShortcut) {
|
if (isInInputField && !isGlobalShortcut) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -227,11 +227,10 @@ const setupKeyboardShortcuts = () => {
|
|||||||
handler: () => { showSearchDialog.value = true }
|
handler: () => { showSearchDialog.value = true }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Ctrl+Shift+C - Channel selector focus
|
// Ctrl+K - Channel selector focus
|
||||||
addShortcut({
|
addShortcut({
|
||||||
key: 'c',
|
key: 'k',
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
shiftKey: true,
|
|
||||||
handler: () => {
|
handler: () => {
|
||||||
// Focus the first channel in the list
|
// Focus the first channel in the list
|
||||||
const firstChannelButton = document.querySelector('.channel-item button') as HTMLElement
|
const firstChannelButton = document.querySelector('.channel-item button') as HTMLElement
|
||||||
@@ -557,6 +556,15 @@ const isUnsentMessage = (messageId: string | number): boolean => {
|
|||||||
return typeof messageId === 'string' && messageId.startsWith('unsent_')
|
return typeof messageId === 'string' && messageId.startsWith('unsent_')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update document title when channel changes
|
||||||
|
watch(() => appStore.currentChannel, (channel) => {
|
||||||
|
if (channel) {
|
||||||
|
document.title = `${channel.name} - Notebrook`
|
||||||
|
} else {
|
||||||
|
document.title = 'Notebrook'
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 1. Load saved state first (offline-first)
|
// 1. Load saved state first (offline-first)
|
||||||
|
|||||||
Reference in New Issue
Block a user