some ui tweaks
This commit is contained in:
116
web/src/App.tsx
116
web/src/App.tsx
@@ -821,6 +821,7 @@ export default function App() {
|
|||||||
const [isRenamingChat, setIsRenamingChat] = useState(false);
|
const [isRenamingChat, setIsRenamingChat] = useState(false);
|
||||||
const [isChatSettingsOpen, setIsChatSettingsOpen] = useState(false);
|
const [isChatSettingsOpen, setIsChatSettingsOpen] = useState(false);
|
||||||
const [isSavingChatSettings, setIsSavingChatSettings] = useState(false);
|
const [isSavingChatSettings, setIsSavingChatSettings] = useState(false);
|
||||||
|
const [isTogglingChatSettingsStar, setIsTogglingChatSettingsStar] = useState(false);
|
||||||
const [chatSettingsError, setChatSettingsError] = useState<string | null>(null);
|
const [chatSettingsError, setChatSettingsError] = useState<string | null>(null);
|
||||||
const [draftChatTitle, setDraftChatTitle] = useState("");
|
const [draftChatTitle, setDraftChatTitle] = useState("");
|
||||||
const [chatSettingsTitleDraft, setChatSettingsTitleDraft] = useState("");
|
const [chatSettingsTitleDraft, setChatSettingsTitleDraft] = useState("");
|
||||||
@@ -957,6 +958,7 @@ export default function App() {
|
|||||||
setPendingAttachments([]);
|
setPendingAttachments([]);
|
||||||
setIsChatSettingsOpen(false);
|
setIsChatSettingsOpen(false);
|
||||||
setIsSavingChatSettings(false);
|
setIsSavingChatSettings(false);
|
||||||
|
setIsTogglingChatSettingsStar(false);
|
||||||
setChatSettingsError(null);
|
setChatSettingsError(null);
|
||||||
setDraftChatTitle("");
|
setDraftChatTitle("");
|
||||||
setChatSettingsTitleDraft("");
|
setChatSettingsTitleDraft("");
|
||||||
@@ -1353,11 +1355,6 @@ export default function App() {
|
|||||||
return chats.find((chat) => chat.id === selectedItem.id) ?? null;
|
return chats.find((chat) => chat.id === selectedItem.id) ?? null;
|
||||||
}, [chats, selectedItem]);
|
}, [chats, selectedItem]);
|
||||||
|
|
||||||
const selectedSidebarItem = useMemo(() => {
|
|
||||||
if (!selectedItem) return null;
|
|
||||||
return sidebarItems.find((item) => item.kind === selectedItem.kind && item.id === selectedItem.id) ?? null;
|
|
||||||
}, [selectedItem, sidebarItems]);
|
|
||||||
|
|
||||||
const selectedSearchSummary = useMemo(() => {
|
const selectedSearchSummary = useMemo(() => {
|
||||||
if (!selectedItem || selectedItem.kind !== "search") return null;
|
if (!selectedItem || selectedItem.kind !== "search") return null;
|
||||||
return searches.find((search) => search.id === selectedItem.id) ?? null;
|
return searches.find((search) => search.id === selectedItem.id) ?? null;
|
||||||
@@ -1737,6 +1734,29 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleChatSettingsStar = async () => {
|
||||||
|
if (draftKind !== null || selectedItem?.kind !== "chat" || isTogglingChatSettingsStar) return;
|
||||||
|
const current = sidebarItems.find((item) => item.kind === "chat" && item.id === selectedItem.id);
|
||||||
|
const nextStarred = !current?.starred;
|
||||||
|
setIsTogglingChatSettingsStar(true);
|
||||||
|
setChatSettingsError(null);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedChat = await updateChatStar(selectedItem.id, nextStarred);
|
||||||
|
applyChatSummary(updatedChat, false);
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
if (message.includes("bearer token")) {
|
||||||
|
handleAuthFailure(message);
|
||||||
|
} else {
|
||||||
|
setChatSettingsError(message);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsTogglingChatSettingsStar(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteFromContextMenu = async () => {
|
const handleDeleteFromContextMenu = async () => {
|
||||||
if (!contextMenu || isItemRunning(contextMenu.item)) return;
|
if (!contextMenu || isItemRunning(contextMenu.item)) return;
|
||||||
const target = contextMenu.item;
|
const target = contextMenu.item;
|
||||||
@@ -2817,6 +2837,10 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const chatSettingsChatId = draftKind === null && selectedItem?.kind === "chat" ? selectedItem.id : null;
|
||||||
|
const chatSettingsStarred = chatSettingsChatId
|
||||||
|
? sidebarItems.find((item) => item.kind === "chat" && item.id === chatSettingsChatId)?.starred ?? false
|
||||||
|
: false;
|
||||||
|
|
||||||
if (isCheckingSession) {
|
if (isCheckingSession) {
|
||||||
return (
|
return (
|
||||||
@@ -2995,8 +3019,8 @@ export default function App() {
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main className="glass-panel relative flex min-w-0 flex-1 flex-col overflow-hidden border-violet-300/18 md:rounded-2xl md:border">
|
<main className="glass-panel relative flex min-w-0 flex-1 flex-col overflow-hidden border-violet-300/18 md:rounded-2xl md:border">
|
||||||
<header className="flex flex-wrap items-center justify-between gap-3 border-b border-violet-300/12 bg-[linear-gradient(180deg,hsl(243_48%_10%_/_0.86),hsl(236_48%_6%_/_0.66))] px-4 py-3 md:px-7">
|
<header className="flex items-center justify-between gap-2 border-b border-violet-300/12 bg-[linear-gradient(180deg,hsl(243_48%_10%_/_0.86),hsl(236_48%_6%_/_0.66))] px-4 py-3 md:gap-3 md:px-7">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex min-w-0 items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -3010,46 +3034,20 @@ export default function App() {
|
|||||||
|
|
||||||
<div className="flex min-w-0 items-center gap-1.5">
|
<div className="flex min-w-0 items-center gap-1.5">
|
||||||
<h1 className="truncate text-sm font-semibold text-violet-50 md:text-base">{selectedTitle}</h1>
|
<h1 className="truncate text-sm font-semibold text-violet-50 md:text-base">{selectedTitle}</h1>
|
||||||
{draftKind === null && selectedItem ? (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-7 w-7 shrink-0 text-violet-100/72 hover:text-violet-50"
|
|
||||||
onClick={() => void handleToggleStar(selectedItem)}
|
|
||||||
title={selectedSidebarItem?.starred ? "Unstar" : "Star"}
|
|
||||||
aria-label={selectedSidebarItem?.starred ? "Unstar" : "Star"}
|
|
||||||
>
|
|
||||||
<Star className={cn("h-3.5 w-3.5", selectedSidebarItem?.starred ? "fill-amber-300 text-amber-300" : "")} />
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{draftKind === null && selectedItem?.kind === "chat" ? (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-7 w-7 shrink-0 text-violet-100/72 hover:text-violet-50"
|
|
||||||
onClick={() => openRenameChatDialog(selectedItem.id)}
|
|
||||||
title="Rename chat"
|
|
||||||
aria-label="Rename chat"
|
|
||||||
>
|
|
||||||
<Pencil className="h-3.5 w-3.5" />
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full max-w-xl items-center justify-end gap-2 md:w-auto">
|
<div className="flex shrink-0 items-center justify-end gap-2">
|
||||||
{!isSearchMode ? (
|
{!isSearchMode ? (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="h-10 max-w-full gap-2 rounded-lg px-3"
|
className="h-10 max-w-[44vw] gap-2 rounded-lg px-3 md:max-w-full"
|
||||||
onClick={openChatSettings}
|
onClick={openChatSettings}
|
||||||
disabled={isActiveSelectionSending}
|
disabled={isActiveSelectionSending}
|
||||||
aria-label="Open chat settings"
|
aria-label="Open chat settings"
|
||||||
>
|
>
|
||||||
<Settings2 className="h-4 w-4 shrink-0" />
|
<Settings2 className="h-4 w-4 shrink-0" />
|
||||||
<span className="shrink-0">Settings</span>
|
<span className="hidden shrink-0 sm:inline">Settings</span>
|
||||||
<span className="hidden min-w-0 max-w-[18rem] truncate text-xs font-medium text-violet-100/58 sm:inline">
|
<span className="hidden min-w-0 max-w-[18rem] truncate text-xs font-medium text-violet-100/58 sm:inline">
|
||||||
{getProviderLabel(provider)} · {model || "No model"}
|
{getProviderLabel(provider)} · {model || "No model"}
|
||||||
</span>
|
</span>
|
||||||
@@ -3260,20 +3258,40 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto pr-1">
|
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto pr-1">
|
||||||
<label className="block">
|
<div>
|
||||||
<span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Chat title</span>
|
<span className="mb-1.5 block text-xs font-semibold text-violet-100/72">Chat title</span>
|
||||||
<input
|
<div className="flex items-center gap-2">
|
||||||
value={chatSettingsTitleDraft}
|
<input
|
||||||
onInput={(event) => {
|
value={chatSettingsTitleDraft}
|
||||||
setChatSettingsTitleDraft(event.currentTarget.value);
|
onInput={(event) => {
|
||||||
if (chatSettingsError) setChatSettingsError(null);
|
setChatSettingsTitleDraft(event.currentTarget.value);
|
||||||
}}
|
if (chatSettingsError) setChatSettingsError(null);
|
||||||
maxLength={120}
|
}}
|
||||||
placeholder={draftKind === null && selectedItem?.kind === "chat" ? "Chat title" : "Optional title"}
|
maxLength={120}
|
||||||
className="h-11 w-full 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"
|
placeholder={draftKind === null && selectedItem?.kind === "chat" ? "Chat title" : "Optional title"}
|
||||||
disabled={isSavingChatSettings}
|
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"
|
||||||
/>
|
disabled={isSavingChatSettings}
|
||||||
</label>
|
/>
|
||||||
|
{chatSettingsChatId ? (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="icon"
|
||||||
|
variant="secondary"
|
||||||
|
className="h-11 w-11 shrink-0 rounded-lg"
|
||||||
|
onClick={() => void handleToggleChatSettingsStar()}
|
||||||
|
disabled={isSavingChatSettings || isTogglingChatSettingsStar}
|
||||||
|
aria-label={chatSettingsStarred ? "Unstar chat" : "Star chat"}
|
||||||
|
title={chatSettingsStarred ? "Unstar chat" : "Star chat"}
|
||||||
|
>
|
||||||
|
{isTogglingChatSettingsStar ? (
|
||||||
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Star className={cn("h-4 w-4", chatSettingsStarred ? "fill-amber-300 text-amber-300" : "")} />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-3 md:grid-cols-[minmax(9rem,0.7fr)_minmax(14rem,1fr)]">
|
<div className="grid gap-3 md:grid-cols-[minmax(9rem,0.7fr)_minmax(14rem,1fr)]">
|
||||||
<label className="block">
|
<label className="block">
|
||||||
|
|||||||
Reference in New Issue
Block a user