chore: ran linting

This commit is contained in:
Anish Sarkar 2026-03-07 04:46:48 +05:30
parent 37e1995546
commit f8b0e946ce
31 changed files with 768 additions and 754 deletions

View file

@ -1,14 +1,6 @@
"use client";
import {
Bell,
ExternalLink,
Info,
type LucideIcon,
Rocket,
Wrench,
Zap,
} from "lucide-react";
import { Bell, ExternalLink, Info, type LucideIcon, Rocket, Wrench, Zap } from "lucide-react";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@ -114,4 +106,3 @@ export function AnnouncementCard({ announcement }: { announcement: AnnouncementW
</Card>
);
}

View file

@ -15,4 +15,3 @@ export function AnnouncementsEmptyState() {
</div>
);
}

View file

@ -170,83 +170,83 @@ export const InlineMentionEditor = forwardRef<InlineMentionEditorRef, InlineMent
// Create a chip element for a document
const createChipElement = useCallback(
(doc: MentionedDocument): HTMLSpanElement => {
const chip = document.createElement("span");
chip.setAttribute(CHIP_DATA_ATTR, "true");
chip.setAttribute(CHIP_ID_ATTR, String(doc.id));
chip.setAttribute(CHIP_DOCTYPE_ATTR, doc.document_type ?? "UNKNOWN");
chip.contentEditable = "false";
chip.className =
"inline-flex items-center gap-1 mx-0.5 px-1 py-0.5 rounded bg-primary/10 text-xs font-bold text-primary/60 select-none cursor-default";
chip.style.userSelect = "none";
chip.style.verticalAlign = "baseline";
const chip = document.createElement("span");
chip.setAttribute(CHIP_DATA_ATTR, "true");
chip.setAttribute(CHIP_ID_ATTR, String(doc.id));
chip.setAttribute(CHIP_DOCTYPE_ATTR, doc.document_type ?? "UNKNOWN");
chip.contentEditable = "false";
chip.className =
"inline-flex items-center gap-1 mx-0.5 px-1 py-0.5 rounded bg-primary/10 text-xs font-bold text-primary/60 select-none cursor-default";
chip.style.userSelect = "none";
chip.style.verticalAlign = "baseline";
// Container that swaps between icon and remove button on hover
const iconContainer = document.createElement("span");
iconContainer.className = "shrink-0 flex items-center size-3 relative";
// Container that swaps between icon and remove button on hover
const iconContainer = document.createElement("span");
iconContainer.className = "shrink-0 flex items-center size-3 relative";
const iconSpan = document.createElement("span");
iconSpan.className = "flex items-center text-muted-foreground";
iconSpan.innerHTML = ReactDOMServer.renderToString(
getConnectorIcon(doc.document_type ?? "UNKNOWN", "h-3 w-3")
);
const iconSpan = document.createElement("span");
iconSpan.className = "flex items-center text-muted-foreground";
iconSpan.innerHTML = ReactDOMServer.renderToString(
getConnectorIcon(doc.document_type ?? "UNKNOWN", "h-3 w-3")
);
const removeBtn = document.createElement("button");
removeBtn.type = "button";
removeBtn.className =
"size-3 items-center justify-center rounded-full text-muted-foreground transition-colors";
removeBtn.style.display = "none";
removeBtn.innerHTML = ReactDOMServer.renderToString(
createElement(X, { className: "h-3 w-3", strokeWidth: 2.5 })
);
removeBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
chip.remove();
const docKey = `${doc.document_type ?? "UNKNOWN"}:${doc.id}`;
setMentionedDocs((prev) => {
const next = new Map(prev);
next.delete(docKey);
return next;
});
onDocumentRemove?.(doc.id, doc.document_type);
focusAtEnd();
};
const removeBtn = document.createElement("button");
removeBtn.type = "button";
removeBtn.className =
"size-3 items-center justify-center rounded-full text-muted-foreground transition-colors";
removeBtn.style.display = "none";
removeBtn.innerHTML = ReactDOMServer.renderToString(
createElement(X, { className: "h-3 w-3", strokeWidth: 2.5 })
);
removeBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
chip.remove();
const docKey = `${doc.document_type ?? "UNKNOWN"}:${doc.id}`;
setMentionedDocs((prev) => {
const next = new Map(prev);
next.delete(docKey);
return next;
});
onDocumentRemove?.(doc.id, doc.document_type);
focusAtEnd();
};
const titleSpan = document.createElement("span");
titleSpan.className = "max-w-[120px] truncate";
titleSpan.textContent = doc.title;
titleSpan.title = doc.title;
titleSpan.setAttribute("data-mention-title", "true");
const titleSpan = document.createElement("span");
titleSpan.className = "max-w-[120px] truncate";
titleSpan.textContent = doc.title;
titleSpan.title = doc.title;
titleSpan.setAttribute("data-mention-title", "true");
const statusSpan = document.createElement("span");
statusSpan.setAttribute(CHIP_STATUS_ATTR, "true");
statusSpan.className = "text-[10px] font-semibold opacity-80 hidden";
const statusSpan = document.createElement("span");
statusSpan.setAttribute(CHIP_STATUS_ATTR, "true");
statusSpan.className = "text-[10px] font-semibold opacity-80 hidden";
const isTouchDevice = window.matchMedia("(hover: none)").matches;
if (isTouchDevice) {
// Mobile: icon on left, title, X on right
chip.appendChild(iconSpan);
chip.appendChild(titleSpan);
chip.appendChild(statusSpan);
removeBtn.style.display = "flex";
removeBtn.className += " ml-0.5";
chip.appendChild(removeBtn);
} else {
// Desktop: icon/X swap on hover in the same slot
iconContainer.appendChild(iconSpan);
iconContainer.appendChild(removeBtn);
chip.addEventListener("mouseenter", () => {
iconSpan.style.display = "none";
const isTouchDevice = window.matchMedia("(hover: none)").matches;
if (isTouchDevice) {
// Mobile: icon on left, title, X on right
chip.appendChild(iconSpan);
chip.appendChild(titleSpan);
chip.appendChild(statusSpan);
removeBtn.style.display = "flex";
});
chip.addEventListener("mouseleave", () => {
iconSpan.style.display = "";
removeBtn.style.display = "none";
});
chip.appendChild(iconContainer);
chip.appendChild(titleSpan);
chip.appendChild(statusSpan);
}
removeBtn.className += " ml-0.5";
chip.appendChild(removeBtn);
} else {
// Desktop: icon/X swap on hover in the same slot
iconContainer.appendChild(iconSpan);
iconContainer.appendChild(removeBtn);
chip.addEventListener("mouseenter", () => {
iconSpan.style.display = "none";
removeBtn.style.display = "flex";
});
chip.addEventListener("mouseleave", () => {
iconSpan.style.display = "";
removeBtn.style.display = "none";
});
chip.appendChild(iconContainer);
chip.appendChild(titleSpan);
chip.appendChild(statusSpan);
}
return chip;
},

