diff --git a/src/web/pages/AskPage.tsx b/src/web/pages/AskPage.tsx index 6793c98..4c52a7b 100644 --- a/src/web/pages/AskPage.tsx +++ b/src/web/pages/AskPage.tsx @@ -9,6 +9,11 @@ import { triggerAuthReload, } from "../lib/api.js"; import { APP_API_BASE } from "../lib/paths.js"; +import { + ASK_SIDEBAR_MAX_WIDTH, + ASK_SIDEBAR_MIN_WIDTH, + useUiPrefsStore, +} from "../stores/ui-prefs-store.js"; /** * Global Ask chat. Left column: thread list. Right column: messages for @@ -33,6 +38,61 @@ export function AskPage() { // thread's origin so we can gate the composer. const [activeThreadOrigin, setActiveThreadOrigin] = useState<"web" | "telegram" | null>(null); + // Persisted, resizable thread sidebar. The drag handler attaches its + // listeners to `document` for the duration of the drag so a fast + // mouse movement that leaves the handle's hitbox keeps tracking. + const sidebarWidth = useUiPrefsStore((s) => s.askSidebarWidth); + const setSidebarWidth = useUiPrefsStore((s) => s.setAskSidebarWidth); + const onSidebarResizeStart = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + const startX = event.clientX; + const startWidth = sidebarWidth; + const onMove = (e: MouseEvent) => { + setSidebarWidth(startWidth + (e.clientX - startX)); + }; + const onUp = () => { + document.removeEventListener("mousemove", onMove); + document.removeEventListener("mouseup", onUp); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + }; + document.addEventListener("mousemove", onMove); + document.addEventListener("mouseup", onUp); + // Keep the resize cursor visible across the whole window while + // dragging, and stop the browser from selecting text under the + // pointer. + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + }, + [sidebarWidth, setSidebarWidth], + ); + const onSidebarResizeKey = useCallback( + (event: React.KeyboardEvent) => { + const STEP = 16; + let next = sidebarWidth; + switch (event.key) { + case "ArrowLeft": + next = sidebarWidth - STEP; + break; + case "ArrowRight": + next = sidebarWidth + STEP; + break; + case "Home": + next = ASK_SIDEBAR_MIN_WIDTH; + break; + case "End": + next = ASK_SIDEBAR_MAX_WIDTH; + break; + default: + return; + } + event.preventDefault(); + setSidebarWidth(next); + }, + [sidebarWidth, setSidebarWidth], + ); + const reloadThreads = useCallback(async () => { try { const res = await api.getAskThreads(); @@ -226,7 +286,10 @@ export function AskPage() { return (
{/* Thread sidebar */} -