Update chat empty state and Auto space mode (#954)

This commit is contained in:
Dhravya Shah 2026-05-16 23:55:34 -07:00 committed by GitHub
parent d0f6a495a6
commit b6a9b1ea7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 250 additions and 104 deletions

View file

@ -329,7 +329,7 @@ export class SupermemoryClient {
async getDocuments(
containerTags?: string[],
page = 1,
limit = 200,
limit = 10,
): Promise<DocumentsApiResponse> {
try {
const response = await fetch(`${this.apiUrl}/v3/documents/documents`, {

View file

@ -318,7 +318,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
? [effectiveContainerTag]
: undefined
const result = await client.getDocuments(containerTags, 1, 200)
const result = await client.getDocuments(containerTags, 1, 10)
const memoryCount = result.documents.reduce(
(sum, d) => sum + d.memoryEntries.length,
@ -366,7 +366,7 @@ export class SupermemoryMCP extends McpAgent<Env, unknown, Props> {
inputSchema: z.object({
containerTag: z.string().optional(),
page: z.number().optional().default(1),
limit: z.number().optional().default(200),
limit: z.number().optional().default(10),
}),
_meta: {
ui: {

View file

@ -159,6 +159,9 @@ export default function NewPage() {
const [fullscreenInitialContent, setFullscreenInitialContent] = useState("")
const [queuedChatSeed, setQueuedChatSeed] = useState<string | null>(null)
const [queuedChatModel, setQueuedChatModel] = useState<ModelId | null>(null)
const [queuedChatProject, setQueuedChatProject] = useState<string | null>(
null,
)
const [queuedHighlightContent, setQueuedHighlightContent] = useState<
string | null
>(null)
@ -473,6 +476,7 @@ export default function NewPage() {
setQueuedHighlightContent(highlightContent)
setQueuedChatSeed(userReply)
setQueuedChatModel(null)
setQueuedChatProject(null)
setQueuedMessageSource("highlight")
void setViewMode("chat")
},
@ -480,10 +484,11 @@ export default function NewPage() {
)
const handleHomeChatStart = useCallback(
(message: string, model: ModelId) => {
(message: string, model: ModelId, projectId: string) => {
setQueuedHighlightContent(null)
setQueuedChatSeed(message)
setQueuedChatModel(model)
setQueuedChatProject(projectId)
setQueuedMessageSource("home")
void setViewMode("chat")
},
@ -493,6 +498,7 @@ export default function NewPage() {
const consumeQueuedChat = useCallback(() => {
setQueuedChatSeed(null)
setQueuedChatModel(null)
setQueuedChatProject(null)
setQueuedHighlightContent(null)
setQueuedMessageSource("highlight")
}, [])
@ -608,7 +614,7 @@ export default function NewPage() {
onConsumeQueuedMessage={consumeQueuedChat}
queuedMessageSource={queuedMessageSource}
initialSelectedModel={queuedChatModel}
emptyStateSuggestions={highlightsData?.questions}
initialChatProject={queuedChatProject}
/>
</div>
) : viewMode === "integrations" ? (

View file

@ -11,12 +11,18 @@ import { dmSansClassName } from "@/lib/fonts"
export function ChatGraphContextRail({
messages,
containerTags,
className,
}: {
messages: UIMessage[]
containerTags?: string[] | null
className?: string
}) {
const { effectiveContainerTags } = useProject()
const graphContainerTags =
containerTags === undefined
? effectiveContainerTags
: (containerTags ?? undefined)
const highlightIds = useMemo(
() => extractHighlightDocumentIdsFromMessages(messages),
[messages],
@ -45,7 +51,7 @@ export function ChatGraphContextRail({
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-r from-transparent to-[#05080D]" />
<div className="relative z-[2] min-h-0 flex-1 pt-10">
<MemoryGraph
containerTags={effectiveContainerTags}
containerTags={graphContainerTags}
variant="consumer"
highlightDocumentIds={highlightIds}
highlightsVisible={highlightIds.length > 0}

View file

@ -1,41 +1,33 @@
"use client"
import { useCallback, useMemo, useState } from "react"
import { useCallback, useState } from "react"
import ChatInput from "./input"
import ChatModelSelector from "./model-selector"
import { getChatSpaceDisplayLabel } from "@/lib/chat-space-label"
import { useProject } from "@/stores"
import { useContainerTags } from "@/hooks/use-container-tags"
import { dmSansClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import type { ModelId } from "@/lib/models"
import { SpaceSelector } from "@/components/space-selector"
export function HomeChatComposer({
onStartChat,
className,
}: {
onStartChat: (message: string, model: ModelId) => void
onStartChat: (message: string, model: ModelId, projectId: string) => void
className?: string
}) {
const [input, setInput] = useState("")
const [selectedModel, setSelectedModel] = useState<ModelId>("gemini-2.5-pro")
const { selectedProject } = useProject()
const { allProjects } = useContainerTags()
const chatSpaceLabel = useMemo(
() =>
getChatSpaceDisplayLabel({
selectedProject,
allProjects,
}),
[selectedProject, allProjects],
)
const [chatSpaceProjects, setChatSpaceProjects] = useState<string[]>([
selectedProject,
])
const send = useCallback(() => {
const t = input.trim()
if (!t) return
onStartChat(t, selectedModel)
onStartChat(t, selectedModel, chatSpaceProjects[0] ?? selectedProject)
setInput("")
}, [input, onStartChat, selectedModel])
}, [chatSpaceProjects, input, onStartChat, selectedModel, selectedProject])
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
@ -62,17 +54,13 @@ export function HomeChatComposer({
onModelChange={setSelectedModel}
minimal
/>
<div
className={cn(
"inline-flex max-w-[min(160px,35vw)] min-w-0 shrink items-center rounded-full bg-fg-primary/5 px-3 py-1.5",
dmSansClassName(),
)}
title={chatSpaceLabel}
>
<span className="truncate text-sm text-fg-primary">
{chatSpaceLabel}
</span>
</div>
<SpaceSelector
selectedProjects={chatSpaceProjects}
onValueChange={setChatSpaceProjects}
variant="insideOut"
includeAuto
triggerClassName="h-auto min-h-0 max-w-[min(160px,35vw)] rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5 shadow-none hover:bg-[#05080D]"
/>
</>
}
/>

View file

@ -22,7 +22,6 @@ import {
ChevronDownIcon,
HistoryIcon,
Plus,
SearchIcon,
SquarePenIcon,
Trash2,
XIcon,
@ -33,11 +32,11 @@ import { dmSansClassName } from "@/lib/fonts"
import ChatInput from "./input"
import ChatModelSelector from "./model-selector"
import { getNovaChatErrorCopy } from "@/lib/chat-stream-error"
import { GradientLogo, LogoBgGradient } from "@ui/assets/Logo"
import { useProject } from "@/stores"
import { useContainerTags } from "@/hooks/use-container-tags"
import { getChatSpaceDisplayLabel } from "@/lib/chat-space-label"
import { modelNames, type ModelId } from "@/lib/models"
import { SpaceSelector } from "@/components/space-selector"
import { SuperLoader } from "../superloader"
import { UserMessage } from "./message/user-message"
import { AgentMessage } from "./message/agent-message"
@ -49,50 +48,78 @@ import { analytics } from "@/lib/analytics"
import { generateId } from "@lib/generate-id"
import { useViewMode } from "@/lib/view-mode-context"
import { threadParam } from "@/lib/search-params"
import { AUTO_CHAT_SPACE_ID } from "@/lib/chat-auto-space"
const DEFAULT_SUGGESTIONS = [
"Show me all content related to Supermemory.",
"Summarize the key ideas from My Gita.",
"Which memories connect design and AI?",
"What are the main themes across my memories?",
const DEFAULT_CHAT_PROMPTS = [
"What do you know about me?",
"What have I been working on lately?",
"What themes keep showing up in my memories?",
]
const chatEmptyCardClass = cn(
"flex min-h-[76px] flex-col justify-between rounded-lg border border-[#2B3038] bg-[#14161A]/95 p-3 text-left md:min-h-[88px]",
"shadow-[0_18px_50px_rgba(0,0,0,0.32),inset_0_1px_0_rgba(255,255,255,0.04)]",
"transition-colors hover:border-[#3374FF]/55 hover:bg-[#1A1F26] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#3374FF]/70",
)
function ChatEmptyStatePlaceholder({
onSuggestionClick,
suggestions = DEFAULT_SUGGESTIONS,
suggestions = DEFAULT_CHAT_PROMPTS,
}: {
onSuggestionClick: (suggestion: string) => void
suggestions?: string[]
}) {
const promptCards = suggestions.slice(0, 3)
return (
<div
id="chat-empty-state"
className="flex flex-col items-center justify-center h-full"
className="relative flex min-h-full items-center justify-center overflow-hidden px-0 py-6 md:px-3"
>
<div className="relative size-32">
<GradientLogo className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-16" />
<LogoBgGradient className="size-full" />
</div>
<div className="gap-3 flex flex-col items-center justify-center">
<p>Ask me anything about your memories</p>
<div
<div
className="pointer-events-none absolute inset-x-[-1rem] inset-y-0 bg-[radial-gradient(circle_at_center,rgba(105,167,240,0.28)_1px,transparent_1px)] bg-size-[32px_32px] opacity-80 mask-[radial-gradient(ellipse_at_center,black_52%,transparent_100%)]"
aria-hidden
/>
<div
className="pointer-events-none absolute inset-x-[-1rem] bottom-0 h-2/3 bg-[radial-gradient(ellipse_at_bottom,rgba(20,65,255,0.42),transparent_68%)]"
aria-hidden
/>
<div className="relative z-10 flex w-full max-w-xl flex-col items-center text-center">
<NovaOrb size={52} className="mb-3 blur-[1.5px]!" />
<h2
className={cn(
"mb-1 max-w-[420px] text-[24px] font-medium leading-[1.12] tracking-normal text-white md:text-[30px]",
dmSansClassName(),
"flex flex-col gap-2 justify-center items-center",
)}
>
{suggestions.map((suggestion) => (
<Button
Nova knows you.
</h2>
<p
className={cn(
"mb-4 max-w-[420px] text-[14px] leading-5 text-[#8B8B8B] md:text-[15px]",
dmSansClassName(),
)}
>
<span className="text-[#FAFAFA]">
Your personal memories are all here.
</span>{" "}
Chat with supermemory and ask about...
</p>
<div className="mb-3 grid w-full grid-cols-1 gap-2.5 sm:grid-cols-3">
{promptCards.map((suggestion, index) => (
<button
key={suggestion}
variant="default"
className="rounded-full text-base gap-1 h-10! border-[#2261CA33] bg-[#041127] border w-fit max-w-[400px] py-[4px] pl-[8px] pr-[12px] hover:bg-[#0A1A3A] hover:[&_span]:text-white hover:[&_svg]:text-white transition-colors cursor-pointer"
type="button"
onClick={() => onSuggestionClick(suggestion)}
className={chatEmptyCardClass}
>
<SearchIcon className="size-4 text-[#267BF1] shrink-0" />
<span className="text-[#267BF1] text-[12px] truncate">
<span className="flex size-5 items-center justify-center rounded-full border border-[#3374FF]/35 bg-[#071B3A] text-[11px] font-medium text-[#4BA0FA]">
{index + 1}
</span>
<span className="mt-2 line-clamp-3 text-[13px] font-medium leading-[18px] text-white md:text-[14px] md:leading-5">
{suggestion}
</span>
</Button>
</button>
))}
</div>
</div>
@ -150,6 +177,7 @@ export function ChatSidebar({
onConsumeQueuedMessage,
queuedMessageSource = "highlight",
initialSelectedModel = null,
initialChatProject = null,
emptyStateSuggestions,
layout = "sidebar",
}: {
@ -160,6 +188,7 @@ export function ChatSidebar({
onConsumeQueuedMessage?: () => void
queuedMessageSource?: "highlight" | "home"
initialSelectedModel?: ModelId | null
initialChatProject?: string | null
emptyStateSuggestions?: string[]
layout?: "sidebar" | "page"
}) {
@ -196,16 +225,22 @@ export function ChatSidebar({
const pendingHighlightMessageRef = useRef<UIMessage[] | null>(null)
const targetHighlightChatIdRef = useRef<string | null>(null)
const { selectedProject } = useProject()
const [chatSpaceProjects, setChatSpaceProjects] = useState<string[]>([
initialChatProject ?? selectedProject,
])
const chatProject = chatSpaceProjects[0] ?? selectedProject
const { allProjects } = useContainerTags()
const selectedProjectRef = useRef(selectedProject)
selectedProjectRef.current = selectedProject
const selectedProjectRef = useRef(chatProject)
selectedProjectRef.current = chatProject
const chatSpaceLabel = useMemo(
() =>
getChatSpaceDisplayLabel({
selectedProject,
allProjects,
}),
[selectedProject, allProjects],
chatProject === AUTO_CHAT_SPACE_ID
? "Auto"
: getChatSpaceDisplayLabel({
selectedProject: chatProject,
allProjects,
}),
[chatProject, allProjects],
)
const { viewMode } = useViewMode()
const { user: _user } = useAuth()
@ -232,6 +267,12 @@ export function ChatSidebar({
metadata: {
chatId: chatIdRef.current,
projectId: selectedProjectRef.current,
spaceMode:
selectedProjectRef.current === AUTO_CHAT_SPACE_ID
? "auto"
: "manual",
enableSpaceDiscovery:
selectedProjectRef.current === AUTO_CHAT_SPACE_ID,
model: selectedModelRef.current,
},
},
@ -326,6 +367,25 @@ export function ChatSidebar({
scrollToBottom()
}
const handleSuggestedQuestion = useCallback(
(suggestion: string) => {
if (status === "submitted" || status === "streaming") return
if (!threadId) setThreadId(fallbackChatId)
analytics.chatSuggestedQuestionClicked()
analytics.chatMessageSent({ source: "suggested" })
sendMessage({ text: suggestion })
scrollToBottom()
},
[
fallbackChatId,
sendMessage,
setThreadId,
status,
threadId,
scrollToBottom,
],
)
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
@ -394,7 +454,7 @@ export function ChatSidebar({
setIsLoadingThreads(true)
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/threads?projectId=${selectedProject}`,
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/threads?projectId=${chatProject}`,
{ credentials: "include" },
)
if (response.ok) {
@ -406,7 +466,7 @@ export function ChatSidebar({
} finally {
setIsLoadingThreads(false)
}
}, [selectedProject])
}, [chatProject])
useEffect(() => {
if (!isHistoryOpen) return
@ -908,20 +968,13 @@ export function ChatSidebar({
selectedModel={selectedModel}
onModelChange={handleModelChange}
/>
<div
className={cn(
"inline-flex h-10 max-w-[min(192px,42vw)] shrink min-w-0 items-center rounded-full border border-[#73737333] bg-[#0D121A] px-3",
dmSansClassName(),
)}
style={{
boxShadow: "1.5px 1.5px 4.5px 0 rgba(0, 0, 0, 0.70) inset",
}}
title={chatSpaceLabel}
>
<span className="truncate text-sm text-white">
{chatSpaceLabel}
</span>
</div>
<SpaceSelector
selectedProjects={chatSpaceProjects}
onValueChange={setChatSpaceProjects}
variant="insideOut"
includeAuto
triggerClassName="h-10 min-h-10 max-w-[min(192px,42vw)] border border-[#73737333] bg-[#0D121A] shadow-[1.5px_1.5px_4.5px_0_rgba(0,0,0,0.70)_inset]"
/>
</>
)}
</div>
@ -948,11 +1001,7 @@ export function ChatSidebar({
)}
{messages.length === 0 && (
<ChatEmptyStatePlaceholder
onSuggestionClick={(suggestion) => {
analytics.chatSuggestedQuestionClicked()
analytics.chatMessageSent({ source: "suggested" })
sendMessage({ text: suggestion })
}}
onSuggestionClick={handleSuggestedQuestion}
suggestions={emptyStateSuggestions}
/>
)}
@ -1105,17 +1154,13 @@ export function ChatSidebar({
onModelChange={handleModelChange}
minimal
/>
<div
className={cn(
"inline-flex max-w-[min(160px,35vw)] shrink min-w-0 items-center rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5",
dmSansClassName(),
)}
title={chatSpaceLabel}
>
<span className="truncate text-sm text-[#FAFAFA]">
{chatSpaceLabel}
</span>
</div>
<SpaceSelector
selectedProjects={chatSpaceProjects}
onValueChange={setChatSpaceProjects}
variant="insideOut"
includeAuto
triggerClassName="h-auto min-h-0 max-w-[min(160px,35vw)] rounded-full border border-[#161F2C] bg-[#000000] px-3 py-1.5 shadow-none hover:bg-[#05080D]"
/>
</>
) : undefined
}
@ -1165,7 +1210,12 @@ export function ChatSidebar({
{chatHistorySheet}
{isPageDesktop ? (
<div className="flex h-full min-h-0 w-full flex-1 flex-row">
<ChatGraphContextRail messages={messages} />
<ChatGraphContextRail
messages={messages}
containerTags={
chatProject === AUTO_CHAT_SPACE_ID ? null : [chatProject]
}
/>
<div className="flex h-full min-h-0 w-full min-w-0 max-w-[720px] shrink-0 basis-[min(720px,50vw)] flex-col">
{pageDesktopToolbarRow}
<div className="relative mx-auto flex h-full min-h-0 w-full min-w-0 max-w-[720px] flex-1 flex-col">

View file

@ -19,6 +19,7 @@ import {
Loader,
Pencil,
Check,
Sparkles,
} from "lucide-react"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { toast } from "sonner"
@ -43,6 +44,7 @@ import {
} from "@/lib/plugin-catalog"
import { InstallSteps, PillButton } from "./integrations/install-steps"
import { useProjectMutations } from "@/hooks/use-project-mutations"
import { AUTO_CHAT_SPACE_ID } from "@/lib/chat-auto-space"
interface SelectSpacesModalProps {
isOpen: boolean
@ -52,6 +54,7 @@ interface SelectSpacesModalProps {
projects: ContainerTagListType[]
recents?: string[]
showNewSpace?: boolean
includeAuto?: boolean
onNewSpace?: () => void
enableDelete?: boolean
onDeleteRequest?: (project: {
@ -90,6 +93,7 @@ export function SelectSpacesModal({
projects,
recents,
showNewSpace = false,
includeAuto = false,
onNewSpace,
enableDelete = false,
onDeleteRequest,
@ -198,6 +202,7 @@ export function SelectSpacesModal({
const defaultCategory = useMemo<CategoryId>(() => {
if (!currentSelection) return "all"
if (currentSelection === AUTO_CHAT_SPACE_ID) return "all"
const plugin = detectPluginSpace(currentSelection)
if (plugin) return `plugin:${plugin.pluginId}`
return "my"
@ -380,6 +385,15 @@ export function SelectSpacesModal({
[onApply],
)
const handleSelectAuto = useCallback(() => {
setEditingProject(null)
setIsBulkDeleteMode(false)
setBulkDeleteTags(new Set())
setLastBulkDeleteTag(null)
onApply([AUTO_CHAT_SPACE_ID])
setSearchQuery("")
}, [onApply])
const handleBulkModeToggle = useCallback(() => {
setEditingProject(null)
setBulkDeleteTags(new Set())
@ -478,6 +492,19 @@ export function SelectSpacesModal({
[filteredProjects, recentSet],
)
const showAutoRow = useMemo(() => {
if (!includeAuto) return false
if (isBulkDeleteMode) return false
if (activeCategory !== "all" && activeCategory !== "my") return false
const query = searchQuery.trim().toLowerCase()
if (!query) return true
return (
"auto".includes(query) ||
"let nova choose the right spaces".includes(query) ||
"discover spaces".includes(query)
)
}, [includeAuto, isBulkDeleteMode, activeCategory, searchQuery])
const visibleBulkDeleteTags = useMemo(
() =>
[...recentProjects, ...mainList]
@ -742,6 +769,48 @@ export function SelectSpacesModal({
],
)
const renderAutoRow = useCallback(() => {
const isSelected = currentSelection === AUTO_CHAT_SPACE_ID
return (
<div
key={AUTO_CHAT_SPACE_ID}
className={cn(
"group flex min-w-0 max-w-full items-center gap-3 w-full px-3 py-2.5 rounded-[12px] transition-colors",
isSelected
? "bg-[#14161A] shadow-inside-out"
: "hover:bg-[#14161A]/50",
)}
>
<div
className={cn(
"w-4 h-4 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors",
isSelected ? "border-[#4BA0FA]" : "border-[#737373]",
)}
>
{isSelected && <div className="w-2 h-2 rounded-full bg-[#4BA0FA]" />}
</div>
<button
type="button"
onClick={handleSelectAuto}
className="flex min-w-0 flex-1 items-center gap-3 text-left cursor-pointer focus:outline-none focus:ring-0"
>
<span
className="shrink-0 flex h-5 w-5 items-center justify-center rounded-[6px] bg-[#071B3A] text-[#4BA0FA]"
aria-hidden
>
<Sparkles className="size-3.5" />
</span>
<span className="min-w-0 flex-1 truncate text-[#fafafa] text-sm font-medium">
Auto
<span className="ml-1.5 text-[12px] text-[#737373]">
· Nova chooses spaces
</span>
</span>
</button>
</div>
)
}, [currentSelection, handleSelectAuto])
return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
<DialogContent
@ -946,12 +1015,21 @@ export function SelectSpacesModal({
</div>
<div className="min-h-0 flex-1 overflow-y-auto scrollbar-thin pr-1 sm:max-h-[360px]">
{filteredProjects.length === 0 ? (
{filteredProjects.length === 0 && !showAutoRow ? (
<p className="text-center text-[#737373] text-sm py-8">
No spaces found
</p>
) : (
<div className="flex flex-col gap-1">
{showAutoRow && (
<>
<div className="px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
Mode
</div>
{renderAutoRow()}
<div className="my-1.5 h-px bg-[rgba(82,89,102,0.18)]" />
</>
)}
{recentProjects.length > 0 && (
<>
<div className="flex items-center gap-1.5 px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">

View file

@ -7,8 +7,9 @@ import { cn } from "@lib/utils"
import { $fetch } from "@lib/api"
import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts"
import { DEFAULT_PROJECT_ID } from "@lib/constants"
import { XIcon, Loader2, Trash2 } from "lucide-react"
import { ChevronDownIcon, Sparkles, XIcon, Loader2, Trash2 } from "lucide-react"
import type { ContainerTagListType } from "@lib/types"
import { AUTO_CHAT_SPACE_ID } from "@/lib/chat-auto-space"
import { AddSpaceModal } from "./add-space-modal"
import { SelectSpacesModal } from "./select-spaces-modal"
import { useProjectMutations } from "@/hooks/use-project-mutations"
@ -46,6 +47,7 @@ export interface SpaceSelectorProps {
showNewSpace?: boolean
enableDelete?: boolean
compact?: boolean
includeAuto?: boolean
}
const triggerVariants = {
@ -104,6 +106,7 @@ export function SpaceSelector({
showNewSpace = true,
enableDelete = false,
compact = false,
includeAuto = false,
}: SpaceSelectorProps) {
const [showCreateDialog, setShowCreateDialog] = useState(false)
const [showSelectSpacesModal, setShowSelectSpacesModal] = useState(false)
@ -158,7 +161,7 @@ export function SpaceSelector({
return data?.pagination?.totalItems ?? 0
},
staleTime: 30 * 1000,
enabled: !!activeTag,
enabled: !!activeTag && activeTag !== AUTO_CHAT_SPACE_ID,
})
const pluginTags = useMemo(
@ -176,10 +179,14 @@ export function SpaceSelector({
name: string
emoji: string | null
plugin: ReturnType<typeof detectPluginSpace>
isAuto: boolean
}>(() => {
const containerTag = selectedProjects[0] ?? ""
if (includeAuto && containerTag === AUTO_CHAT_SPACE_ID) {
return { name: "Auto", emoji: null, plugin: null, isAuto: true }
}
if (!containerTag || containerTag === DEFAULT_PROJECT_ID) {
return { name: "My Space", emoji: "📁", plugin: null }
return { name: "My Space", emoji: "📁", plugin: null, isAuto: false }
}
const found = allProjects.find(
(p: ContainerTagListType) => p.containerTag === containerTag,
@ -195,8 +202,9 @@ export function SpaceSelector({
: spaceSelectorDisplayName(found, containerTag),
emoji: found?.emoji || "📁",
plugin,
isAuto: false,
}
}, [allProjects, selectedProjects, pluginMetaMap])
}, [allProjects, selectedProjects, pluginMetaMap, includeAuto])
const pushRecent = useCallback((tag: string) => {
setRecents((prev) => {
@ -212,7 +220,7 @@ export function SpaceSelector({
const selectedTag = next[0]
setShowSelectSpacesModal(false)
onValueChange(next)
if (selectedTag) {
if (selectedTag && selectedTag !== AUTO_CHAT_SPACE_ID) {
queueMicrotask(() => {
analytics.spaceSwitched({ space_id: selectedTag })
pushRecent(selectedTag)
@ -356,7 +364,9 @@ export function SpaceSelector({
triggerClassName,
)}
>
{displayInfo.plugin ? (
{displayInfo.isAuto ? (
<Sparkles className="size-3.5 shrink-0 text-[#4BA0FA]" />
) : displayInfo.plugin ? (
displayInfo.plugin.iconSrc ? (
<Image
src={displayInfo.plugin.iconSrc}
@ -404,6 +414,12 @@ export function SpaceSelector({
· {formatCount(spaceCountData)}
</span>
)}
{!compact && (
<ChevronDownIcon
className="size-3.5 shrink-0 text-[#737373]"
aria-hidden
/>
)}
{compact && (
<span className="sr-only">
{isLoading ? "Loading" : displayInfo.name}
@ -433,6 +449,7 @@ export function SpaceSelector({
projects={allProjects}
recents={recents}
showNewSpace={showNewSpace}
includeAuto={includeAuto}
onNewSpace={handleNewSpace}
enableDelete={enableDelete}
onDeleteRequest={handleDeleteRequest}

View file

@ -0,0 +1 @@
export const AUTO_CHAT_SPACE_ID = "__supermemory_auto_space__"