View file

@ -461,9 +461,7 @@ const Composer: FC = () => {
/>,
document.body
)}
<ComposerAction
isBlockedByOtherUser={isBlockedByOtherUser}
/>
<ComposerAction isBlockedByOtherUser={isBlockedByOtherUser} />
</div>
</ComposerPrimitive.Root>
);
@ -473,9 +471,7 @@ interface ComposerActionProps {
isBlockedByOtherUser?: boolean;
}
const ComposerAction: FC<ComposerActionProps> = ({
isBlockedByOtherUser = false,
}) => {
const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false }) => {
const mentionedDocuments = useAtomValue(mentionedDocumentsAtom);
const sidebarDocs = useAtomValue(sidebarSelectedDocumentsAtom);
const setDocumentsSidebarOpen = useSetAtom(documentsSidebarOpenAtom);
@ -502,10 +498,7 @@ const ComposerAction: FC<ComposerActionProps> = ({
return userConfigs?.some((c) => c.id === agentLlmId) ?? false;
}, [preferences, globalConfigs, userConfigs]);
const isSendDisabled =
isComposerEmpty ||
!hasModelConfigured ||
isBlockedByOtherUser;
const isSendDisabled = isComposerEmpty || !hasModelConfigured || isBlockedByOtherUser;
return (
<div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
@ -542,47 +535,47 @@ const ComposerAction: FC<ComposerActionProps> = ({
</button>
)}
<AssistantIf condition={({ thread }) => !thread.isRunning}>
<ComposerPrimitive.Send asChild disabled={isSendDisabled}>
<TooltipIconButton
tooltip={
isBlockedByOtherUser
? "Wait for AI to finish responding"
: !hasModelConfigured
? "Please select a model from the header to start chatting"
: isComposerEmpty
? "Enter a message to send"
: "Send message"
}
side="bottom"
type="submit"
variant="default"
size="icon"
className={cn(
"aui-composer-send size-8 rounded-full",
isSendDisabled && "cursor-not-allowed opacity-50"
)}
aria-label="Send message"
disabled={isSendDisabled}
>
<ArrowUpIcon className="aui-composer-send-icon size-4" />
</TooltipIconButton>
</ComposerPrimitive.Send>
</AssistantIf>
<AssistantIf condition={({ thread }) => !thread.isRunning}>
<ComposerPrimitive.Send asChild disabled={isSendDisabled}>
<TooltipIconButton
tooltip={
isBlockedByOtherUser
? "Wait for AI to finish responding"
: !hasModelConfigured
? "Please select a model from the header to start chatting"
: isComposerEmpty
? "Enter a message to send"
: "Send message"
}
side="bottom"
type="submit"
variant="default"
size="icon"
className={cn(
"aui-composer-send size-8 rounded-full",
isSendDisabled && "cursor-not-allowed opacity-50"
)}
aria-label="Send message"
disabled={isSendDisabled}
>
<ArrowUpIcon className="aui-composer-send-icon size-4" />
</TooltipIconButton>
</ComposerPrimitive.Send>
</AssistantIf>
<AssistantIf condition={({ thread }) => thread.isRunning}>
<ComposerPrimitive.Cancel asChild>
<Button
type="button"
variant="default"
size="icon"
className="aui-composer-cancel size-8 rounded-full"
aria-label="Stop generating"
>
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
</Button>
</ComposerPrimitive.Cancel>
</AssistantIf>
<AssistantIf condition={({ thread }) => thread.isRunning}>
<ComposerPrimitive.Cancel asChild>
<Button
type="button"
variant="default"
size="icon"
className="aui-composer-cancel size-8 rounded-full"
aria-label="Stop generating"
>
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
</Button>
</ComposerPrimitive.Cancel>
</AssistantIf>
</div>
</div>
);

View file

@ -2,12 +2,7 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import {
AlertTriangle,
Inbox,
Megaphone,
SquareLibrary,
} from "lucide-react";
import { AlertTriangle, Inbox, Megaphone, SquareLibrary } from "lucide-react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
@ -68,10 +63,7 @@ function formatInboxCount(count: number): string {
return `${thousands}k+`;
}
export function LayoutDataProvider({
searchSpaceId,
children,
}: LayoutDataProviderProps) {
export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProviderProps) {
const t = useTranslations("dashboard");
const tCommon = useTranslations("common");
const tSidebar = useTranslations("sidebar");
@ -186,7 +178,6 @@ export function LayoutDataProvider({
}
}, [statusInbox.inboxItems, statusInbox.loading, searchSpaceId, router]);
// Delete dialogs state
const [showDeleteChatDialog, setShowDeleteChatDialog] = useState(false);
const [chatToDelete, setChatToDelete] = useState<{ id: number; name: string } | null>(null);
@ -683,9 +674,7 @@ export function LayoutDataProvider({
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeletingChat}>
{tCommon("cancel")}
</AlertDialogCancel>
<AlertDialogCancel disabled={isDeletingChat}>{tCommon("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={(e) => {
e.preventDefault();

View file

@ -160,9 +160,9 @@ export function LayoutShell({
<SidebarProvider value={sidebarContextValue}>
<TooltipProvider delayDuration={0}>
<div className={cn("flex h-screen w-full flex-col bg-background", className)}>
<Header
mobileMenuTrigger={<MobileSidebarTrigger onClick={() => setMobileMenuOpen(true)} />}
/>
<Header
mobileMenuTrigger={<MobileSidebarTrigger onClick={() => setMobileMenuOpen(true)} />}
/>
<MobileSidebar
isOpen={mobileMenuOpen}

View file

@ -364,72 +364,72 @@ export function AllPrivateChatsSidebar({
isBusy && "opacity-50 pointer-events-none"
)}
>
{isMobile ? (
<button
type="button"
onClick={() => {
if (wasLongPress()) return;
handleThreadClick(thread.id);
}}
onTouchStart={() => {
pendingThreadIdRef.current = thread.id;
longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => handleThreadClick(thread.id)}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
<TooltipContent side="bottom" align="start">
<p>
{t("updated") || "Updated"}:{" "}
{format(new Date(thread.updatedAt), "MMM d, yyyy 'at' h:mm a")}
</p>
</TooltipContent>
</Tooltip>
)}
<DropdownMenu
open={openDropdownId === thread.id}
onOpenChange={(isOpen) => setOpenDropdownId(isOpen ? thread.id : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 shrink-0",
isMobile
? "opacity-0 pointer-events-none absolute"
: "md:opacity-0 md:group-hover:opacity-100 md:focus:opacity-100",
"transition-opacity"
)}
{isMobile ? (
<button
type="button"
onClick={() => {
if (wasLongPress()) return;
handleThreadClick(thread.id);
}}
onTouchStart={() => {
pendingThreadIdRef.current = thread.id;
longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
{isDeleting ? (
<Spinner size="xs" />
) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)}
<span className="sr-only">{t("more_options") || "More options"}</span>
</Button>
</DropdownMenuTrigger>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => handleThreadClick(thread.id)}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
<TooltipContent side="bottom" align="start">
<p>
{t("updated") || "Updated"}:{" "}
{format(new Date(thread.updatedAt), "MMM d, yyyy 'at' h:mm a")}
</p>
</TooltipContent>
</Tooltip>
)}
<DropdownMenu
open={openDropdownId === thread.id}
onOpenChange={(isOpen) => setOpenDropdownId(isOpen ? thread.id : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 shrink-0",
isMobile
? "opacity-0 pointer-events-none absolute"
: "md:opacity-0 md:group-hover:opacity-100 md:focus:opacity-100",
"transition-opacity"
)}
disabled={isBusy}
>
{isDeleting ? (
<Spinner size="xs" />
) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)}
<span className="sr-only">{t("more_options") || "More options"}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40 z-80">
{!thread.archived && (
<DropdownMenuItem
@ -456,9 +456,7 @@ export function AllPrivateChatsSidebar({
)}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => handleDeleteThread(thread.id)}
>
<DropdownMenuItem onClick={() => handleDeleteThread(thread.id)}>
<Trash2 className="mr-2 h-4 w-4" />
<span>{t("delete") || "Delete"}</span>
</DropdownMenuItem>

View file

@ -364,72 +364,72 @@ export function AllSharedChatsSidebar({
isBusy && "opacity-50 pointer-events-none"
)}
>
{isMobile ? (
<button
type="button"
onClick={() => {
if (wasLongPress()) return;
handleThreadClick(thread.id);
}}
onTouchStart={() => {
pendingThreadIdRef.current = thread.id;
longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => handleThreadClick(thread.id)}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
<TooltipContent side="bottom" align="start">
<p>
{t("updated") || "Updated"}:{" "}
{format(new Date(thread.updatedAt), "MMM d, yyyy 'at' h:mm a")}
</p>
</TooltipContent>
</Tooltip>
)}
<DropdownMenu
open={openDropdownId === thread.id}
onOpenChange={(isOpen) => setOpenDropdownId(isOpen ? thread.id : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 shrink-0",
isMobile
? "opacity-0 pointer-events-none absolute"
: "md:opacity-0 md:group-hover:opacity-100 md:focus:opacity-100",
"transition-opacity"
)}
{isMobile ? (
<button
type="button"
onClick={() => {
if (wasLongPress()) return;
handleThreadClick(thread.id);
}}
onTouchStart={() => {
pendingThreadIdRef.current = thread.id;
longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
{isDeleting ? (
<Spinner size="xs" />
) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)}
<span className="sr-only">{t("more_options") || "More options"}</span>
</Button>
</DropdownMenuTrigger>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => handleThreadClick(thread.id)}
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
<TooltipContent side="bottom" align="start">
<p>
{t("updated") || "Updated"}:{" "}
{format(new Date(thread.updatedAt), "MMM d, yyyy 'at' h:mm a")}
</p>
</TooltipContent>
</Tooltip>
)}
<DropdownMenu
open={openDropdownId === thread.id}
onOpenChange={(isOpen) => setOpenDropdownId(isOpen ? thread.id : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 shrink-0",
isMobile
? "opacity-0 pointer-events-none absolute"
: "md:opacity-0 md:group-hover:opacity-100 md:focus:opacity-100",
"transition-opacity"
)}
disabled={isBusy}
>
{isDeleting ? (
<Spinner size="xs" />
) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)}
<span className="sr-only">{t("more_options") || "More options"}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40 z-80">
{!thread.archived && (
<DropdownMenuItem
@ -456,9 +456,7 @@ export function AllSharedChatsSidebar({
)}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => handleDeleteThread(thread.id)}
>
<DropdownMenuItem onClick={() => handleDeleteThread(thread.id)}>
<Trash2 className="mr-2 h-4 w-4" />
<span>{t("delete") || "Delete"}</span>
</DropdownMenuItem>

View file

@ -72,4 +72,3 @@ export function AnnouncementsSidebar({
</SidebarSlideOutPanel>
);
}

View file

@ -73,12 +73,12 @@ export function ChatListItem({
</button>
{/* Actions dropdown - trigger hidden on mobile, long-press opens it instead */}
<div className={cn(
"absolute right-1 top-1/2 -translate-y-1/2 transition-opacity",
isMobile
? "opacity-0 pointer-events-none"
: "opacity-0 group-hover/item:opacity-100"
)}>
<div
className={cn(
"absolute right-1 top-1/2 -translate-y-1/2 transition-opacity",
isMobile ? "opacity-0 pointer-events-none" : "opacity-0 group-hover/item:opacity-100"
)}
>
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="h-6 w-6">
@ -125,7 +125,7 @@ export function ChatListItem({
e.stopPropagation();
onDelete();
}}
>
>
<Trash2 className="mr-2 h-4 w-4" />
<span>{t("delete")}</span>
</DropdownMenuItem>

View file

@ -41,10 +41,7 @@ export function DocumentsSidebar({ open, onOpenChange }: DocumentsSidebarProps)
const { mutateAsync: deleteDocumentMutation } = useAtomValue(deleteDocumentMutationAtom);
const [sidebarDocs, setSidebarDocs] = useAtom(sidebarSelectedDocumentsAtom);
const mentionedDocIds = useMemo(
() => new Set(sidebarDocs.map((d) => d.id)),
[sidebarDocs]
);
const mentionedDocIds = useMemo(() => new Set(sidebarDocs.map((d) => d.id)), [sidebarDocs]);
const handleToggleChatMention = useCallback(
(doc: { id: number; title: string; document_type: string }, isMentioned: boolean) => {
@ -53,7 +50,10 @@ export function DocumentsSidebar({ open, onOpenChange }: DocumentsSidebarProps)
} else {
setSidebarDocs((prev) => {
if (prev.some((d) => d.id === doc.id)) return prev;
return [...prev, { id: doc.id, title: doc.title, document_type: doc.document_type as DocumentTypeEnum }];
return [
...prev,
{ id: doc.id, title: doc.title, document_type: doc.document_type as DocumentTypeEnum },
];
});
}
},

View file

@ -638,58 +638,56 @@ export function InboxSidebar({
)}
</div>
</div>
{activeTab === "status" && statusSourceOptions.length > 0 && (
<div className="space-y-2">
<p className="text-xs text-muted-foreground/80 font-medium px-1">
{t("sources") || "Sources"}
</p>
<div className="space-y-1">
<button
type="button"
onClick={() => {
setSelectedSource(null);
setFilterDrawerOpen(false);
}}
className={cn(
"flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-sm transition-colors",
selectedSource === null
? "bg-primary/10 text-primary"
: "hover:bg-muted"
)}
>
<span className="flex items-center gap-2">
<LayoutGrid className="h-4 w-4" />
<span>{t("all_sources") || "All sources"}</span>
</span>
{selectedSource === null && <Check className="h-4 w-4" />}
</button>
{statusSourceOptions.map((source) => (
{activeTab === "status" && statusSourceOptions.length > 0 && (
<div className="space-y-2">
<p className="text-xs text-muted-foreground/80 font-medium px-1">
{t("sources") || "Sources"}
</p>
<div className="space-y-1">
<button
key={source.key}
type="button"
onClick={() => {
setSelectedSource(source.key);
setSelectedSource(null);
setFilterDrawerOpen(false);
}}
className={cn(
"flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-sm transition-colors",
selectedSource === source.key
selectedSource === null
? "bg-primary/10 text-primary"
: "hover:bg-muted"
)}
>
<span className="flex items-center gap-2">
{getConnectorIcon(source.type, "h-4 w-4")}
<span>{source.displayName}</span>
<LayoutGrid className="h-4 w-4" />
<span>{t("all_sources") || "All sources"}</span>
</span>
{selectedSource === source.key && (
<Check className="h-4 w-4" />
)}
{selectedSource === null && <Check className="h-4 w-4" />}
</button>
))}
{statusSourceOptions.map((source) => (
<button
key={source.key}
type="button"
onClick={() => {
setSelectedSource(source.key);
setFilterDrawerOpen(false);
}}
className={cn(
"flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-sm transition-colors",
selectedSource === source.key
? "bg-primary/10 text-primary"
: "hover:bg-muted"
)}
>
<span className="flex items-center gap-2">
{getConnectorIcon(source.type, "h-4 w-4")}
<span>{source.displayName}</span>
</span>
{selectedSource === source.key && <Check className="h-4 w-4" />}
</button>
))}
</div>
</div>
</div>
)}
)}
</div>
</DrawerContent>
</Drawer>
@ -712,7 +710,10 @@ export function InboxSidebar({
</Tooltip>
<DropdownMenuContent
align="end"
className={cn("z-80 select-none max-h-[60vh] overflow-hidden flex flex-col", activeTab === "status" ? "w-52" : "w-44")}
className={cn(
"z-80 select-none max-h-[60vh] overflow-hidden flex flex-col",
activeTab === "status" ? "w-52" : "w-44"
)}
>
<DropdownMenuLabel className="text-xs text-muted-foreground/80 font-normal">
{t("filter") || "Filter"}
@ -749,45 +750,45 @@ export function InboxSidebar({
{activeFilter === "errors" && <Check className="h-4 w-4" />}
</DropdownMenuItem>
)}
{activeTab === "status" && statusSourceOptions.length > 0 && (
<>
<DropdownMenuLabel className="text-xs text-muted-foreground/80 font-normal mt-2">
{t("sources") || "Sources"}
</DropdownMenuLabel>
<div
className="relative max-h-[30vh] overflow-y-auto overflow-x-hidden -mb-1"
onScroll={handleConnectorScroll}
style={{
maskImage: `linear-gradient(to bottom, ${connectorScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${connectorScrollPos === "bottom" ? "black" : "transparent"})`,
WebkitMaskImage: `linear-gradient(to bottom, ${connectorScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${connectorScrollPos === "bottom" ? "black" : "transparent"})`,
}}
>
<DropdownMenuItem
onClick={() => setSelectedSource(null)}
className="flex items-center justify-between"
{activeTab === "status" && statusSourceOptions.length > 0 && (
<>
<DropdownMenuLabel className="text-xs text-muted-foreground/80 font-normal mt-2">
{t("sources") || "Sources"}
</DropdownMenuLabel>
<div
className="relative max-h-[30vh] overflow-y-auto overflow-x-hidden -mb-1"
onScroll={handleConnectorScroll}
style={{
maskImage: `linear-gradient(to bottom, ${connectorScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${connectorScrollPos === "bottom" ? "black" : "transparent"})`,
WebkitMaskImage: `linear-gradient(to bottom, ${connectorScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${connectorScrollPos === "bottom" ? "black" : "transparent"})`,
}}
>
<span className="flex items-center gap-2">
<LayoutGrid className="h-4 w-4" />
<span>{t("all_sources") || "All sources"}</span>
</span>
{selectedSource === null && <Check className="h-4 w-4" />}
</DropdownMenuItem>
{statusSourceOptions.map((source) => (
<DropdownMenuItem
key={source.key}
onClick={() => setSelectedSource(source.key)}
onClick={() => setSelectedSource(null)}
className="flex items-center justify-between"
>
<span className="flex items-center gap-2">
{getConnectorIcon(source.type, "h-4 w-4")}
<span>{source.displayName}</span>
<LayoutGrid className="h-4 w-4" />
<span>{t("all_sources") || "All sources"}</span>
</span>
{selectedSource === source.key && <Check className="h-4 w-4" />}
{selectedSource === null && <Check className="h-4 w-4" />}
</DropdownMenuItem>
))}
</div>
</>
)}
{statusSourceOptions.map((source) => (
<DropdownMenuItem
key={source.key}
onClick={() => setSelectedSource(source.key)}
className="flex items-center justify-between"
>
<span className="flex items-center gap-2">
{getConnectorIcon(source.type, "h-4 w-4")}
<span>{source.displayName}</span>
</span>
{selectedSource === source.key && <Check className="h-4 w-4" />}
</DropdownMenuItem>
))}
</div>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
)}

View file

@ -215,7 +215,12 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
</div>
<div className="flex-1 text-left min-w-0">
<div className="flex items-center gap-1.5">
<span className={cn("text-sm font-medium", isSelected && "text-primary dark:text-white")}>
<span
className={cn(
"text-sm font-medium",
isSelected && "text-primary dark:text-white"
)}
>
{option.label}
</span>
</div>

View file

@ -143,11 +143,11 @@ export function ImageConfigDialog({
setIsSubmitting(true);
try {
if (mode === "create") {
const result = await createConfig({
name: formData.name,
provider: formData.provider as ImageGenProvider,
model_name: formData.model_name,
api_key: formData.api_key,
const result = await createConfig({
name: formData.name,
provider: formData.provider as ImageGenProvider,
model_name: formData.model_name,
api_key: formData.api_key,
api_base: formData.api_base || undefined,
api_version: formData.api_version || undefined,
description: formData.description || undefined,
@ -162,14 +162,14 @@ export function ImageConfigDialog({
toast.success("Image model created and assigned!");
onOpenChange(false);
} else if (!isGlobal && config) {
await updateConfig({
id: config.id,
data: {
name: formData.name,
description: formData.description || undefined,
provider: formData.provider as ImageGenProvider,
model_name: formData.model_name,
api_key: formData.api_key,
await updateConfig({
id: config.id,
data: {
name: formData.name,
description: formData.description || undefined,
provider: formData.provider as ImageGenProvider,
model_name: formData.model_name,
api_key: formData.api_key,
api_base: formData.api_base || undefined,
api_version: formData.api_version || undefined,
},
@ -248,7 +248,9 @@ export function ImageConfigDialog({
"flex flex-col overflow-hidden"
)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => { if (e.key === "Escape") onOpenChange(false); }}
onKeyDown={(e) => {
if (e.key === "Escape") onOpenChange(false);
}}
>
{/* Header */}
<div className="flex items-start justify-between px-6 pt-6 pb-4">
@ -268,7 +270,9 @@ export function ImageConfigDialog({
</div>
<p className="text-sm text-muted-foreground">{getSubtitle()}</p>
{config && !isAutoMode && mode !== "create" && (
<p className="text-xs font-mono text-muted-foreground/70">{config.model_name}</p>
<p className="text-xs font-mono text-muted-foreground/70">
{config.model_name}
</p>
)}
</div>
<Button
@ -404,7 +408,10 @@ export function ImageConfigDialog({
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0 bg-muted dark:border-neutral-700" align="start">
<PopoverContent
className="w-full p-0 bg-muted dark:border-neutral-700"
align="start"
>
<Command className="bg-transparent">
<CommandInput
placeholder="Search or type model..."
@ -505,7 +512,7 @@ export function ImageConfigDialog({
>
Cancel
</Button>
{(mode === "create" || (mode === "edit" && !isGlobal)) ? (
{mode === "create" || (mode === "edit" && !isGlobal) ? (
<Button
onClick={handleSubmit}
disabled={isSubmitting || !isFormValid}
@ -516,8 +523,10 @@ export function ImageConfigDialog({
<Spinner size="sm" />
{mode === "edit" ? "Saving" : "Creating"}
</>
) : mode === "edit" ? (
"Save Changes"
) : (
mode === "edit" ? "Save Changes" : "Create & Use"
"Create & Use"
)}
</Button>
) : isAutoMode ? (

View file

@ -200,7 +200,9 @@ export function ModelConfigDialog({
"flex flex-col overflow-hidden"
)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => { if (e.key === "Escape") onOpenChange(false); }}
onKeyDown={(e) => {
if (e.key === "Escape") onOpenChange(false);
}}
>
{/* Header */}
<div className="flex items-start justify-between px-6 pt-6 pb-4">
@ -225,7 +227,9 @@ export function ModelConfigDialog({
</div>
<p className="text-sm text-muted-foreground">{getSubtitle()}</p>
{config && !isAutoMode && mode !== "create" && (
<p className="text-xs font-mono text-muted-foreground/70">{config.model_name}</p>
<p className="text-xs font-mono text-muted-foreground/70">
{config.model_name}
</p>
)}
</div>
<Button
@ -330,7 +334,6 @@ export function ModelConfigDialog({
</div>
</div>
</div>
</div>
) : isGlobal && config ? (
<div className="space-y-6">
@ -401,7 +404,6 @@ export function ModelConfigDialog({
</>
)}
</div>
</div>
) : config ? (
<LLMConfigForm
@ -431,16 +433,16 @@ export function ModelConfigDialog({
{/* Fixed footer */}
<div className="shrink-0 px-6 py-4 flex items-center justify-end gap-3">
<Button
type="button"
variant="secondary"
onClick={() => onOpenChange(false)}
disabled={isSubmitting}
className="text-sm h-9"
>
Cancel
<Button
type="button"
variant="secondary"
onClick={() => onOpenChange(false)}
disabled={isSubmitting}
className="text-sm h-9"
>
Cancel
</Button>
{(mode === "create" || (!isGlobal && !isAutoMode && config)) ? (
{mode === "create" || (!isGlobal && !isAutoMode && config) ? (
<Button
type="submit"
form="model-config-form"
@ -452,8 +454,10 @@ export function ModelConfigDialog({
<Spinner size="sm" />
{mode === "edit" ? "Saving" : "Creating"}
</>
) : mode === "edit" ? (
"Save Changes"
) : (
mode === "edit" ? "Save Changes" : "Create & Use"
"Create & Use"
)}
</Button>
) : isAutoMode ? (

View file

@ -578,9 +578,7 @@ function RolesContent({
<DropdownMenuSeparator />
<AlertDialog>
<AlertDialogTrigger asChild>
<DropdownMenuItem
onSelect={(e) => e.preventDefault()}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
<Trash2 className="h-4 w-4 mr-2" />
Delete Role
</DropdownMenuItem>

View file

@ -173,14 +173,9 @@ export function LLMConfigForm({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel className="text-xs sm:text-sm">
Configuration Name
</FormLabel>
<FormLabel className="text-xs sm:text-sm">Configuration Name</FormLabel>
<FormControl>
<Input
placeholder="e.g., My GPT-4 Agent"
{...field}
/>
<Input placeholder="e.g., My GPT-4 Agent" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@ -280,17 +275,20 @@ export function LLMConfigForm({
variant="outline"
role="combobox"
aria-expanded={modelComboboxOpen}
className={cn(
"w-full justify-between font-normal bg-transparent",
!field.value && "text-muted-foreground"
)}
className={cn(
"w-full justify-between font-normal bg-transparent",
!field.value && "text-muted-foreground"
)}
>
{field.value || "Select or type model name"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0 bg-muted dark:border-neutral-700" align="start">
<PopoverContent
className="w-full p-0 bg-muted dark:border-neutral-700"
align="start"
>
<Command shouldFilter={false} className="bg-transparent">
<CommandInput
placeholder={selectedProvider?.example || "Type model name..."}
@ -362,9 +360,7 @@ export function LLMConfigForm({
name="api_key"
render={({ field }) => (
<FormItem>
<FormLabel className="text-xs sm:text-sm">
API Key
</FormLabel>
<FormLabel className="text-xs sm:text-sm">API Key</FormLabel>
<FormControl>
<Input
type="password"
@ -488,7 +484,7 @@ export function LLMConfigForm({
type="button"
className="flex w-full items-center justify-between py-2 text-xs sm:text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
>
<span>System Instructions</span>
<span>System Instructions</span>
<ChevronDown
className={cn(
"h-4 w-4 transition-transform duration-200",
@ -589,7 +585,8 @@ export function LLMConfigForm({
</>
) : (
<>
{submitLabel ?? (mode === "edit" ? "Update Configuration" : "Create Configuration")}
{submitLabel ??
(mode === "edit" ? "Update Configuration" : "Create Configuration")}
</>
)}
</Button>

View file

@ -88,7 +88,10 @@ function Calendar({
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
defaultClassNames.day
),
range_start: cn("rounded-l-md bg-accent dark:bg-neutral-700", defaultClassNames.range_start),
range_start: cn(
"rounded-l-md bg-accent dark:bg-neutral-700",
defaultClassNames.range_start
),
range_middle: cn("rounded-none", defaultClassNames.range_middle),
range_end: cn("rounded-r-md bg-accent dark:bg-neutral-700", defaultClassNames.range_end),
today: cn(

View file

@ -59,7 +59,9 @@ export function FixedToolbarButtons() {
</ToolbarButton>
<ToolbarButton
tooltip={<TooltipWithShortcut label="Redo" keys={shortcutKeys("Mod", "Shift", "Z")} />}
tooltip={
<TooltipWithShortcut label="Redo" keys={shortcutKeys("Mod", "Shift", "Z")} />
}
onClick={() => {
editor.redo();
editor.tf.focus();
@ -75,11 +77,17 @@ export function FixedToolbarButtons() {
</ToolbarGroup>
<ToolbarGroup>
<MarkToolbarButton nodeType={KEYS.bold} tooltip={<TooltipWithShortcut label="Bold" keys={shortcutKeys("Mod", "B")} />}>
<MarkToolbarButton
nodeType={KEYS.bold}
tooltip={<TooltipWithShortcut label="Bold" keys={shortcutKeys("Mod", "B")} />}
>
<BoldIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.italic} tooltip={<TooltipWithShortcut label="Italic" keys={shortcutKeys("Mod", "I")} />}>
<MarkToolbarButton
nodeType={KEYS.italic}
tooltip={<TooltipWithShortcut label="Italic" keys={shortcutKeys("Mod", "I")} />}
>
<ItalicIcon />
</MarkToolbarButton>
@ -92,18 +100,28 @@ export function FixedToolbarButtons() {
<MarkToolbarButton
nodeType={KEYS.strikethrough}
tooltip={<TooltipWithShortcut label="Strikethrough" keys={shortcutKeys("Mod", "Shift", "X")} />}
tooltip={
<TooltipWithShortcut
label="Strikethrough"
keys={shortcutKeys("Mod", "Shift", "X")}
/>
}
>
<StrikethroughIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.code} tooltip={<TooltipWithShortcut label="Code" keys={shortcutKeys("Mod", "E")} />}>
<MarkToolbarButton
nodeType={KEYS.code}
tooltip={<TooltipWithShortcut label="Code" keys={shortcutKeys("Mod", "E")} />}
>
<Code2Icon />
</MarkToolbarButton>
<MarkToolbarButton
nodeType={KEYS.highlight}
tooltip={<TooltipWithShortcut label="Highlight" keys={shortcutKeys("Mod", "Shift", "H")} />}
tooltip={
<TooltipWithShortcut label="Highlight" keys={shortcutKeys("Mod", "Shift", "H")} />
}
>
<HighlighterIcon />
</MarkToolbarButton>
@ -122,7 +140,13 @@ export function FixedToolbarButtons() {
{!readOnly && onSave && hasUnsavedChanges && (
<ToolbarGroup>
<ToolbarButton
tooltip={isSaving ? "Saving..." : <TooltipWithShortcut label="Save" keys={shortcutKeys("Mod", "S")} />}
tooltip={
isSaving ? (
"Saving..."
) : (
<TooltipWithShortcut label="Save" keys={shortcutKeys("Mod", "S")} />
)
}
onClick={onSave}
disabled={isSaving}
className="bg-primary text-primary-foreground hover:bg-primary/90"