From 58df321d8dd344baadc1021520bf99667aa0a68e Mon Sep 17 00:00:00 2001 From: fretuks Date: Tue, 9 Jun 2026 12:01:42 +0200 Subject: [PATCH] fix more stuff and improve styling immensely --- public/app/api.js | 15 +- public/app/chatDrawer.js | 54 +- public/app/dom.js | 2 + public/index.html | 12 +- public/market.html | 10 +- public/market.js | 13 +- public/script.js | 53 +- public/settings.html | 14 +- public/settings.js | 13 +- public/style.css | 1273 ++++++++++++++++++++++++++++++++ src/routes/personas.js | 13 +- src/services/chatService.js | 6 +- src/services/modelService.js | 56 +- src/services/personaService.js | 39 +- test/app.test.js | 91 ++- 15 files changed, 1594 insertions(+), 70 deletions(-) diff --git a/public/app/api.js b/public/app/api.js index 7446eae..7d96387 100644 --- a/public/app/api.js +++ b/public/app/api.js @@ -2,6 +2,7 @@ export async function request(url, data, method = "POST") { try { const res = await fetch(url, { method, + credentials: "same-origin", headers: {"Content-Type": "application/json"}, body: data ? JSON.stringify(data) : undefined }); @@ -19,12 +20,24 @@ export const del = (url) => request(url, null, "DELETE"); export async function stream(url, data, handlers = {}) { const response = await fetch(url, { method: "POST", + credentials: "same-origin", headers: {"Content-Type": "application/json"}, body: JSON.stringify(data || {}) }); if (!response.ok || !response.body) { - throw new Error("Streaming request failed"); + let error = "Streaming request failed"; + try { + const payload = await response.json(); + error = payload.error || error; + } catch { + // Keep the generic error if the server did not send JSON. + } + if (handlers.onError) { + handlers.onError({error}); + return; + } + throw new Error(error); } const reader = response.body.getReader(); diff --git a/public/app/chatDrawer.js b/public/app/chatDrawer.js index 1fceb8f..0cec46b 100644 --- a/public/app/chatDrawer.js +++ b/public/app/chatDrawer.js @@ -1,11 +1,41 @@ import {chatDrawer, chatDrawerButton, chatDrawerCloseBtn} from "./dom.js"; +let lastFocusedElement = null; + +const focusableSelector = [ + "a[href]", + "button:not([disabled])", + "textarea:not([disabled])", + "input:not([disabled])", + "select:not([disabled])", + "summary", + "[tabindex]:not([tabindex='-1'])" +].join(","); + +function getFocusableElements() { + if (!chatDrawer) return []; + return Array.from(chatDrawer.querySelectorAll(focusableSelector)) + .filter((element) => element instanceof HTMLElement && element.offsetParent !== null); +} + function syncChatDrawerState(isOpen) { if (!chatDrawer || !chatDrawerButton) return; - if (isOpen) document.body.classList.remove("sidebar-open"); + if (isOpen) { + lastFocusedElement = document.activeElement instanceof HTMLElement ? document.activeElement : null; + document.body.classList.remove("sidebar-open"); + } chatDrawer.classList.toggle("hidden", !isOpen); chatDrawerButton.setAttribute("aria-expanded", String(isOpen)); document.body.classList.toggle("chat-drawer-open", isOpen); + if (isOpen) { + window.requestAnimationFrame(() => { + const firstFocusable = getFocusableElements()[0]; + (chatDrawerCloseBtn || firstFocusable)?.focus(); + }); + } else if (lastFocusedElement) { + lastFocusedElement.focus(); + lastFocusedElement = null; + } } function isChatDrawerOpen() { @@ -34,7 +64,27 @@ function handleDocumentClick(event) { } function handleDocumentKeydown(event) { - if (event.key === "Escape") closeChatDrawer(); + if (!isChatDrawerOpen()) return; + if (event.key === "Escape") { + closeChatDrawer(); + return; + } + if (event.key !== "Tab") return; + + const focusableElements = getFocusableElements(); + if (!focusableElements.length) return; + + const first = focusableElements[0]; + const last = focusableElements[focusableElements.length - 1]; + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last.focus(); + return; + } + if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first.focus(); + } } if (chatDrawerButton && chatDrawer) { diff --git a/public/app/dom.js b/public/app/dom.js index 4bbfb6d..6df6d4a 100644 --- a/public/app/dom.js +++ b/public/app/dom.js @@ -26,6 +26,7 @@ const registerSubmit = $("registerSubmit"); const chatList = $("chatList"); const chatSearchInput = $("chatSearch"); const chatSidebar = $("chatSidebar"); +const chatSidebarToggle = $("chatSidebarToggle"); const chatDrawerCloseBtn = $("chatDrawerClose"); const chatDrawerButton = $("chatDrawerButton"); const chatDrawer = $("chatDrawer"); @@ -123,6 +124,7 @@ export { chatListLoading, chatListLoadingText, chatSidebar, + chatSidebarToggle, chatDrawerCloseBtn, chatSearchInput, chatUserPersonaPill, diff --git a/public/index.html b/public/index.html index 5d3113b..85e4515 100644 --- a/public/index.html +++ b/public/index.html @@ -68,9 +68,9 @@ -