Quality of life: in-app dialogs, selections in the URL, editable widgets, token dialog, gallery search, status history, stream errors that say why, tab reordering and shortcuts, log out everywhere

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-19 10:34:59 -07:00
co-authored by Claude Fable 5.1
parent 8ee2a2d293
commit d7ed4d8549
24 changed files with 361 additions and 101 deletions
+21
View File
@@ -0,0 +1,21 @@
// The part of the URL after the page — #/grid/services/<this> — is that page's selection:
// which unit, which job, which folder. Reload keeps it and a link can carry it.
import { useCallback, useEffect, useState } from 'react'
const read = () => { try { return decodeURIComponent(location.hash.split('/').slice(3).join('/')) } catch { return '' } }
export function useHashSel(): [string, (v: string) => void] {
const [sel, setSel] = useState(read)
useEffect(() => {
const h = () => setSel(read())
window.addEventListener('popstate', h); window.addEventListener('hashchange', h)
return () => { window.removeEventListener('popstate', h); window.removeEventListener('hashchange', h) }
}, [])
const set = useCallback((v: string) => {
const base = location.hash.split('/').slice(0, 3).join('/')
const want = v ? `${base}/${encodeURIComponent(v)}` : base
if (location.hash !== want) history.replaceState(null, '', want)
setSel(v)
}, [])
return [sel, set]
}