Compare commits
6 Commits
6585ec2abb
...
cf15a0f9c2
Author | SHA1 | Date | |
---|---|---|---|
cf15a0f9c2 | |||
452192d0a9 | |||
0d50359dae | |||
420ff46f05 | |||
b312065d3d | |||
b07916309e |
@@ -7,7 +7,7 @@
|
||||
<title>Notebrook</title>
|
||||
<meta name="description" content="Light note taking app in messenger style">
|
||||
</head>
|
||||
<body>
|
||||
<body role="application">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
@@ -5,9 +5,9 @@
|
||||
{ 'message--unsent': isUnsent }
|
||||
]"
|
||||
:data-message-id="message.id"
|
||||
:tabindex="tabindex || 0"
|
||||
:tabindex="tabindex || -1"
|
||||
:aria-label="messageAriaLabel"
|
||||
role="listitem"
|
||||
role="option"
|
||||
@keydown="handleKeydown"
|
||||
>
|
||||
<div class="message__content">
|
||||
|
@@ -1,33 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="messages-container"
|
||||
ref="containerRef"
|
||||
@keydown="handleKeydown"
|
||||
tabindex="0"
|
||||
role="list"
|
||||
:aria-label="messagesAriaLabel"
|
||||
>
|
||||
<div class="messages-container" ref="containerRef" @keydown="handleKeydown" tabindex="0" role="listbox"
|
||||
:aria-label="messagesAriaLabel">
|
||||
<div class="messages" role="presentation">
|
||||
<!-- Regular Messages -->
|
||||
<MessageItem
|
||||
v-for="(message, index) in messages"
|
||||
:key="message.id"
|
||||
:message="message"
|
||||
:tabindex="index === focusedMessageIndex ? 0 : -1"
|
||||
:data-message-index="index"
|
||||
@focus="focusedMessageIndex = index"
|
||||
/>
|
||||
|
||||
<MessageItem v-for="(message, index) in messages" :key="message.id" :message="message"
|
||||
:tabindex="index === focusedMessageIndex ? 0 : -1" :data-message-index="index"
|
||||
:aria-selected="index === focusedMessageIndex ? 'true' : 'false'"
|
||||
@focus="focusedMessageIndex = index" />
|
||||
|
||||
<!-- Unsent Messages -->
|
||||
<MessageItem
|
||||
v-for="(unsentMsg, index) in unsentMessages"
|
||||
:key="unsentMsg.id"
|
||||
:message="unsentMsg"
|
||||
:is-unsent="true"
|
||||
:tabindex="(messages.length + index) === focusedMessageIndex ? 0 : -1"
|
||||
:data-message-index="messages.length + index"
|
||||
@focus="focusedMessageIndex = messages.length + index"
|
||||
/>
|
||||
<MessageItem v-for="(unsentMsg, index) in unsentMessages" :key="unsentMsg.id" :message="unsentMsg"
|
||||
:is-unsent="true" :tabindex="(messages.length + index) === focusedMessageIndex ? 0 : -1"
|
||||
:data-message-index="messages.length + index" @focus="focusedMessageIndex = messages.length + index" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -59,13 +43,13 @@ const totalMessages = computed(() => allMessages.value.length)
|
||||
const messagesAriaLabel = computed(() => {
|
||||
const total = totalMessages.value
|
||||
const current = focusedMessageIndex.value + 1
|
||||
|
||||
|
||||
if (total === 0) {
|
||||
return 'Messages list, no messages'
|
||||
} else if (total === 1) {
|
||||
return 'Messages list, 1 message'
|
||||
} else {
|
||||
return `Messages list, ${total} messages, currently focused on message ${current} of ${total}`
|
||||
return `Messages list, ${total} messages`
|
||||
}
|
||||
})
|
||||
|
||||
@@ -74,50 +58,50 @@ const navigationHint = 'Use arrow keys to navigate, Page Up/Down to jump 10 mess
|
||||
// Keyboard navigation
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (totalMessages.value === 0) return
|
||||
|
||||
|
||||
let newIndex = focusedMessageIndex.value
|
||||
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
event.preventDefault()
|
||||
newIndex = Math.max(0, focusedMessageIndex.value - 1)
|
||||
break
|
||||
|
||||
|
||||
case 'ArrowDown':
|
||||
event.preventDefault()
|
||||
newIndex = Math.min(totalMessages.value - 1, focusedMessageIndex.value + 1)
|
||||
break
|
||||
|
||||
|
||||
case 'PageUp':
|
||||
event.preventDefault()
|
||||
newIndex = Math.max(0, focusedMessageIndex.value - 10)
|
||||
break
|
||||
|
||||
|
||||
case 'PageDown':
|
||||
event.preventDefault()
|
||||
newIndex = Math.min(totalMessages.value - 1, focusedMessageIndex.value + 10)
|
||||
break
|
||||
|
||||
|
||||
case 'Home':
|
||||
event.preventDefault()
|
||||
newIndex = 0
|
||||
break
|
||||
|
||||
|
||||
case 'End':
|
||||
event.preventDefault()
|
||||
newIndex = totalMessages.value - 1
|
||||
break
|
||||
|
||||
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
event.preventDefault()
|
||||
selectCurrentMessage()
|
||||
return
|
||||
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (newIndex !== focusedMessageIndex.value) {
|
||||
focusMessage(newIndex)
|
||||
}
|
||||
@@ -229,19 +213,19 @@ defineExpose({
|
||||
.messages-container {
|
||||
background: #111827;
|
||||
}
|
||||
|
||||
|
||||
.messages-container:focus {
|
||||
outline-color: #60a5fa;
|
||||
}
|
||||
|
||||
|
||||
.messages-container::-webkit-scrollbar-track {
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
|
||||
.messages-container::-webkit-scrollbar-thumb {
|
||||
background: #4b5563;
|
||||
}
|
||||
|
||||
|
||||
.messages-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #6b7280;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
<label class="setting-item">
|
||||
<input
|
||||
ref="soundInput"
|
||||
type="checkbox"
|
||||
v-model="localSettings.soundEnabled"
|
||||
class="checkbox"
|
||||
@@ -245,6 +246,7 @@ const isSaving = ref(false)
|
||||
const isResetting = ref(false)
|
||||
const showResetConfirm = ref(false)
|
||||
const selectedVoiceURI = ref('')
|
||||
const soundInput = ref()
|
||||
|
||||
// Computed property for current server URL
|
||||
const currentServerUrl = computed(() => authStore.serverUrl)
|
||||
@@ -338,6 +340,7 @@ onMounted(() => {
|
||||
|
||||
// Set up voice selection
|
||||
selectedVoiceURI.value = appStore.settings.selectedVoiceURI || ''
|
||||
soundInput.value.focus();
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="channel-list-container" ref="containerRef">
|
||||
<ul class="channel-list" role="list" aria-label="Channels">
|
||||
<ul class="channel-list" role="listbox" aria-label="Channels">
|
||||
<ChannelListItem
|
||||
v-for="(channel, index) in channels"
|
||||
:key="channel.id"
|
||||
|
@@ -12,9 +12,11 @@
|
||||
class="channel-button"
|
||||
@click="$emit('select', channel.id)"
|
||||
@focus="handleFocus"
|
||||
role="option"
|
||||
:aria-current="isActive"
|
||||
aria-selected="true"
|
||||
@keydown="handleKeydown"
|
||||
:tabindex="tabindex"
|
||||
:aria-pressed="isActive"
|
||||
:aria-label="channelAriaLabel"
|
||||
>
|
||||
<span class="channel-name">{{ channel.name }}</span>
|
||||
@@ -23,7 +25,7 @@
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
<button v-if="isActive"
|
||||
class="channel-info-button"
|
||||
@click.stop="$emit('info', channel)"
|
||||
:aria-label="`Channel info for ${channel.name}`"
|
||||
@@ -58,7 +60,7 @@ const props = defineProps<Props>()
|
||||
|
||||
// Better ARIA label that announces the channel name and unread count
|
||||
const channelAriaLabel = computed(() => {
|
||||
let label = `${props.channel.name} channel`
|
||||
let label = `${props.channel.name}`
|
||||
if (props.unreadCount) {
|
||||
label += `, ${props.unreadCount} unread message${props.unreadCount > 1 ? 's' : ''}`
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
<form @submit.prevent="handleAuth" class="auth-form">
|
||||
<BaseInput
|
||||
v-model="serverUrl"
|
||||
ref="serverInput"
|
||||
type="url"
|
||||
label="Server URL (optional)"
|
||||
:placeholder="defaultServerUrl"
|
||||
@@ -59,7 +60,7 @@ const serverUrl = ref('')
|
||||
const error = ref('')
|
||||
const isLoading = ref(false)
|
||||
const tokenInput = ref()
|
||||
|
||||
const serverInput = ref()
|
||||
// Get default server URL for placeholder
|
||||
const defaultServerUrl = authStore.getDefaultServerUrl()
|
||||
|
||||
@@ -80,7 +81,7 @@ const handleAuth = async () => {
|
||||
router.push('/')
|
||||
} else {
|
||||
error.value = 'Invalid authentication token or server URL'
|
||||
tokenInput.value?.focus()
|
||||
serverInput.value?.focus()
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = 'Authentication failed. Please check your token and server URL.'
|
||||
@@ -91,7 +92,7 @@ const handleAuth = async () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
tokenInput.value?.focus()
|
||||
serverInput.value?.focus()
|
||||
playSound('intro')
|
||||
})
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user