Compare commits
	
		
			6 Commits
		
	
	
		
			6585ec2abb
			...
			cf15a0f9c2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cf15a0f9c2 | |||
| 452192d0a9 | |||
| 0d50359dae | |||
| 420ff46f05 | |||
| b312065d3d | |||
| b07916309e | 
@@ -7,7 +7,7 @@
 | 
				
			|||||||
    <title>Notebrook</title>
 | 
					    <title>Notebrook</title>
 | 
				
			||||||
    <meta name="description" content="Light note taking app in messenger style">
 | 
					    <meta name="description" content="Light note taking app in messenger style">
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body role="application">
 | 
				
			||||||
    <div id="app"></div>
 | 
					    <div id="app"></div>
 | 
				
			||||||
    <script type="module" src="/src/main.ts"></script>
 | 
					    <script type="module" src="/src/main.ts"></script>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,9 @@
 | 
				
			|||||||
      { 'message--unsent': isUnsent }
 | 
					      { 'message--unsent': isUnsent }
 | 
				
			||||||
    ]"
 | 
					    ]"
 | 
				
			||||||
    :data-message-id="message.id"
 | 
					    :data-message-id="message.id"
 | 
				
			||||||
    :tabindex="tabindex || 0"
 | 
					    :tabindex="tabindex || -1"
 | 
				
			||||||
    :aria-label="messageAriaLabel"
 | 
					    :aria-label="messageAriaLabel"
 | 
				
			||||||
    role="listitem"
 | 
					    role="option"
 | 
				
			||||||
    @keydown="handleKeydown"
 | 
					    @keydown="handleKeydown"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <div class="message__content">
 | 
					    <div class="message__content">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +1,17 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div 
 | 
					  <div class="messages-container" ref="containerRef" @keydown="handleKeydown" tabindex="0" role="listbox"
 | 
				
			||||||
    class="messages-container" 
 | 
					    :aria-label="messagesAriaLabel">
 | 
				
			||||||
    ref="containerRef"
 | 
					 | 
				
			||||||
    @keydown="handleKeydown"
 | 
					 | 
				
			||||||
    tabindex="0"
 | 
					 | 
				
			||||||
    role="list"
 | 
					 | 
				
			||||||
    :aria-label="messagesAriaLabel"
 | 
					 | 
				
			||||||
  >
 | 
					 | 
				
			||||||
    <div class="messages" role="presentation">
 | 
					    <div class="messages" role="presentation">
 | 
				
			||||||
      <!-- Regular Messages -->
 | 
					      <!-- Regular Messages -->
 | 
				
			||||||
      <MessageItem
 | 
					      <MessageItem v-for="(message, index) in messages" :key="message.id" :message="message"
 | 
				
			||||||
        v-for="(message, index) in messages"
 | 
					        :tabindex="index === focusedMessageIndex ? 0 : -1" :data-message-index="index"
 | 
				
			||||||
        :key="message.id"
 | 
					        :aria-selected="index === focusedMessageIndex ? 'true' : 'false'"
 | 
				
			||||||
        :message="message"
 | 
					        @focus="focusedMessageIndex = index" />
 | 
				
			||||||
        :tabindex="index === focusedMessageIndex ? 0 : -1"
 | 
					 | 
				
			||||||
        :data-message-index="index"
 | 
					 | 
				
			||||||
        @focus="focusedMessageIndex = index"
 | 
					 | 
				
			||||||
      />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <!-- Unsent Messages -->
 | 
					      <!-- Unsent Messages -->
 | 
				
			||||||
      <MessageItem
 | 
					      <MessageItem v-for="(unsentMsg, index) in unsentMessages" :key="unsentMsg.id" :message="unsentMsg"
 | 
				
			||||||
        v-for="(unsentMsg, index) in unsentMessages"
 | 
					        :is-unsent="true" :tabindex="(messages.length + index) === focusedMessageIndex ? 0 : -1"
 | 
				
			||||||
        :key="unsentMsg.id"
 | 
					        :data-message-index="messages.length + index" @focus="focusedMessageIndex = messages.length + index" />
 | 
				
			||||||
        :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>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@@ -65,7 +49,7 @@ const messagesAriaLabel = computed(() => {
 | 
				
			|||||||
  } else if (total === 1) {
 | 
					  } else if (total === 1) {
 | 
				
			||||||
    return 'Messages list, 1 message'
 | 
					    return 'Messages list, 1 message'
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    return `Messages list, ${total} messages, currently focused on message ${current} of ${total}`
 | 
					    return `Messages list, ${total} messages`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        <label class="setting-item">
 | 
					        <label class="setting-item">
 | 
				
			||||||
          <input 
 | 
					          <input 
 | 
				
			||||||
 | 
					          ref="soundInput"
 | 
				
			||||||
            type="checkbox" 
 | 
					            type="checkbox" 
 | 
				
			||||||
            v-model="localSettings.soundEnabled"
 | 
					            v-model="localSettings.soundEnabled"
 | 
				
			||||||
            class="checkbox"
 | 
					            class="checkbox"
 | 
				
			||||||
@@ -245,6 +246,7 @@ const isSaving = ref(false)
 | 
				
			|||||||
const isResetting = ref(false)
 | 
					const isResetting = ref(false)
 | 
				
			||||||
const showResetConfirm = ref(false)
 | 
					const showResetConfirm = ref(false)
 | 
				
			||||||
const selectedVoiceURI = ref('')
 | 
					const selectedVoiceURI = ref('')
 | 
				
			||||||
 | 
					const soundInput = ref()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Computed property for current server URL
 | 
					// Computed property for current server URL
 | 
				
			||||||
const currentServerUrl = computed(() => authStore.serverUrl)
 | 
					const currentServerUrl = computed(() => authStore.serverUrl)
 | 
				
			||||||
@@ -338,6 +340,7 @@ onMounted(() => {
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
  // Set up voice selection
 | 
					  // Set up voice selection
 | 
				
			||||||
  selectedVoiceURI.value = appStore.settings.selectedVoiceURI || ''
 | 
					  selectedVoiceURI.value = appStore.settings.selectedVoiceURI || ''
 | 
				
			||||||
 | 
					  soundInput.value.focus();
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="channel-list-container" ref="containerRef">
 | 
					  <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
 | 
					      <ChannelListItem
 | 
				
			||||||
        v-for="(channel, index) in channels"
 | 
					        v-for="(channel, index) in channels"
 | 
				
			||||||
        :key="channel.id"
 | 
					        :key="channel.id"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,11 @@
 | 
				
			|||||||
        class="channel-button"
 | 
					        class="channel-button"
 | 
				
			||||||
        @click="$emit('select', channel.id)"
 | 
					        @click="$emit('select', channel.id)"
 | 
				
			||||||
        @focus="handleFocus"
 | 
					        @focus="handleFocus"
 | 
				
			||||||
 | 
					        role="option"
 | 
				
			||||||
 | 
					        :aria-current="isActive"
 | 
				
			||||||
 | 
					        aria-selected="true"
 | 
				
			||||||
        @keydown="handleKeydown"
 | 
					        @keydown="handleKeydown"
 | 
				
			||||||
        :tabindex="tabindex"
 | 
					        :tabindex="tabindex"
 | 
				
			||||||
        :aria-pressed="isActive"
 | 
					 | 
				
			||||||
        :aria-label="channelAriaLabel"
 | 
					        :aria-label="channelAriaLabel"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <span class="channel-name">{{ channel.name }}</span>
 | 
					        <span class="channel-name">{{ channel.name }}</span>
 | 
				
			||||||
@@ -23,7 +25,7 @@
 | 
				
			|||||||
        </span>
 | 
					        </span>
 | 
				
			||||||
      </button>
 | 
					      </button>
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      <button
 | 
					      <button v-if="isActive"
 | 
				
			||||||
        class="channel-info-button"
 | 
					        class="channel-info-button"
 | 
				
			||||||
        @click.stop="$emit('info', channel)"
 | 
					        @click.stop="$emit('info', channel)"
 | 
				
			||||||
        :aria-label="`Channel info for ${channel.name}`"
 | 
					        :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
 | 
					// Better ARIA label that announces the channel name and unread count
 | 
				
			||||||
const channelAriaLabel = computed(() => {
 | 
					const channelAriaLabel = computed(() => {
 | 
				
			||||||
  let label = `${props.channel.name} channel`
 | 
					  let label = `${props.channel.name}`
 | 
				
			||||||
  if (props.unreadCount) {
 | 
					  if (props.unreadCount) {
 | 
				
			||||||
    label += `, ${props.unreadCount} unread message${props.unreadCount > 1 ? 's' : ''}`
 | 
					    label += `, ${props.unreadCount} unread message${props.unreadCount > 1 ? 's' : ''}`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@
 | 
				
			|||||||
      <form @submit.prevent="handleAuth" class="auth-form">
 | 
					      <form @submit.prevent="handleAuth" class="auth-form">
 | 
				
			||||||
        <BaseInput
 | 
					        <BaseInput
 | 
				
			||||||
          v-model="serverUrl"
 | 
					          v-model="serverUrl"
 | 
				
			||||||
 | 
					          ref="serverInput"
 | 
				
			||||||
          type="url"
 | 
					          type="url"
 | 
				
			||||||
          label="Server URL (optional)"
 | 
					          label="Server URL (optional)"
 | 
				
			||||||
          :placeholder="defaultServerUrl"
 | 
					          :placeholder="defaultServerUrl"
 | 
				
			||||||
@@ -59,7 +60,7 @@ const serverUrl = ref('')
 | 
				
			|||||||
const error = ref('')
 | 
					const error = ref('')
 | 
				
			||||||
const isLoading = ref(false)
 | 
					const isLoading = ref(false)
 | 
				
			||||||
const tokenInput = ref()
 | 
					const tokenInput = ref()
 | 
				
			||||||
 | 
					const serverInput = ref()
 | 
				
			||||||
// Get default server URL for placeholder
 | 
					// Get default server URL for placeholder
 | 
				
			||||||
const defaultServerUrl = authStore.getDefaultServerUrl()
 | 
					const defaultServerUrl = authStore.getDefaultServerUrl()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,7 +81,7 @@ const handleAuth = async () => {
 | 
				
			|||||||
      router.push('/')
 | 
					      router.push('/')
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      error.value = 'Invalid authentication token or server URL'
 | 
					      error.value = 'Invalid authentication token or server URL'
 | 
				
			||||||
      tokenInput.value?.focus()
 | 
					      serverInput.value?.focus()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    error.value = 'Authentication failed. Please check your token and server URL.'
 | 
					    error.value = 'Authentication failed. Please check your token and server URL.'
 | 
				
			||||||
@@ -91,7 +92,7 @@ const handleAuth = async () => {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
  tokenInput.value?.focus()
 | 
					  serverInput.value?.focus()
 | 
				
			||||||
  playSound('intro')
 | 
					  playSound('intro')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user