mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
chore: ran linting
This commit is contained in:
parent
f1b3c88354
commit
d66295aedd
48 changed files with 372 additions and 443 deletions
|
|
@ -53,7 +53,10 @@ export default function Loading() {
|
||||||
|
|
||||||
{/* Table Rows */}
|
{/* Table Rows */}
|
||||||
{[...Array(6)].map((_, i) => (
|
{[...Array(6)].map((_, i) => (
|
||||||
<div key={i} className="border-b px-4 py-3 flex items-center gap-4 hover:bg-accent hover:text-accent-foreground">
|
<div
|
||||||
|
key={i}
|
||||||
|
className="border-b px-4 py-3 flex items-center gap-4 hover:bg-accent hover:text-accent-foreground"
|
||||||
|
>
|
||||||
<Skeleton className="h-4 w-4" />
|
<Skeleton className="h-4 w-4" />
|
||||||
<Skeleton className="h-6 w-12 rounded-full" />
|
<Skeleton className="h-6 w-12 rounded-full" />
|
||||||
<Skeleton className="h-6 w-16 rounded-full" />
|
<Skeleton className="h-6 w-16 rounded-full" />
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ import {
|
||||||
} from "@/components/assistant-ui/edit-message-dialog";
|
} from "@/components/assistant-ui/edit-message-dialog";
|
||||||
import { StepSeparatorDataUI } from "@/components/assistant-ui/step-separator";
|
import { StepSeparatorDataUI } from "@/components/assistant-ui/step-separator";
|
||||||
import { Thread } from "@/components/assistant-ui/thread";
|
import { Thread } from "@/components/assistant-ui/thread";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
import {
|
||||||
createTokenUsageStore,
|
createTokenUsageStore,
|
||||||
type TokenUsageData,
|
type TokenUsageData,
|
||||||
TokenUsageProvider,
|
TokenUsageProvider,
|
||||||
} from "@/components/assistant-ui/token-usage-context";
|
} from "@/components/assistant-ui/token-usage-context";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
type HitlDecision,
|
type HitlDecision,
|
||||||
PendingInterruptProvider,
|
PendingInterruptProvider,
|
||||||
|
|
@ -79,10 +79,7 @@ import {
|
||||||
setActivePodcastTaskId,
|
setActivePodcastTaskId,
|
||||||
} from "@/lib/chat/podcast-state";
|
} from "@/lib/chat/podcast-state";
|
||||||
import { createStreamFlushHelpers } from "@/lib/chat/stream-flush";
|
import { createStreamFlushHelpers } from "@/lib/chat/stream-flush";
|
||||||
import {
|
import { consumeSseEvents, processSharedStreamEvent } from "@/lib/chat/stream-pipeline";
|
||||||
consumeSseEvents,
|
|
||||||
processSharedStreamEvent,
|
|
||||||
} from "@/lib/chat/stream-pipeline";
|
|
||||||
import {
|
import {
|
||||||
applyTurnIdToAssistantMessageList,
|
applyTurnIdToAssistantMessageList,
|
||||||
mergeChatTurnIdIntoMessage,
|
mergeChatTurnIdIntoMessage,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { GeneralSettingsManager } from "@/components/settings/general-settings-manager";
|
import { GeneralSettingsManager } from "@/components/settings/general-settings-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <GeneralSettingsManager searchSpaceId={Number(search_space_id)} />;
|
return <GeneralSettingsManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { ImageModelManager } from "@/components/settings/image-model-manager";
|
import { ImageModelManager } from "@/components/settings/image-model-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <ImageModelManager searchSpaceId={Number(search_space_id)} />;
|
return <ImageModelManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { AgentModelManager } from "@/components/settings/agent-model-manager";
|
import { AgentModelManager } from "@/components/settings/agent-model-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <AgentModelManager searchSpaceId={Number(search_space_id)} />;
|
return <AgentModelManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { PromptConfigManager } from "@/components/settings/prompt-config-manager";
|
import { PromptConfigManager } from "@/components/settings/prompt-config-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <PromptConfigManager searchSpaceId={Number(search_space_id)} />;
|
return <PromptConfigManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { PublicChatSnapshotsManager } from "@/components/public-chat-snapshots/public-chat-snapshots-manager";
|
import { PublicChatSnapshotsManager } from "@/components/public-chat-snapshots/public-chat-snapshots-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <PublicChatSnapshotsManager searchSpaceId={Number(search_space_id)} />;
|
return <PublicChatSnapshotsManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { LLMRoleManager } from "@/components/settings/llm-role-manager";
|
import { LLMRoleManager } from "@/components/settings/llm-role-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <LLMRoleManager key={search_space_id} searchSpaceId={Number(search_space_id)} />;
|
return <LLMRoleManager key={search_space_id} searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { TeamMemoryManager } from "@/components/settings/team-memory-manager";
|
import { TeamMemoryManager } from "@/components/settings/team-memory-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <TeamMemoryManager searchSpaceId={Number(search_space_id)} />;
|
return <TeamMemoryManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { RolesManager } from "@/components/settings/roles-manager";
|
import { RolesManager } from "@/components/settings/roles-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <RolesManager searchSpaceId={Number(search_space_id)} />;
|
return <RolesManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { VisionModelManager } from "@/components/settings/vision-model-manager";
|
import { VisionModelManager } from "@/components/settings/vision-model-manager";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({ params }: { params: Promise<{ search_space_id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ search_space_id: string }>;
|
|
||||||
}) {
|
|
||||||
const { search_space_id } = await params;
|
const { search_space_id } = await params;
|
||||||
return <VisionModelManager searchSpaceId={Number(search_space_id)} />;
|
return <VisionModelManager searchSpaceId={Number(search_space_id)} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
|
|
@ -39,6 +38,7 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import {
|
import {
|
||||||
type AgentPermissionAction,
|
type AgentPermissionAction,
|
||||||
|
|
@ -215,8 +215,10 @@ export function AgentPermissionsContent() {
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<p>
|
<p>
|
||||||
Flip{" "}
|
Flip{" "}
|
||||||
<code className="rounded bg-popover px-1 py-0.5 text-[10px] text-popover-foreground">SURFSENSE_ENABLE_PERMISSION</code> on
|
<code className="rounded bg-popover px-1 py-0.5 text-[10px] text-popover-foreground">
|
||||||
the backend to manage allow/deny/ask rules from this panel.
|
SURFSENSE_ENABLE_PERMISSION
|
||||||
|
</code>{" "}
|
||||||
|
on the backend to manage allow/deny/ask rules from this panel.
|
||||||
</p>
|
</p>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
@ -425,9 +427,7 @@ export function AgentPermissionsContent() {
|
||||||
className={cn("h-8 gap-1 border px-2 text-[11px]", badge.className)}
|
className={cn("h-8 gap-1 border px-2 text-[11px]", badge.className)}
|
||||||
>
|
>
|
||||||
<SelectValue>
|
<SelectValue>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">{badge.label}</span>
|
||||||
{badge.label}
|
|
||||||
</span>
|
|
||||||
</SelectValue>
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,9 @@ export function MemoryContent() {
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
disabled={editing || !editQuery.trim()}
|
disabled={editing || !editQuery.trim()}
|
||||||
className={`h-11 w-11 shrink-0 rounded-full ${
|
className={`h-11 w-11 shrink-0 rounded-full ${
|
||||||
editing ? "" : "bg-muted-foreground/15 hover:bg-accent hover:text-accent-foreground"
|
editing
|
||||||
|
? ""
|
||||||
|
: "bg-muted-foreground/15 hover:bg-accent hover:text-accent-foreground"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,7 @@ interface UserSettingsLayoutShellProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserSettingsLayoutShell({
|
export function UserSettingsLayoutShell({ searchSpaceId, children }: UserSettingsLayoutShellProps) {
|
||||||
searchSpaceId,
|
|
||||||
children,
|
|
||||||
}: UserSettingsLayoutShellProps) {
|
|
||||||
const t = useTranslations("userSettings");
|
const t = useTranslations("userSettings");
|
||||||
const { isDesktop } = usePlatform();
|
const { isDesktop } = usePlatform();
|
||||||
const segment = useSelectedLayoutSegment();
|
const segment = useSelectedLayoutSegment();
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,7 @@ export default function DashboardError({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button type="button" onClick={reset}>
|
||||||
type="button"
|
|
||||||
onClick={reset}
|
|
||||||
>
|
|
||||||
Try again
|
Try again
|
||||||
</Button>
|
</Button>
|
||||||
<Link
|
<Link
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,7 @@ export default function ErrorPage({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button type="button" onClick={reset}>
|
||||||
type="button"
|
|
||||||
onClick={reset}
|
|
||||||
>
|
|
||||||
Try again
|
Try again
|
||||||
</Button>
|
</Button>
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,7 @@ import { actionLogDialogAtom } from "@/atoms/agent/action-log-dialog.atom";
|
||||||
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
|
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog";
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogTitle,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { agentActionsQueryKey, useAgentActionsQuery } from "@/hooks/use-agent-actions-query";
|
import { agentActionsQueryKey, useAgentActionsQuery } from "@/hooks/use-agent-actions-query";
|
||||||
|
|
|
||||||
|
|
@ -194,9 +194,7 @@ export function ActionLogItem({ action, threadId, onRevertSuccess }: ActionLogIt
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={handleCopyArguments}
|
onClick={handleCopyArguments}
|
||||||
className="size-6 rounded-lg p-0 text-muted-foreground hover:bg-popover hover:text-popover-foreground"
|
className="size-6 rounded-lg p-0 text-muted-foreground hover:bg-popover hover:text-popover-foreground"
|
||||||
aria-label={
|
aria-label={copiedSection === "arguments" ? "Arguments copied" : "Copy arguments"}
|
||||||
copiedSection === "arguments" ? "Arguments copied" : "Copy arguments"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{copiedSection === "arguments" ? (
|
{copiedSection === "arguments" ? (
|
||||||
<Check className="size-3" />
|
<Check className="size-3" />
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ import dynamic from "next/dynamic";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { commentsEnabledAtom, targetCommentIdAtom } from "@/atoms/chat/current-thread.atom";
|
import { commentsEnabledAtom, targetCommentIdAtom } from "@/atoms/chat/current-thread.atom";
|
||||||
import { tryGetHostname } from "@/lib/url";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
globalNewLLMConfigsAtom,
|
globalNewLLMConfigsAtom,
|
||||||
newLLMConfigsAtom,
|
newLLMConfigsAtom,
|
||||||
|
|
@ -60,6 +58,7 @@ import { useComments } from "@/hooks/use-comments";
|
||||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||||
import { useElectronAPI } from "@/hooks/use-platform";
|
import { useElectronAPI } from "@/hooks/use-platform";
|
||||||
import { getProviderIcon } from "@/lib/provider-icons";
|
import { getProviderIcon } from "@/lib/provider-icons";
|
||||||
|
import { tryGetHostname } from "@/lib/url";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
// Captured once at module load — survives client-side navigations that strip the query param.
|
// Captured once at module load — survives client-side navigations that strip the query param.
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ export const BaiduSearchApiConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSu
|
||||||
<AlertTitle>API Key Required</AlertTitle>
|
<AlertTitle>API Key Required</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<p>
|
<p>
|
||||||
You'll need a Baidu AppBuilder API key to use this connector. You can get one by
|
You'll need a Baidu AppBuilder API key to use this connector. You can get one by signing
|
||||||
signing up at{" "}
|
up at{" "}
|
||||||
<a
|
<a
|
||||||
href="https://qianfan.cloud.baidu.com/"
|
href="https://qianfan.cloud.baidu.com/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
||||||
|
|
@ -92,16 +92,19 @@ export const GoogleDriveConfig: FC<ConnectorConfigProps> = ({ connector, onConfi
|
||||||
const [selectedFiles, setSelectedFiles] = useState<SelectedItem[]>(existingFiles);
|
const [selectedFiles, setSelectedFiles] = useState<SelectedItem[]>(existingFiles);
|
||||||
const [indexingOptions, setIndexingOptions] = useState<IndexingOptions>(existingIndexingOptions);
|
const [indexingOptions, setIndexingOptions] = useState<IndexingOptions>(existingIndexingOptions);
|
||||||
|
|
||||||
const updateConfig = useCallback((folders: SelectedItem[], files: SelectedItem[], options: IndexingOptions) => {
|
const updateConfig = useCallback(
|
||||||
if (onConfigChange) {
|
(folders: SelectedItem[], files: SelectedItem[], options: IndexingOptions) => {
|
||||||
onConfigChange({
|
if (onConfigChange) {
|
||||||
...connector.config,
|
onConfigChange({
|
||||||
selected_folders: folders,
|
...connector.config,
|
||||||
selected_files: files,
|
selected_folders: folders,
|
||||||
indexing_options: options,
|
selected_files: files,
|
||||||
});
|
indexing_options: options,
|
||||||
}
|
});
|
||||||
}, [connector.config, onConfigChange]);
|
}
|
||||||
|
},
|
||||||
|
[connector.config, onConfigChange]
|
||||||
|
);
|
||||||
|
|
||||||
const handlePicked = useCallback(
|
const handlePicked = useCallback(
|
||||||
(result: PickerResult) => {
|
(result: PickerResult) => {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ export const TeamsConfig: FC<TeamsConfigProps> = () => {
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<p>
|
<p>
|
||||||
Your agent can search and read messages from Teams channels you have access to, and send
|
Your agent can search and read messages from Teams channels you have access to, and send
|
||||||
messages on your behalf. Make sure you're a member of the teams you want to
|
messages on your behalf. Make sure you're a member of the teams you want to interact
|
||||||
interact with.
|
with.
|
||||||
</p>
|
</p>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,11 @@ const NumericChunkCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
|
||||||
>
|
>
|
||||||
{chunkId}
|
{chunkId}
|
||||||
</Button>
|
</Button>
|
||||||
<Drawer open={mobilePreviewOpen} onOpenChange={setMobilePreviewOpen} shouldScaleBackground={false}>
|
<Drawer
|
||||||
|
open={mobilePreviewOpen}
|
||||||
|
onOpenChange={setMobilePreviewOpen}
|
||||||
|
shouldScaleBackground={false}
|
||||||
|
>
|
||||||
<DrawerContent
|
<DrawerContent
|
||||||
className="h-[85vh] max-h-[85vh] z-80 overflow-hidden"
|
className="h-[85vh] max-h-[85vh] z-80 overflow-hidden"
|
||||||
overlayClassName="z-80"
|
overlayClassName="z-80"
|
||||||
|
|
@ -156,16 +160,21 @@ const SurfsenseDocCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
|
||||||
>
|
>
|
||||||
<SurfsenseDocPreview chunkId={chunkId} />
|
<SurfsenseDocPreview chunkId={chunkId} />
|
||||||
</CitationHoverPopover>
|
</CitationHoverPopover>
|
||||||
<Drawer open={mobilePreviewOpen} onOpenChange={setMobilePreviewOpen} shouldScaleBackground={false}>
|
<Drawer
|
||||||
<DrawerContent
|
open={mobilePreviewOpen}
|
||||||
className="max-h-[85vh] z-80"
|
onOpenChange={setMobilePreviewOpen}
|
||||||
overlayClassName="z-80"
|
shouldScaleBackground={false}
|
||||||
>
|
>
|
||||||
|
<DrawerContent className="max-h-[85vh] z-80" overlayClassName="z-80">
|
||||||
<DrawerHandle />
|
<DrawerHandle />
|
||||||
<DrawerHeader className="pb-0">
|
<DrawerHeader className="pb-0">
|
||||||
<DrawerTitle>Surfsense documentation</DrawerTitle>
|
<DrawerTitle>Surfsense documentation</DrawerTitle>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
<SurfsenseDocPreviewContent chunkId={chunkId} query={docQuery} contentClassName="max-h-[60vh]" />
|
<SurfsenseDocPreviewContent
|
||||||
|
chunkId={chunkId}
|
||||||
|
query={docQuery}
|
||||||
|
contentClassName="max-h-[60vh]"
|
||||||
|
/>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</>
|
</>
|
||||||
|
|
@ -202,9 +211,7 @@ const SurfsenseDocPreviewContent: FC<{
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between gap-2 border-b px-3 py-2">
|
<div className="flex items-center justify-between gap-2 border-b px-3 py-2">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="truncate text-sm font-medium">
|
<p className="truncate text-sm font-medium">{data?.title ?? "Surfsense documentation"}</p>
|
||||||
{data?.title ?? "Surfsense documentation"}
|
|
||||||
</p>
|
|
||||||
<p className="text-[11px] text-muted-foreground">Chunk #{chunkId}</p>
|
<p className="text-[11px] text-muted-foreground">Chunk #{chunkId}</p>
|
||||||
</div>
|
</div>
|
||||||
{data?.public_url && (
|
{data?.public_url && (
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ import { MentionChip } from "@/components/assistant-ui/mention-chip";
|
||||||
import "katex/dist/katex.min.css";
|
import "katex/dist/katex.min.css";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { processChildrenWithCitations } from "@/components/citations/citation-renderer";
|
import { processChildrenWithCitations } from "@/components/citations/citation-renderer";
|
||||||
import { tryGetHostname } from "@/lib/url";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -36,6 +34,7 @@ import { useElectronAPI } from "@/hooks/use-platform";
|
||||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||||
import { getVirtualPathDisplay } from "@/lib/chat/virtual-path-display";
|
import { getVirtualPathDisplay } from "@/lib/chat/virtual-path-display";
|
||||||
import { type CitationUrlMap, preprocessCitationMarkdown } from "@/lib/citations/citation-parser";
|
import { type CitationUrlMap, preprocessCitationMarkdown } from "@/lib/citations/citation-parser";
|
||||||
|
import { tryGetHostname } from "@/lib/url";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function MarkdownCodeBlockLoading() {
|
function MarkdownCodeBlockLoading() {
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,7 @@ import {
|
||||||
import { PromptPicker, type PromptPickerRef } from "@/components/new-chat/prompt-picker";
|
import { PromptPicker, type PromptPickerRef } from "@/components/new-chat/prompt-picker";
|
||||||
import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from "@/components/ui/collapsible";
|
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
|
|
@ -383,7 +379,9 @@ const ClipboardChip: FC<{ text: string; onDismiss: () => void }> = ({ text, onDi
|
||||||
size="icon"
|
size="icon"
|
||||||
className="size-5 text-muted-foreground hover:bg-transparent hover:text-accent-foreground"
|
className="size-5 text-muted-foreground hover:bg-transparent hover:text-accent-foreground"
|
||||||
>
|
>
|
||||||
<ChevronDown className={cn("size-3.5 transition-transform", expanded && "rotate-180")} />
|
<ChevronDown
|
||||||
|
className={cn("size-3.5 transition-transform", expanded && "rotate-180")}
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -991,112 +989,110 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
<div className="min-h-0 flex-1 overflow-y-auto scrollbar-thin pb-6">
|
<div className="min-h-0 flex-1 overflow-y-auto scrollbar-thin pb-6">
|
||||||
{regularToolGroups.map((group) => (
|
{regularToolGroups.map((group) => (
|
||||||
<div key={group.label}>
|
<div key={group.label}>
|
||||||
<div className="px-4 pt-3 pb-1 text-xs text-muted-foreground/80 font-medium select-none">
|
<div className="px-4 pt-3 pb-1 text-xs text-muted-foreground/80 font-medium select-none">
|
||||||
{group.label}
|
{group.label}
|
||||||
</div>
|
|
||||||
{group.tools.map((tool) => {
|
|
||||||
const isDisabled = disabledToolsSet.has(tool.name);
|
|
||||||
const ToolIcon = getToolIcon(tool.name);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={tool.name}
|
|
||||||
className="flex w-full items-center gap-3 px-4 py-2 hover:bg-accent hover:text-accent-foreground transition-colors"
|
|
||||||
>
|
|
||||||
<ToolIcon className="size-4 shrink-0 text-muted-foreground" />
|
|
||||||
<span className="flex-1 min-w-0 text-sm font-medium truncate">
|
|
||||||
{formatToolName(tool.name)}
|
|
||||||
</span>
|
|
||||||
<Switch
|
|
||||||
checked={!isDisabled}
|
|
||||||
onCheckedChange={() => toggleTool(tool.name)}
|
|
||||||
className="shrink-0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
{group.tools.map((tool) => {
|
||||||
|
const isDisabled = disabledToolsSet.has(tool.name);
|
||||||
|
const ToolIcon = getToolIcon(tool.name);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={tool.name}
|
||||||
|
className="flex w-full items-center gap-3 px-4 py-2 hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<ToolIcon className="size-4 shrink-0 text-muted-foreground" />
|
||||||
|
<span className="flex-1 min-w-0 text-sm font-medium truncate">
|
||||||
|
{formatToolName(tool.name)}
|
||||||
|
</span>
|
||||||
|
<Switch
|
||||||
|
checked={!isDisabled}
|
||||||
|
onCheckedChange={() => toggleTool(tool.name)}
|
||||||
|
className="shrink-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
{connectorToolGroups.length > 0 && (
|
{connectorToolGroups.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div className="px-4 pt-3 pb-1 text-xs text-muted-foreground/80 font-medium select-none">
|
<div className="px-4 pt-3 pb-1 text-xs text-muted-foreground/80 font-medium select-none">
|
||||||
Connector Actions
|
Connector Actions
|
||||||
</div>
|
</div>
|
||||||
{connectorToolGroups.map((group) => {
|
{connectorToolGroups.map((group) => {
|
||||||
const iconKey = group.connectorIcon ?? "";
|
const iconKey = group.connectorIcon ?? "";
|
||||||
const iconInfo = CONNECTOR_TOOL_ICON_PATHS[iconKey];
|
const iconInfo = CONNECTOR_TOOL_ICON_PATHS[iconKey];
|
||||||
const toolNames = group.tools.map((t) => t.name);
|
const toolNames = group.tools.map((t) => t.name);
|
||||||
const allDisabled = toolNames.every((n) => disabledToolsSet.has(n));
|
const allDisabled = toolNames.every((n) => disabledToolsSet.has(n));
|
||||||
const isExpanded = expandedConnectorGroups.has(group.label);
|
const isExpanded = expandedConnectorGroups.has(group.label);
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
key={group.label}
|
key={group.label}
|
||||||
open={isExpanded}
|
open={isExpanded}
|
||||||
onOpenChange={(open) =>
|
onOpenChange={(open) => setConnectorGroupExpanded(group.label, open)}
|
||||||
setConnectorGroupExpanded(group.label, open)
|
>
|
||||||
}
|
<div className="flex w-full items-center gap-3 px-4 py-2 hover:bg-accent hover:text-accent-foreground transition-colors">
|
||||||
>
|
<CollapsibleTrigger asChild>
|
||||||
<div className="flex w-full items-center gap-3 px-4 py-2 hover:bg-accent hover:text-accent-foreground transition-colors">
|
<button
|
||||||
<CollapsibleTrigger asChild>
|
type="button"
|
||||||
<button
|
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
||||||
type="button"
|
>
|
||||||
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
{iconInfo ? (
|
||||||
|
<Image
|
||||||
|
src={iconInfo.src}
|
||||||
|
alt={iconInfo.alt}
|
||||||
|
width={18}
|
||||||
|
height={18}
|
||||||
|
className="size-[18px] shrink-0 select-none pointer-events-none"
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Wrench className="size-4 shrink-0 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<span className="min-w-0 flex-1 truncate text-sm font-medium">
|
||||||
|
{group.label}
|
||||||
|
</span>
|
||||||
|
{isExpanded ? (
|
||||||
|
<ChevronDown className="size-4 shrink-0 text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="size-4 shrink-0 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<Switch
|
||||||
|
checked={!allDisabled}
|
||||||
|
onCheckedChange={() => toggleToolGroup(toolNames)}
|
||||||
|
className="shrink-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<CollapsibleContent className="pb-1">
|
||||||
|
{group.tools.map((tool) => {
|
||||||
|
const isDisabled = disabledToolsSet.has(tool.name);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={tool.name}
|
||||||
|
className={cn(
|
||||||
|
"ml-8 flex items-center gap-3 px-4 py-1.5 rounded-md transition-colors",
|
||||||
|
"hover:bg-accent hover:text-accent-foreground",
|
||||||
|
!isDisabled && "text-primary"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{iconInfo ? (
|
<span className="min-w-0 flex-1 truncate text-sm">
|
||||||
<Image
|
{formatToolName(tool.name)}
|
||||||
src={iconInfo.src}
|
|
||||||
alt={iconInfo.alt}
|
|
||||||
width={18}
|
|
||||||
height={18}
|
|
||||||
className="size-[18px] shrink-0 select-none pointer-events-none"
|
|
||||||
draggable={false}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Wrench className="size-4 shrink-0 text-muted-foreground" />
|
|
||||||
)}
|
|
||||||
<span className="min-w-0 flex-1 truncate text-sm font-medium">
|
|
||||||
{group.label}
|
|
||||||
</span>
|
</span>
|
||||||
{isExpanded ? (
|
<Switch
|
||||||
<ChevronDown className="size-4 shrink-0 text-muted-foreground" />
|
checked={!isDisabled}
|
||||||
) : (
|
onCheckedChange={() => toggleTool(tool.name)}
|
||||||
<ChevronRight className="size-4 shrink-0 text-muted-foreground" />
|
className="shrink-0"
|
||||||
)}
|
/>
|
||||||
</button>
|
</div>
|
||||||
</CollapsibleTrigger>
|
);
|
||||||
<Switch
|
})}
|
||||||
checked={!allDisabled}
|
</CollapsibleContent>
|
||||||
onCheckedChange={() => toggleToolGroup(toolNames)}
|
</Collapsible>
|
||||||
className="shrink-0"
|
);
|
||||||
/>
|
})}
|
||||||
</div>
|
|
||||||
<CollapsibleContent className="pb-1">
|
|
||||||
{group.tools.map((tool) => {
|
|
||||||
const isDisabled = disabledToolsSet.has(tool.name);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={tool.name}
|
|
||||||
className={cn(
|
|
||||||
"ml-8 flex items-center gap-3 px-4 py-1.5 rounded-md transition-colors",
|
|
||||||
"hover:bg-accent hover:text-accent-foreground",
|
|
||||||
!isDisabled && "text-primary"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="min-w-0 flex-1 truncate text-sm">
|
|
||||||
{formatToolName(tool.name)}
|
|
||||||
</span>
|
|
||||||
<Switch
|
|
||||||
checked={!isDisabled}
|
|
||||||
onCheckedChange={() => toggleTool(tool.name)}
|
|
||||||
className="shrink-0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{otherToolGroup && (
|
{otherToolGroup && (
|
||||||
|
|
@ -1227,124 +1223,124 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
||||||
onScroll={() => setOpenConnectorSubmenu(null)}
|
onScroll={() => setOpenConnectorSubmenu(null)}
|
||||||
>
|
>
|
||||||
{regularToolGroups.map((group) => (
|
{regularToolGroups.map((group) => (
|
||||||
<div key={group.label}>
|
<div key={group.label}>
|
||||||
<div className="px-2 pt-1.5 pb-0.5 text-[10px] text-muted-foreground/80 font-normal select-none">
|
<div className="px-2 pt-1.5 pb-0.5 text-[10px] text-muted-foreground/80 font-normal select-none">
|
||||||
{group.label}
|
{group.label}
|
||||||
</div>
|
|
||||||
{group.tools.map((tool) => {
|
|
||||||
const isDisabled = disabledToolsSet.has(tool.name);
|
|
||||||
const ToolIcon = getToolIcon(tool.name);
|
|
||||||
return (
|
|
||||||
<DropdownMenuItem
|
|
||||||
key={tool.name}
|
|
||||||
onSelect={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleTool(tool.name);
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"mb-1 last:mb-0 transition-all",
|
|
||||||
"hover:bg-accent hover:text-accent-foreground",
|
|
||||||
!isDisabled && "text-primary"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ToolIcon className="h-4 w-4" />
|
|
||||||
<span className="flex-1 min-w-0 truncate">
|
|
||||||
{formatToolName(tool.name)}
|
|
||||||
</span>
|
|
||||||
<Switch
|
|
||||||
checked={!isDisabled}
|
|
||||||
tabIndex={-1}
|
|
||||||
className="pointer-events-none shrink-0 scale-[0.6]"
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
{group.tools.map((tool) => {
|
||||||
|
const isDisabled = disabledToolsSet.has(tool.name);
|
||||||
|
const ToolIcon = getToolIcon(tool.name);
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={tool.name}
|
||||||
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleTool(tool.name);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"mb-1 last:mb-0 transition-all",
|
||||||
|
"hover:bg-accent hover:text-accent-foreground",
|
||||||
|
!isDisabled && "text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ToolIcon className="h-4 w-4" />
|
||||||
|
<span className="flex-1 min-w-0 truncate">
|
||||||
|
{formatToolName(tool.name)}
|
||||||
|
</span>
|
||||||
|
<Switch
|
||||||
|
checked={!isDisabled}
|
||||||
|
tabIndex={-1}
|
||||||
|
className="pointer-events-none shrink-0 scale-[0.6]"
|
||||||
|
/>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
{connectorToolGroups.length > 0 && (
|
{connectorToolGroups.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div className="px-2 pt-1.5 pb-0.5 text-[10px] text-muted-foreground/80 font-normal select-none">
|
<div className="px-2 pt-1.5 pb-0.5 text-[10px] text-muted-foreground/80 font-normal select-none">
|
||||||
Connector Actions
|
Connector Actions
|
||||||
</div>
|
</div>
|
||||||
{connectorToolGroups.map((group) => {
|
{connectorToolGroups.map((group) => {
|
||||||
const iconKey = group.connectorIcon ?? "";
|
const iconKey = group.connectorIcon ?? "";
|
||||||
const iconInfo = CONNECTOR_TOOL_ICON_PATHS[iconKey];
|
const iconInfo = CONNECTOR_TOOL_ICON_PATHS[iconKey];
|
||||||
const toolNames = group.tools.map((t) => t.name);
|
const toolNames = group.tools.map((t) => t.name);
|
||||||
const allDisabled = toolNames.every((n) => disabledToolsSet.has(n));
|
const allDisabled = toolNames.every((n) => disabledToolsSet.has(n));
|
||||||
return (
|
return (
|
||||||
<DropdownMenuSub
|
<DropdownMenuSub
|
||||||
key={group.label}
|
key={group.label}
|
||||||
open={openConnectorSubmenu === group.label}
|
open={openConnectorSubmenu === group.label}
|
||||||
onOpenChange={(open) =>
|
onOpenChange={(open) =>
|
||||||
setOpenConnectorSubmenu(open ? group.label : null)
|
setOpenConnectorSubmenu(open ? group.label : null)
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
<DropdownMenuSubTrigger
|
||||||
|
className={cn(
|
||||||
|
"mb-1 last:mb-0 transition-all",
|
||||||
|
"hover:bg-accent hover:text-accent-foreground",
|
||||||
|
"gap-1 [&>svg:last-child]:ml-0",
|
||||||
|
!allDisabled && "text-primary"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<DropdownMenuSubTrigger
|
{iconInfo ? (
|
||||||
className={cn(
|
<Image
|
||||||
"mb-1 last:mb-0 transition-all",
|
src={iconInfo.src}
|
||||||
"hover:bg-accent hover:text-accent-foreground",
|
alt={iconInfo.alt}
|
||||||
"gap-1 [&>svg:last-child]:ml-0",
|
width={16}
|
||||||
!allDisabled && "text-primary"
|
height={16}
|
||||||
)}
|
className="h-4 w-4 shrink-0 select-none pointer-events-none"
|
||||||
>
|
draggable={false}
|
||||||
{iconInfo ? (
|
|
||||||
<Image
|
|
||||||
src={iconInfo.src}
|
|
||||||
alt={iconInfo.alt}
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
className="h-4 w-4 shrink-0 select-none pointer-events-none"
|
|
||||||
draggable={false}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Wrench className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
<span className="min-w-0 flex-1 truncate">{group.label}</span>
|
|
||||||
<Switch
|
|
||||||
checked={!allDisabled}
|
|
||||||
tabIndex={-1}
|
|
||||||
onPointerDown={(event) => event.stopPropagation()}
|
|
||||||
onClick={(event) => event.stopPropagation()}
|
|
||||||
onCheckedChange={() => toggleToolGroup(toolNames)}
|
|
||||||
className="shrink-0 scale-[0.6]"
|
|
||||||
/>
|
/>
|
||||||
</DropdownMenuSubTrigger>
|
) : (
|
||||||
<DropdownMenuPortal>
|
<Wrench className="h-4 w-4" />
|
||||||
<DropdownMenuSubContent
|
)}
|
||||||
collisionPadding={8}
|
<span className="min-w-0 flex-1 truncate">{group.label}</span>
|
||||||
className="w-60 max-h-56 overflow-y-auto overscroll-none"
|
<Switch
|
||||||
>
|
checked={!allDisabled}
|
||||||
{group.tools.map((tool) => {
|
tabIndex={-1}
|
||||||
const isDisabled = disabledToolsSet.has(tool.name);
|
onPointerDown={(event) => event.stopPropagation()}
|
||||||
return (
|
onClick={(event) => event.stopPropagation()}
|
||||||
<DropdownMenuItem
|
onCheckedChange={() => toggleToolGroup(toolNames)}
|
||||||
key={tool.name}
|
className="shrink-0 scale-[0.6]"
|
||||||
onSelect={(e) => {
|
/>
|
||||||
e.preventDefault();
|
</DropdownMenuSubTrigger>
|
||||||
toggleTool(tool.name);
|
<DropdownMenuPortal>
|
||||||
}}
|
<DropdownMenuSubContent
|
||||||
className={cn(
|
collisionPadding={8}
|
||||||
"mb-1 last:mb-0 transition-all",
|
className="w-60 max-h-56 overflow-y-auto overscroll-none"
|
||||||
"hover:bg-accent hover:text-accent-foreground",
|
>
|
||||||
!isDisabled && "text-primary"
|
{group.tools.map((tool) => {
|
||||||
)}
|
const isDisabled = disabledToolsSet.has(tool.name);
|
||||||
>
|
return (
|
||||||
<span className="min-w-0 flex-1 truncate">
|
<DropdownMenuItem
|
||||||
{formatToolName(tool.name)}
|
key={tool.name}
|
||||||
</span>
|
onSelect={(e) => {
|
||||||
<Switch
|
e.preventDefault();
|
||||||
checked={!isDisabled}
|
toggleTool(tool.name);
|
||||||
tabIndex={-1}
|
}}
|
||||||
className="pointer-events-none shrink-0 scale-[0.6]"
|
className={cn(
|
||||||
/>
|
"mb-1 last:mb-0 transition-all",
|
||||||
</DropdownMenuItem>
|
"hover:bg-accent hover:text-accent-foreground",
|
||||||
);
|
!isDisabled && "text-primary"
|
||||||
})}
|
)}
|
||||||
</DropdownMenuSubContent>
|
>
|
||||||
</DropdownMenuPortal>
|
<span className="min-w-0 flex-1 truncate">
|
||||||
</DropdownMenuSub>
|
{formatToolName(tool.name)}
|
||||||
);
|
</span>
|
||||||
})}
|
<Switch
|
||||||
|
checked={!isDisabled}
|
||||||
|
tabIndex={-1}
|
||||||
|
className="pointer-events-none shrink-0 scale-[0.6]"
|
||||||
|
/>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{otherToolGroup && (
|
{otherToolGroup && (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import { CommentComposer } from "../comment-composer/comment-composer";
|
import { CommentComposer } from "../comment-composer/comment-composer";
|
||||||
import { CommentThread } from "../comment-thread/comment-thread";
|
import { CommentThread } from "../comment-thread/comment-thread";
|
||||||
import type { CommentPanelProps } from "./types";
|
import type { CommentPanelProps } from "./types";
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ export function MemberMentionItem({
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-auto w-full justify-start gap-3 rounded-none px-3 py-2 text-left transition-colors",
|
"h-auto w-full justify-start gap-3 rounded-none px-3 py-2 text-left transition-colors",
|
||||||
isHighlighted ? "bg-primary/15 text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
|
isHighlighted
|
||||||
|
? "bg-primary/15 text-accent-foreground"
|
||||||
|
: "hover:bg-accent hover:text-accent-foreground"
|
||||||
)}
|
)}
|
||||||
onClick={() => onSelect(member)}
|
onClick={() => onSelect(member)}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,8 @@ export function AnonymousChat({ model }: AnonymousChatProps) {
|
||||||
remaining: 0,
|
remaining: 0,
|
||||||
status: "exceeded",
|
status: "exceeded",
|
||||||
warning_threshold: quota?.warning_threshold ?? 800000,
|
warning_threshold: quota?.warning_threshold ?? 800000,
|
||||||
captcha_required: errorData.detail?.captcha_required ?? quota?.captcha_required ?? false,
|
captcha_required:
|
||||||
|
errorData.detail?.captcha_required ?? quota?.captcha_required ?? false,
|
||||||
});
|
});
|
||||||
setMessages((prev) => prev.filter((m) => m.id !== assistantId));
|
setMessages((prev) => prev.filter((m) => m.id !== assistantId));
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,10 @@ export default function InferenceParamsEditor({ params, setParams }: InferencePa
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-200 bg-black dark:bg-black">
|
<tbody className="divide-y divide-gray-200 bg-black dark:bg-black">
|
||||||
{Object.entries(params).map(([key, val]) => (
|
{Object.entries(params).map(([key, val]) => (
|
||||||
<tr key={key} className="hover:bg-accent hover:text-accent-foreground transition-colors">
|
<tr
|
||||||
|
key={key}
|
||||||
|
className="hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||||
|
>
|
||||||
<td className="px-4 py-3 font-medium text-gray-900 dark:text-white">{key}</td>
|
<td className="px-4 py-3 font-medium text-gray-900 dark:text-white">{key}</td>
|
||||||
<td className="px-4 py-3 text-gray-700 dark:text-gray-300">{val.toString()}</td>
|
<td className="px-4 py-3 text-gray-700 dark:text-gray-300">{val.toString()}</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ export function useSidebarResize(defaultWidth = SIDEBAR_MIN_WIDTH): UseSidebarRe
|
||||||
widthRef.current = parsed;
|
widthRef.current = parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {}
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const persistWidth = useCallback((width: number) => {
|
const persistWidth = useCallback((width: number) => {
|
||||||
|
|
@ -71,32 +70,27 @@ export function useSidebarResize(defaultWidth = SIDEBAR_MIN_WIDTH): UseSidebarRe
|
||||||
if (target.hasPointerCapture(pointerId)) {
|
if (target.hasPointerCapture(pointerId)) {
|
||||||
target.releasePointerCapture(pointerId);
|
target.releasePointerCapture(pointerId);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
captureTargetRef.current = null;
|
captureTargetRef.current = null;
|
||||||
pointerIdRef.current = null;
|
pointerIdRef.current = null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handlePointerDown = useCallback(
|
const handlePointerDown = useCallback((e: React.PointerEvent<HTMLElement>) => {
|
||||||
(e: React.PointerEvent<HTMLElement>) => {
|
if (e.pointerType === "mouse" && e.button !== 0) return;
|
||||||
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const target = e.currentTarget;
|
const target = e.currentTarget;
|
||||||
try {
|
try {
|
||||||
target.setPointerCapture(e.pointerId);
|
target.setPointerCapture(e.pointerId);
|
||||||
} catch {
|
} catch {}
|
||||||
}
|
captureTargetRef.current = target;
|
||||||
captureTargetRef.current = target;
|
pointerIdRef.current = e.pointerId;
|
||||||
pointerIdRef.current = e.pointerId;
|
startXRef.current = e.clientX;
|
||||||
startXRef.current = e.clientX;
|
startWidthRef.current = widthRef.current;
|
||||||
startWidthRef.current = widthRef.current;
|
setIsDragging(true);
|
||||||
setIsDragging(true);
|
setGlobalDragCursor(true);
|
||||||
setGlobalDragCursor(true);
|
}, []);
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isDragging) return;
|
if (!isDragging) return;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { NavItem, SearchSpace } from "../../types/layout.types";
|
import type { NavItem, SearchSpace, User } from "../../types/layout.types";
|
||||||
import type { User } from "../../types/layout.types";
|
|
||||||
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
|
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
|
||||||
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useAtom } from "jotai";
|
||||||
import { Folder, FolderPlus, Search, X } from "lucide-react";
|
import { Folder, FolderPlus, Search, X } from "lucide-react";
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
import { localExpandedFolderKeysAtom } from "@/atoms/documents/folder.atoms";
|
import { localExpandedFolderKeysAtom } from "@/atoms/documents/folder.atoms";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -11,7 +12,6 @@ import {
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
|
|
||||||
|
|
@ -1047,7 +1047,9 @@ export function InboxSidebarContent({
|
||||||
<History className="mx-auto mb-2.5 h-10 w-10 text-muted-foreground" />
|
<History className="mx-auto mb-2.5 h-10 w-10 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground">{getEmptyStateMessage().title}</p>
|
<p className="text-xs text-muted-foreground">{getEmptyStateMessage().title}</p>
|
||||||
<p className="mt-1 text-[11px] text-muted-foreground/70">{getEmptyStateMessage().hint}</p>
|
<p className="mt-1 text-[11px] text-muted-foreground/70">
|
||||||
|
{getEmptyStateMessage().hint}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,10 @@ export function MobileSidebar({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={isOpen} onOpenChange={onOpenChange}>
|
<Sheet open={isOpen} onOpenChange={onOpenChange}>
|
||||||
<SheetContent side="left" className="w-[340px] p-0 flex flex-row gap-0 bg-panel [&>button]:hidden">
|
<SheetContent
|
||||||
|
side="left"
|
||||||
|
className="w-[340px] p-0 flex flex-row gap-0 bg-panel [&>button]:hidden"
|
||||||
|
>
|
||||||
<SheetTitle className="sr-only">Navigation</SheetTitle>
|
<SheetTitle className="sr-only">Navigation</SheetTitle>
|
||||||
|
|
||||||
{/* Vertical Search Spaces Rail - left side */}
|
{/* Vertical Search Spaces Rail - left side */}
|
||||||
|
|
|
||||||
|
|
@ -64,17 +64,13 @@ export function SidebarButton({
|
||||||
isCollapsed ? "shrink-0" : "flex-1"
|
isCollapsed ? "shrink-0" : "flex-1"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="flex h-3.5 w-3.5 shrink-0 items-center justify-center">
|
<span className="flex h-3.5 w-3.5 shrink-0 items-center justify-center">{iconNode}</span>
|
||||||
{iconNode}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-w-0 overflow-hidden whitespace-nowrap text-left",
|
"min-w-0 overflow-hidden whitespace-nowrap text-left",
|
||||||
"transition-[max-width,opacity,margin-left] duration-200 ease-out",
|
"transition-[max-width,opacity,margin-left] duration-200 ease-out",
|
||||||
isCollapsed
|
isCollapsed ? "max-w-0 opacity-0 ml-0" : "max-w-[260px] flex-1 opacity-100 ml-2"
|
||||||
? "max-w-0 opacity-0 ml-0"
|
|
||||||
: "max-w-[260px] flex-1 opacity-100 ml-2"
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="block truncate">{label}</span>
|
<span className="block truncate">{label}</span>
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,7 @@ export function SidebarSection({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CollapsibleContent className={cn("overflow-hidden flex-1 flex flex-col min-h-0")}>
|
<CollapsibleContent className={cn("overflow-hidden flex-1 flex flex-col min-h-0")}>
|
||||||
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden")}>
|
<div className={cn("px-2 flex-1 flex flex-col min-h-0 overflow-hidden")}>{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -29,7 +30,6 @@ import {
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { useLocaleContext } from "@/contexts/LocaleContext";
|
import { useLocaleContext } from "@/contexts/LocaleContext";
|
||||||
|
|
|
||||||
|
|
@ -190,10 +190,7 @@ export function TabBar({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div
|
<div data-tab-id={tab.id} className="group relative h-full w-[180px] shrink-0">
|
||||||
data-tab-id={tab.id}
|
|
||||||
className="group relative h-full w-[180px] shrink-0"
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ export function PublicChatSnapshotRow({
|
||||||
{getInitials(member.name)}
|
{getInitials(member.name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span className="text-[11px] text-muted-foreground/60 truncate">
|
<span className="text-[11px] text-muted-foreground/60 truncate">
|
||||||
{member.name}
|
{member.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -317,44 +317,44 @@ export function ReportPanelContent({
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const exportButton = !isEditing && (
|
const exportButton =
|
||||||
isResume ? (
|
!isEditing &&
|
||||||
<Button
|
(isResume ? (
|
||||||
variant="ghost"
|
<Button
|
||||||
size="icon"
|
variant="ghost"
|
||||||
className="size-6"
|
size="icon"
|
||||||
onClick={() => handleExport("pdf")}
|
className="size-6"
|
||||||
disabled={isLoading || !reportContent?.content || exporting !== null}
|
onClick={() => handleExport("pdf")}
|
||||||
>
|
disabled={isLoading || !reportContent?.content || exporting !== null}
|
||||||
{exporting === "pdf" ? <Spinner size="xs" /> : <Download className="size-3.5" />}
|
>
|
||||||
<span className="sr-only">Download report</span>
|
{exporting === "pdf" ? <Spinner size="xs" /> : <Download className="size-3.5" />}
|
||||||
</Button>
|
<span className="sr-only">Download report</span>
|
||||||
) : (
|
</Button>
|
||||||
<DropdownMenu modal={insideDrawer ? false : undefined}>
|
) : (
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenu modal={insideDrawer ? false : undefined}>
|
||||||
<Button
|
<DropdownMenuTrigger asChild>
|
||||||
variant="ghost"
|
<Button
|
||||||
size="icon"
|
variant="ghost"
|
||||||
className="size-6"
|
size="icon"
|
||||||
disabled={isLoading || !reportContent?.content}
|
className="size-6"
|
||||||
>
|
disabled={isLoading || !reportContent?.content}
|
||||||
<Download className="size-3.5" />
|
|
||||||
<span className="sr-only">Export report</span>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
align="end"
|
|
||||||
className={`min-w-[200px] select-none${insideDrawer ? " z-[100]" : ""}`}
|
|
||||||
>
|
>
|
||||||
<ExportDropdownItems
|
<Download className="size-3.5" />
|
||||||
onExport={handleExport}
|
<span className="sr-only">Export report</span>
|
||||||
exporting={exporting}
|
</Button>
|
||||||
showAllFormats={!shareToken}
|
</DropdownMenuTrigger>
|
||||||
/>
|
<DropdownMenuContent
|
||||||
</DropdownMenuContent>
|
align="end"
|
||||||
</DropdownMenu>
|
className={`min-w-[200px] select-none${insideDrawer ? " z-[100]" : ""}`}
|
||||||
)
|
>
|
||||||
);
|
<ExportDropdownItems
|
||||||
|
onExport={handleExport}
|
||||||
|
exporting={exporting}
|
||||||
|
showAllFormats={!shareToken}
|
||||||
|
/>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
));
|
||||||
|
|
||||||
const versionSwitcher = !isEditing && versions.length > 1 && (
|
const versionSwitcher = !isEditing && versions.length > 1 && (
|
||||||
<DropdownMenu modal={insideDrawer ? false : undefined}>
|
<DropdownMenu modal={insideDrawer ? false : undefined}>
|
||||||
|
|
@ -446,9 +446,7 @@ export function ReportPanelContent({
|
||||||
<>
|
<>
|
||||||
{/* Header — matches the Documents panel header pattern */}
|
{/* Header — matches the Documents panel header pattern */}
|
||||||
<div className="shrink-0 flex h-12 items-center justify-between px-3 border-b">
|
<div className="shrink-0 flex h-12 items-center justify-between px-3 border-b">
|
||||||
<h2 className="select-none text-lg font-semibold">
|
<h2 className="select-none text-lg font-semibold">{isResume ? "Resume" : "Report"}</h2>
|
||||||
{isResume ? "Resume" : "Report"}
|
|
||||||
</h2>
|
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
|
||||||
|
|
@ -462,9 +462,7 @@ function RolesContent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={role.id} className="rounded-lg border border-border/60 overflow-hidden">
|
<div key={role.id} className="rounded-lg border border-border/60 overflow-hidden">
|
||||||
<div
|
<div className="group/role-header flex items-center gap-4 p-4 transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground">
|
||||||
className="group/role-header flex items-center gap-4 p-4 transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground"
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
@ -697,9 +695,7 @@ function PermissionsEditor({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={category} className="rounded-lg border border-border/60 overflow-hidden">
|
<div key={category} className="rounded-lg border border-border/60 overflow-hidden">
|
||||||
<div
|
<div className="group/category-header flex items-center justify-between px-3 py-2.5 transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground">
|
||||||
className="group/category-header flex items-center justify-between px-3 py-2.5 transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground"
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
@ -751,7 +747,9 @@ function PermissionsEditor({
|
||||||
key={perm.value}
|
key={perm.value}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-between gap-3 px-2.5 py-2 rounded-md transition-colors",
|
"flex items-center justify-between gap-3 px-2.5 py-2 rounded-md transition-colors",
|
||||||
isSelected ? "bg-muted/60 hover:bg-accent hover:text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
|
isSelected
|
||||||
|
? "bg-muted/60 hover:bg-accent hover:text-accent-foreground"
|
||||||
|
: "hover:bg-accent hover:text-accent-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,9 @@ export function TeamMemoryManager({ searchSpaceId }: TeamMemoryManagerProps) {
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
disabled={editing || !editQuery.trim()}
|
disabled={editing || !editQuery.trim()}
|
||||||
className={`h-11 w-11 shrink-0 rounded-full ${
|
className={`h-11 w-11 shrink-0 rounded-full ${
|
||||||
editing ? "" : "bg-muted-foreground/15 hover:bg-accent hover:text-accent-foreground"
|
editing
|
||||||
|
? ""
|
||||||
|
: "bg-muted-foreground/15 hover:bg-accent hover:text-accent-foreground"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import {
|
import { ChevronDown, Crown, Dot, File as FileIcon, FolderOpen, X, Zap } from "lucide-react";
|
||||||
ChevronDown,
|
|
||||||
Crown,
|
|
||||||
Dot,
|
|
||||||
File as FileIcon,
|
|
||||||
FolderOpen,
|
|
||||||
X,
|
|
||||||
Zap,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
@ -117,7 +109,8 @@ function flattenTree(
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentPath = ""
|
parentPath = ""
|
||||||
): { name: string; isFolder: boolean; depth: number; path: string; size?: number }[] {
|
): { name: string; isFolder: boolean; depth: number; path: string; size?: number }[] {
|
||||||
const items: { name: string; isFolder: boolean; depth: number; path: string; size?: number }[] = [];
|
const items: { name: string; isFolder: boolean; depth: number; path: string; size?: number }[] =
|
||||||
|
[];
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
const path = parentPath ? `${parentPath}/${node.name}` : node.name;
|
const path = parentPath ? `${parentPath}/${node.name}` : node.name;
|
||||||
items.push({ name: node.name, isFolder: node.isFolder, depth, path, size: node.size });
|
items.push({ name: node.name, isFolder: node.isFolder, depth, path, size: node.size });
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
import { ExternalLink, Globe } from "lucide-react";
|
import { ExternalLink, Globe } from "lucide-react";
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { tryGetHostname } from "@/lib/url";
|
||||||
import { openSafeNavigationHref, sanitizeHref } from "../shared/media";
|
import { openSafeNavigationHref, sanitizeHref } from "../shared/media";
|
||||||
import { cn } from "./_adapter";
|
import { cn } from "./_adapter";
|
||||||
import { CitationHoverPopover } from "./citation-hover-popover";
|
import { CitationHoverPopover } from "./citation-hover-popover";
|
||||||
import type { CitationVariant, SerializableCitation } from "./schema";
|
import type { CitationVariant, SerializableCitation } from "./schema";
|
||||||
import { tryGetHostname } from "@/lib/url";
|
|
||||||
import { TYPE_ICONS } from "./type-icons";
|
import { TYPE_ICONS } from "./type-icons";
|
||||||
|
|
||||||
const FALLBACK_LOCALE = "en-US";
|
const FALLBACK_LOCALE = "en-US";
|
||||||
|
|
@ -115,9 +115,7 @@ export function Citation(props: CitationProps) {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm leading-snug font-medium">{title}</p>
|
<p className="text-sm leading-snug font-medium">{title}</p>
|
||||||
{snippet && (
|
{snippet && (
|
||||||
<p className="text-muted-foreground line-clamp-2 text-xs leading-relaxed">
|
<p className="text-muted-foreground line-clamp-2 text-xs leading-relaxed">{snippet}</p>
|
||||||
{snippet}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CitationHoverPopover>
|
</CitationHoverPopover>
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,7 @@ export function CalloutElement({ children, ...props }: PlateElementProps<TCallou
|
||||||
}, [editor, variant, props.path]);
|
}, [editor, variant, props.path]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlateElement
|
<PlateElement {...props} className={cn(calloutVariants({ variant }), props.className)}>
|
||||||
{...props}
|
|
||||||
className={cn(calloutVariants({ variant }), props.className)}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="mt-0.5 h-auto shrink-0 cursor-pointer select-none p-0 text-lg leading-none hover:bg-transparent"
|
className="mt-0.5 h-auto shrink-0 cursor-pointer select-none p-0 text-lg leading-none hover:bg-transparent"
|
||||||
|
|
|
||||||
|
|
@ -84,12 +84,7 @@ export function EquationElement({ children, ...props }: PlateElementProps<TEquat
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button size="sm" className="h-auto px-2 py-1 text-xs" onClick={onSubmit} type="button">
|
||||||
size="sm"
|
|
||||||
className="h-auto px-2 py-1 text-xs"
|
|
||||||
onClick={onSubmit}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Done
|
Done
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -231,4 +231,3 @@ export function getRegenerateUrl(threadId: number): string {
|
||||||
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
|
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
|
||||||
return `${backendUrl}/api/v1/threads/${threadId}/regenerate`;
|
return `${backendUrl}/api/v1/threads/${threadId}/regenerate`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
* `extractDomain` helpers that had subtly different error fallbacks.
|
* `extractDomain` helpers that had subtly different error fallbacks.
|
||||||
*/
|
*/
|
||||||
export function tryGetHostname(url: string): string | undefined {
|
export function tryGetHostname(url: string): string | undefined {
|
||||||
try {
|
try {
|
||||||
return new URL(url).hostname.replace(/^www\./, "");
|
return new URL(url).hostname.replace(/^www\./, "");
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue