diff --git a/web/src/App.tsx b/web/src/App.tsx index 843e624..749c9ba 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useState } from "preact/hooks"; +import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks"; import type { TargetedTouchEvent } from "preact"; import { Check, @@ -654,9 +654,10 @@ type ModelComboboxProps = { value: string; onChange: (value: string) => void; disabled?: boolean; + className?: string; }; -function ModelCombobox({ options, value, onChange, disabled = false }: ModelComboboxProps) { +function ModelCombobox({ options, value, onChange, disabled = false, className }: ModelComboboxProps) { const [open, setOpen] = useState(false); const [draftValue, setDraftValue] = useState(value); const rootRef = useRef(null); @@ -710,7 +711,12 @@ function ModelCombobox({ options, value, onChange, disabled = false }: ModelComb return (
-
+
(null); const contextMenuRef = useRef(null); const renameChatInputRef = useRef(null); + const chatSettingsDialogRef = useRef(null); const fileInputRef = useRef(null); const dragDepthRef = useRef(0); const pendingAttachmentsRef = useRef([]); @@ -1930,6 +1937,7 @@ export default function App() { if (!isAuthenticated) return; const handleKeyDown = (event: KeyboardEvent) => { + if (chatSettingsDialogRef.current?.open) return; const hasPrimaryModifier = event.metaKey || event.ctrlKey; if (!hasPrimaryModifier || event.altKey) return; @@ -2331,16 +2339,16 @@ export default function App() { return () => window.clearTimeout(timer); }, [renameChatDialog]); - useEffect(() => { + useLayoutEffect(() => { if (!isChatSettingsOpen) return; - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key !== "Escape" || isSavingChatSettings) return; - event.preventDefault(); - setIsChatSettingsOpen(false); + const dialog = chatSettingsDialogRef.current; + const previouslyFocused = document.activeElement; + dialog?.showModal(); + return () => { + dialog?.close(); + if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus({ preventScroll: true }); }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [isChatSettingsOpen, isSavingChatSettings]); + }, [isChatSettingsOpen]); useEffect(() => { if (!isQuickQuestionOpen) return; @@ -3822,44 +3830,66 @@ export default function App() {
) : null} {isChatSettingsOpen ? ( -
{ + event.preventDefault(); + if (!isSavingChatSettings) setIsChatSettingsOpen(false); + }} onMouseDown={(event) => { if (event.target === event.currentTarget && !isSavingChatSettings) setIsChatSettingsOpen(false); }} >
void handleChatSettingsSubmit(event)} > -
+
+
-

+

Chat settings

-

{chatSettingsTitleDraft.trim() || "New chat"}

+

{chatSettingsTitleDraft.trim() || "New chat"}

+
-
+
- Chat title +
{ setChatSettingsTitleDraft(event.currentTarget.value); @@ -3867,7 +3897,7 @@ export default function App() { }} maxLength={120} placeholder={draftKind === null && selectedItem?.kind === "chat" ? "Chat title" : "Optional title"} - className="h-11 min-w-0 flex-1 rounded-lg border border-violet-300/22 bg-background/72 px-3 text-sm text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] placeholder:text-muted-foreground focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70" + className="h-11 min-w-0 flex-1 rounded-lg border border-violet-300/22 bg-background/70 px-3 text-base text-violet-50 outline-none shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)] placeholder:text-muted-foreground focus:border-violet-300/45 focus:ring-1 focus:ring-ring/70 md:text-sm" disabled={isSavingChatSettings} /> {chatSettingsChatId ? ( @@ -3895,7 +3925,7 @@ export default function App() { ); }) ) : ( -

+

No chat tools are available.

)} @@ -4003,8 +4035,8 @@ export default function App() {
- {chatSettingsError ?

{chatSettingsError}

: null} -
+ {chatSettingsError ?

{chatSettingsError}

: null} +
@@ -4014,7 +4046,7 @@ export default function App() {
-
+ ) : null} {renameChatDialog ? (