frontend: add biome checks and pre-commit integration (excluding preview files)

This commit is contained in:
2026-04-26 06:43:06 +00:00
parent 942b54d795
commit eb5ff82de6
42 changed files with 1016 additions and 504 deletions
+53 -41
View File
@@ -1,11 +1,11 @@
import type { FileEntry, FUID, SelectedItems } from '@/repositories/Document'
import type { FUID, FileEntry, SelectedItems } from '@/repositories/Document'
import { Doc } from '@/repositories/Document'
import { defineStore, type StateTree } from 'pinia'
import { resumeWatching, watchConnect } from '@/repositories/WS'
import { collator } from '@/utils'
import { watchConnect, resumeWatching } from '@/repositories/WS'
import { sorted, type SortOrder } from '@/utils/docsort'
import { type SortOrder, sorted } from '@/utils/docsort'
import SearchWorker from '@/workers/searchWorker?worker'
import { getDocuments, setDocuments, documentRef } from './documentStore'
import { type StateTree, defineStore } from 'pinia'
import { documentRef, getDocuments, setDocuments } from './documentStore'
// Singleton search worker instance
let searchWorker: Worker | null = null
@@ -19,8 +19,8 @@ function getSearchWorker(): Worker {
if (!searchWorker) {
searchWorker = new SearchWorker()
// Set up message handler once
searchWorker.onmessage = (e) => {
if (!searchStore || e.data.id !== searchId) return // Stale result
searchWorker.onmessage = e => {
if (!searchStore || e.data.id !== searchId) return // Stale result
// Convert plain data back to Doc instances
const docs = e.data.docs.map((d: any) => new Doc(d))
@@ -34,7 +34,7 @@ function getSearchWorker(): Worker {
// Throttle rapid intermediate updates to reduce UI flicker
const now = performance.now()
if (!e.data.done && now - lastResultUpdate < 50) {
return // Skip intermediate update if too recent
return // Skip intermediate update if too recent
}
lastResultUpdate = now
@@ -73,13 +73,13 @@ export const useMainStore = defineStore('main', {
searchLoading: false,
_searchRouteTimer: null as ReturnType<typeof setTimeout> | null,
fileExplorer: null as any,
error: '' as string, // Permanent status message (e.g., "Reconnecting...")
toast: '' as string, // Temporary toast (auto-dismisses)
error: '' as string, // Permanent status message (e.g., "Reconnecting...")
toast: '' as string, // Temporary toast (auto-dismisses)
toastTimeout: null as ReturnType<typeof setTimeout> | null,
connected: false,
authInProgress: false,
cursor: '' as string,
server: {} as Record<string, any> & { public?: boolean, paskia?: boolean },
server: {} as Record<string, any> & { public?: boolean; paskia?: boolean },
dialog: '' as '' | 'settings' | 'usermgmt' | 'accessdenied' | 'tokens',
uprogress: {} as any,
dprogress: {} as any,
@@ -87,19 +87,19 @@ export const useMainStore = defineStore('main', {
gallery: false,
sortListing: '' as SortOrder,
sortFiltered: '' as SortOrder,
searchHotkey: '/', // Character shown for search hotkey (Slash key)
searchHotkey: '/' // Character shown for search hotkey (Slash key)
},
user: {
username: '' as string,
privileged: false as boolean,
isLoggedIn: false as boolean,
isLoggedIn: false as boolean
},
space: {
disk: 0,
free: 0,
used: 0,
storage: 0,
allocated: 0,
allocated: 0
}
}),
persist: {
@@ -114,7 +114,7 @@ export const useMainStore = defineStore('main', {
tree.selected = Array.from(tree.selected)
return JSON.stringify(tree)
}
},
}
},
actions: {
updateRoot(root: FileEntry[]) {
@@ -122,22 +122,26 @@ export const useMainStore = defineStore('main', {
let loc = [] as string[]
for (const [level, name, key, mtime, size, allocated, isfile] of root) {
loc = loc.slice(0, level - 1)
docs.push(new Doc({
name,
loc: level ? loc.join('/') : '/',
key,
size,
allocated,
mtime,
dir: !isfile,
}))
docs.push(
new Doc({
name,
loc: level ? loc.join('/') : '/',
key,
size,
allocated,
mtime,
dir: !isfile
})
)
loc.push(name)
}
// Store in non-reactive external storage
setDocuments(docs)
// Clear ghosts that now exist in the real list
const realPaths = new Set(docs.map(d => d.loc ? `${d.loc}/${d.name}` : d.name))
this.ghosts = this.ghosts.filter(g => !realPaths.has(g.loc ? `${g.loc}/${g.name}` : g.name))
const realPaths = new Set(docs.map(d => (d.loc ? `${d.loc}/${d.name}` : d.name)))
this.ghosts = this.ghosts.filter(
g => !realPaths.has(g.loc ? `${g.loc}/${g.name}` : g.name)
)
// Clear hidden paths that no longer exist (deletion confirmed)
for (const path of this.hiddenPaths.keys()) {
if (!realPaths.has(path)) this.hiddenPaths.delete(path)
@@ -224,14 +228,14 @@ export const useMainStore = defineStore('main', {
size: doc.size,
allocated: doc.allocated,
mtime: doc.mtime,
dir: doc.dir,
dir: doc.dir
}))
worker.postMessage({ type: 'update', documents: docData })
},
search(query: string, loc: string) {
const worker = getSearchWorker()
const id = ++searchId
searchStore = this // Store reference for worker callback
searchStore = this // Store reference for worker callback
// Update query immediately so watchers know we're handling this
this.query = query
@@ -264,7 +268,8 @@ export const useMainStore = defineStore('main', {
// Delay showing loading indicator to avoid flicker on fast searches
loadingTimer = setTimeout(() => {
if (searchId === id) { // Still the current search
if (searchId === id) {
// Still the current search
this.searchLoading = true
}
loadingTimer = null
@@ -296,7 +301,7 @@ export const useMainStore = defineStore('main', {
this.cursor = ''
},
async logout() {
console.log("Logout")
console.log('Logout')
try {
const res = await fetch('/auth/api/logout', { method: 'POST' })
if (!res.ok) {
@@ -326,25 +331,29 @@ export const useMainStore = defineStore('main', {
showSortToast(order: SortOrder | '') {
const labels: Record<string, string> = {
'': 'Folders first',
'name': 'Alphabetical order',
'modified': 'Newest first',
'size': 'Largest first',
name: 'Alphabetical order',
modified: 'Newest first',
size: 'Largest first'
}
this.showToast(labels[order] || order, 1200)
},
focusBreadcrumb() {
(document.querySelector('.breadcrumb') as HTMLAnchorElement).focus()
;(document.querySelector('.breadcrumb') as HTMLAnchorElement).focus()
},
cancelDownloads() {
location.reload() // FIXME
location.reload() // FIXME
},
cancelUploads() {
location.reload() // FIXME
},
location.reload() // FIXME
}
},
getters: {
sortOrder(): SortOrder { return this.query ? this.prefs.sortFiltered : this.prefs.sortListing },
isUserLogged(): boolean { return this.user.isLoggedIn },
sortOrder(): SortOrder {
return this.query ? this.prefs.sortFiltered : this.prefs.sortListing
},
isUserLogged(): boolean {
return this.user.isLoggedIn
},
/** Get documents count (triggers on docVersion change) */
documentCount(): number {
// Access docVersion to make this reactive
@@ -366,7 +375,7 @@ export const useMainStore = defineStore('main', {
missing: new Set(),
docs: {},
keys: [],
recursive: [],
recursive: []
}
for (const doc of docs) {
if (selected.has(doc.key)) {
@@ -384,7 +393,10 @@ export const useMainStore = defineStore('main', {
const nremove = base.loc.length
ret.recursive.push([base.name, basepath, base])
for (const doc of docs) {
if (doc.loc === basepath || doc.loc.startsWith(basepath) && doc.loc[basepath.length] === '/') {
if (
doc.loc === basepath ||
(doc.loc.startsWith(basepath) && doc.loc[basepath.length] === '/')
) {
const full = doc.loc ? `${doc.loc}/${doc.name}` : doc.name
const rel = full.slice(nremove)
ret.recursive.push([rel, full, doc])
+1 -1
View File
@@ -1,7 +1,7 @@
import { clearTree } from '@/repositories/WS'
import { defineStore } from 'pinia'
import { computed } from 'vue'
import { useMainStore } from './main'
import { clearTree } from '@/repositories/WS'
export const useSsoAuthStore = defineStore('ssoAuth', () => {
const isExternalAuth = computed(() => {