Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f79538f6b2 | ||
|
|
a3a218fef1 | ||
|
|
0c6c3b8c43 |
@@ -50,6 +50,7 @@
|
||||
|
||||
import { audioStore } from '$lib/audio.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { untrack } from 'svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils';
|
||||
import type { Voice } from '$lib/types';
|
||||
@@ -981,7 +982,7 @@
|
||||
let floatMoved = $state(false);
|
||||
|
||||
function onFloatPointerDown(e: PointerEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
floatDragging = true;
|
||||
floatMoved = false;
|
||||
floatDragStart = { mx: e.clientX, my: e.clientY, ox: audioStore.floatPos.x, oy: audioStore.floatPos.y };
|
||||
@@ -1000,19 +1001,24 @@
|
||||
};
|
||||
audioStore.floatPos = clampFloatPos(raw.x, raw.y);
|
||||
}
|
||||
function onFloatPointerUp() {
|
||||
function onFloatPointerUp(e: PointerEvent) {
|
||||
if (!floatDragging) return;
|
||||
if (floatDragging && !floatMoved) {
|
||||
// Tap: toggle play/pause
|
||||
audioStore.toggleRequest++;
|
||||
}
|
||||
floatDragging = false;
|
||||
try { (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// Clamp saved position to viewport on mount and on resize
|
||||
// Clamp saved position to viewport on mount and on resize.
|
||||
// Use untrack() when reading floatPos to avoid a reactive loop
|
||||
// (reading + writing the same state inside $effect would re-trigger forever).
|
||||
$effect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
const clamp = () => {
|
||||
audioStore.floatPos = clampFloatPos(audioStore.floatPos.x, audioStore.floatPos.y);
|
||||
const { x, y } = untrack(() => audioStore.floatPos);
|
||||
audioStore.floatPos = clampFloatPos(x, y);
|
||||
};
|
||||
clamp();
|
||||
window.addEventListener('resize', clamp);
|
||||
@@ -1525,7 +1531,7 @@
|
||||
onpointerdown={onFloatPointerDown}
|
||||
onpointermove={onFloatPointerMove}
|
||||
onpointerup={onFloatPointerUp}
|
||||
onpointercancel={onFloatPointerUp}
|
||||
onpointercancel={(e) => { floatDragging = false; try { (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); } catch { /* ignore */ } }}
|
||||
>
|
||||
<!-- Pulsing ring when playing -->
|
||||
{#if audioStore.isPlaying}
|
||||
|
||||
@@ -570,20 +570,18 @@
|
||||
</a>
|
||||
{/if}
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<!-- Universal search button (hidden on chapter/reader pages) -->
|
||||
{#if !/\/books\/[^/]+\/chapters\//.test(page.url.pathname)}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { searchOpen = true; userMenuOpen = false; langMenuOpen = false; themeMenuOpen = false; menuOpen = false; notificationsOpen = false; }}
|
||||
title="Search (/ or ⌘K)"
|
||||
aria-label="Search books"
|
||||
class="flex items-center justify-center w-8 h-8 rounded transition-colors text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2)"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
<!-- Universal search button -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { searchOpen = true; userMenuOpen = false; langMenuOpen = false; themeMenuOpen = false; menuOpen = false; notificationsOpen = false; }}
|
||||
title="Search (/ or ⌘K)"
|
||||
aria-label="Search books"
|
||||
class="flex items-center justify-center w-8 h-8 rounded transition-colors text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2)"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Notifications bell -->
|
||||
{#if data.user}
|
||||
@@ -1168,8 +1166,6 @@
|
||||
// Don't intercept when typing in an input/textarea
|
||||
const tag = (e.target as HTMLElement).tagName;
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target as HTMLElement).isContentEditable) return;
|
||||
// Don't open on chapter reader pages
|
||||
if (/\/books\/[^/]+\/chapters\//.test(page.url.pathname)) return;
|
||||
if (searchOpen) return;
|
||||
// `/` key or Cmd/Ctrl+K
|
||||
if (e.key === '/' || ((e.metaKey || e.ctrlKey) && e.key === 'k')) {
|
||||
|
||||
Reference in New Issue
Block a user