frontend: add biome checks and pre-commit integration (excluding preview files)
This commit is contained in:
+73
-33
@@ -24,21 +24,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import type HeaderMain from '@/components/HeaderMain.vue'
|
||||
import { onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
||||
import { loadSession, watchConnect, watchDisconnect } from '@/repositories/WS'
|
||||
import { useMainStore } from '@/stores/main'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import Router from '@/router/index'
|
||||
import type { SortOrder } from './utils/docsort'
|
||||
import { computed } from 'vue'
|
||||
import AccessDeniedModal from './components/AccessDeniedModal.vue'
|
||||
import SelectionToolbar from './components/SelectionToolbar.vue'
|
||||
import type SettingsModalVue from './components/SettingsModal.vue'
|
||||
import UserManagementModal from './components/UserManagementModal.vue'
|
||||
import UserTokensModal from './components/UserTokensModal.vue'
|
||||
import AccessDeniedModal from './components/AccessDeniedModal.vue'
|
||||
import SelectionToolbar from './components/SelectionToolbar.vue'
|
||||
import type { SortOrder } from './utils/docsort'
|
||||
|
||||
interface Path {
|
||||
path: string
|
||||
@@ -57,7 +57,10 @@ const path: ComputedRef<Path> = computed(() => {
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
document.title = path.value.path.replace(/\/$/, '').split('/').pop() || store.server.name || 'Cista Storage'
|
||||
document.title =
|
||||
path.value.path.replace(/\/$/, '').split('/').pop() ||
|
||||
store.server.name ||
|
||||
'Cista Storage'
|
||||
})
|
||||
onMounted(loadSession)
|
||||
onMounted(watchConnect)
|
||||
@@ -106,11 +109,11 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||
// Handle arrows: in search input with text, only up/down; otherwise all arrows
|
||||
const searchInput = inHeader && input
|
||||
const searchHasText = searchInput && (event.target as HTMLInputElement).value
|
||||
if (event.key.startsWith("Arrow")) {
|
||||
if (event.key.startsWith('Arrow')) {
|
||||
const dir = event.key.slice(5).toLowerCase()
|
||||
// In search with text: left/right move cursor, up/down navigate
|
||||
if (searchHasText && (dir === 'left' || dir === 'right')) {
|
||||
return // Let browser handle cursor movement
|
||||
return // Let browser handle cursor movement
|
||||
}
|
||||
arrow = dir
|
||||
}
|
||||
@@ -135,8 +138,7 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||
store.clearToast()
|
||||
headerMain.value!.clearSearch(event)
|
||||
store.focusBreadcrumb()
|
||||
}
|
||||
else if (!input && keyup && event.key === 'Backspace') {
|
||||
} else if (!input && keyup && event.key === 'Backspace') {
|
||||
Router.back()
|
||||
}
|
||||
// Select all (toggle); keydown to precede and prevent builtin
|
||||
@@ -151,20 +153,27 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||
else if (
|
||||
!input &&
|
||||
keyup &&
|
||||
(event.code === 'Backquote' || event.key === '1' || event.key === '2' || event.key === '3')
|
||||
(event.code === 'Backquote' ||
|
||||
event.key === '1' ||
|
||||
event.key === '2' ||
|
||||
event.key === '3')
|
||||
) {
|
||||
store.sort(['', 'name', 'modified', 'size'][+event.key || 0] as SortOrder)
|
||||
}
|
||||
// Rename
|
||||
else if (!input && c && keyup && !event.ctrlKey && (event.key === 'F2' || event.key === 'r')) {
|
||||
else if (
|
||||
!input &&
|
||||
c &&
|
||||
keyup &&
|
||||
!event.ctrlKey &&
|
||||
(event.key === 'F2' || event.key === 'r')
|
||||
) {
|
||||
fileExplorer.cursorRename()
|
||||
}
|
||||
// Toggle selections on file explorer; ignore all spaces to prevent scrolling built-in hotkey
|
||||
else if (!input && c && event.code === 'Space') {
|
||||
if (keyup && !event.altKey && !event.ctrlKey)
|
||||
fileExplorer.cursorSelect()
|
||||
}
|
||||
else return
|
||||
if (keyup && !event.altKey && !event.ctrlKey) fileExplorer.cursorSelect()
|
||||
} else return
|
||||
/// We are handling this!
|
||||
event.preventDefault()
|
||||
if (timer) {
|
||||
@@ -174,36 +183,67 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||
let f: any
|
||||
// Arrow navigation - always use fileExplorer for repeatable movement
|
||||
if (arrow && !keyup) {
|
||||
const focusSearch = () => (document.querySelector('.headermain input[type="search"]') as HTMLElement)?.focus()
|
||||
const focusBreadcrumb = () => (document.querySelector('.breadcrumb') as HTMLElement)?.focus()
|
||||
const focusSearch = () =>
|
||||
(
|
||||
document.querySelector('.headermain input[type="search"]') as HTMLElement
|
||||
)?.focus()
|
||||
const focusBreadcrumb = () =>
|
||||
(document.querySelector('.breadcrumb') as HTMLElement)?.focus()
|
||||
|
||||
if (inBreadcrumb) {
|
||||
// Breadcrumb: up→header (no repeat), down→files (with repeat)
|
||||
if (arrow === 'up') { focusSearch(); f = null }
|
||||
else if (arrow === 'down') { fileExplorer.focusFirst?.(); f = null }
|
||||
if (arrow === 'up') {
|
||||
focusSearch()
|
||||
f = null
|
||||
} else if (arrow === 'down') {
|
||||
fileExplorer.focusFirst?.()
|
||||
f = null
|
||||
}
|
||||
} else if (inHeader) {
|
||||
// Header: left/right navigate focusable items (buttons without tabindex=-1, search input, disk space)
|
||||
const items = Array.from(document.querySelectorAll('.headermain button:not([tabindex=\"-1\"]), .headermain input[type=\"search\"], .headermain [tabindex=\"0\"]')) as HTMLElement[]
|
||||
const items = Array.from(
|
||||
document.querySelectorAll(
|
||||
'.headermain button:not([tabindex="-1"]), .headermain input[type="search"], .headermain [tabindex="0"]'
|
||||
)
|
||||
) as HTMLElement[]
|
||||
const idx = items.indexOf(document.activeElement as HTMLElement)
|
||||
if (arrow === 'left' && idx > 0) { items[idx - 1]?.focus(); f = null }
|
||||
else if (arrow === 'right' && idx < items.length - 1) { items[idx + 1]?.focus(); f = null }
|
||||
else if (arrow === 'up') f = () => fileExplorer.up({ shiftKey: false })
|
||||
else if (arrow === 'down') { focusBreadcrumb(); f = null }
|
||||
if (arrow === 'left' && idx > 0) {
|
||||
items[idx - 1]?.focus()
|
||||
f = null
|
||||
} else if (arrow === 'right' && idx < items.length - 1) {
|
||||
items[idx + 1]?.focus()
|
||||
f = null
|
||||
} else if (arrow === 'up') f = () => fileExplorer.up({ shiftKey: false })
|
||||
else if (arrow === 'down') {
|
||||
focusBreadcrumb()
|
||||
f = null
|
||||
}
|
||||
} else {
|
||||
// File explorer: normal navigation with repeat
|
||||
switch (arrow) {
|
||||
case 'up': f = () => fileExplorer.up(event); break
|
||||
case 'down': f = () => fileExplorer.down(event); break
|
||||
case 'left': f = () => fileExplorer.left(event); break
|
||||
case 'right': f = () => fileExplorer.right(event); break
|
||||
case 'up':
|
||||
f = () => fileExplorer.up(event)
|
||||
break
|
||||
case 'down':
|
||||
f = () => fileExplorer.down(event)
|
||||
break
|
||||
case 'left':
|
||||
f = () => fileExplorer.left(event)
|
||||
break
|
||||
case 'right':
|
||||
f = () => fileExplorer.right(event)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (f) {
|
||||
// Initial move, then t0 delay until repeats at tr intervals
|
||||
const t0 = 200, tr = event.altKey ? 20 : 100
|
||||
const t0 = 200,
|
||||
tr = event.altKey ? 20 : 100
|
||||
f()
|
||||
timer = setTimeout(() => { timer = setInterval(f, tr) }, t0 - tr)
|
||||
timer = setTimeout(() => {
|
||||
timer = setInterval(f, tr)
|
||||
}, t0 - tr)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user