feat: bind/unbind by id, video streaming proxy, unmute default, library refresh, hamburger click-outside

- Core API: bind/unbind accept id (integer PK) when face_id is null
- Tauri proxy: stream video responses instead of buffering for seek bar
- VideoPlayer: remove muted attribute, unmute by default
- LibraryView: call invalidateFiles() before ensureFiles() on register/unregister
- PeopleView: close sort panel on click-outside, not just hamburger toggle
- bind_face: prefer face_id, fallback to id when face_id is null
This commit is contained in:
2026-06-18 22:24:03 +08:00
parent 97af4da331
commit 7855983dc1
19 changed files with 5321 additions and 793 deletions

View File

@@ -0,0 +1,44 @@
import { Directive } from 'vue'
const vObserve: Directive<HTMLElement> = {
mounted(el, binding) {
if (typeof binding.value !== 'function') return
const callback = binding.value
const observer = new IntersectionObserver(
(entries) => {
if (!entries.length) return
if (entries[0].isIntersecting) {
callback()
observer.disconnect()
}
},
{ rootMargin: '200px 0px' }
)
observer.observe(el)
;(el as any)._io = observer
requestAnimationFrame(() => {
const rect = el.getBoundingClientRect()
if (
rect.top < window.innerHeight + 200 &&
rect.bottom > -200 &&
rect.width > 0
) {
callback()
observer.disconnect()
;(el as any)._io = undefined
}
})
},
unmounted(el) {
if ((el as any)._io) {
;(el as any)._io.disconnect()
;(el as any)._io = undefined
}
},
}
export default vObserve