mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 01:06:23 +02:00
Merge branch 'dev' into documents-mentions
This commit is contained in:
commit
c4400a0ec2
13 changed files with 55 additions and 115 deletions
|
|
@ -6,9 +6,8 @@ Create Date: 2025-12-21
|
||||||
|
|
||||||
This migration:
|
This migration:
|
||||||
1. Migrates data from old 'chats' table to 'new_chat_threads' and 'new_chat_messages'
|
1. Migrates data from old 'chats' table to 'new_chat_threads' and 'new_chat_messages'
|
||||||
2. Drops the 'podcasts' table (podcast data is not migrated as per user request)
|
2. Drops the 'chats' table
|
||||||
3. Drops the 'chats' table
|
3. Removes the 'chattype' enum
|
||||||
4. Removes the 'chattype' enum
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
@ -92,7 +91,11 @@ def upgrade() -> None:
|
||||||
print(f"[Migration 49] Skipping empty chat {chat_id}")
|
print(f"[Migration 49] Skipping empty chat {chat_id}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create new thread
|
# Create new thread - truncate title to 500 chars (VARCHAR(500) limit)
|
||||||
|
thread_title = title or "Migrated Chat"
|
||||||
|
if len(thread_title) > 500:
|
||||||
|
thread_title = thread_title[:497] + "..."
|
||||||
|
|
||||||
result = connection.execute(
|
result = connection.execute(
|
||||||
sa.text("""
|
sa.text("""
|
||||||
INSERT INTO new_chat_threads
|
INSERT INTO new_chat_threads
|
||||||
|
|
@ -101,7 +104,7 @@ def upgrade() -> None:
|
||||||
RETURNING id
|
RETURNING id
|
||||||
"""),
|
"""),
|
||||||
{
|
{
|
||||||
"title": title or "Migrated Chat",
|
"title": thread_title,
|
||||||
"search_space_id": search_space_id,
|
"search_space_id": search_space_id,
|
||||||
"created_at": created_at,
|
"created_at": created_at,
|
||||||
},
|
},
|
||||||
|
|
@ -162,11 +165,7 @@ def upgrade() -> None:
|
||||||
|
|
||||||
print(f"[Migration 49] Successfully migrated {migrated_count} chats")
|
print(f"[Migration 49] Successfully migrated {migrated_count} chats")
|
||||||
|
|
||||||
# Drop podcasts table (FK references chats, so drop first)
|
# Drop chats table (podcasts table was already updated to remove chat_id FK)
|
||||||
print("[Migration 49] Dropping podcasts table...")
|
|
||||||
op.drop_table("podcasts")
|
|
||||||
|
|
||||||
# Drop chats table
|
|
||||||
print("[Migration 49] Dropping chats table...")
|
print("[Migration 49] Dropping chats table...")
|
||||||
op.drop_table("chats")
|
op.drop_table("chats")
|
||||||
|
|
||||||
|
|
@ -178,7 +177,7 @@ def upgrade() -> None:
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
"""Recreate old tables (data cannot be restored)."""
|
"""Recreate old chats table (data cannot be restored)."""
|
||||||
# Recreate chattype enum
|
# Recreate chattype enum
|
||||||
op.execute(
|
op.execute(
|
||||||
sa.text("""
|
sa.text("""
|
||||||
|
|
@ -209,32 +208,4 @@ def downgrade() -> None:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Recreate podcasts table
|
print("[Migration 49 Downgrade] Chats table recreated (data not restored)")
|
||||||
op.create_table(
|
|
||||||
"podcasts",
|
|
||||||
sa.Column("id", sa.Integer(), primary_key=True, index=True),
|
|
||||||
sa.Column("title", sa.String(), nullable=False, index=True),
|
|
||||||
sa.Column("podcast_transcript", sa.JSON(), nullable=False, server_default="{}"),
|
|
||||||
sa.Column("file_location", sa.String(500), nullable=False, server_default=""),
|
|
||||||
sa.Column(
|
|
||||||
"chat_id",
|
|
||||||
sa.Integer(),
|
|
||||||
sa.ForeignKey("chats.id", ondelete="CASCADE"),
|
|
||||||
nullable=True,
|
|
||||||
),
|
|
||||||
sa.Column("chat_state_version", sa.BigInteger(), nullable=True),
|
|
||||||
sa.Column(
|
|
||||||
"search_space_id",
|
|
||||||
sa.Integer(),
|
|
||||||
sa.ForeignKey("searchspaces.id", ondelete="CASCADE"),
|
|
||||||
nullable=False,
|
|
||||||
),
|
|
||||||
sa.Column(
|
|
||||||
"created_at",
|
|
||||||
sa.TIMESTAMP(timezone=True),
|
|
||||||
nullable=False,
|
|
||||||
server_default=sa.func.now(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
print("[Migration 49 Downgrade] Tables recreated (data not restored)")
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ Create Date: 2024-12-22
|
||||||
|
|
||||||
This migration:
|
This migration:
|
||||||
1. Migrates data from old llm_configs table to new_llm_configs (preserving user configs)
|
1. Migrates data from old llm_configs table to new_llm_configs (preserving user configs)
|
||||||
2. Drops the old llm_configs table (no longer used)
|
2. Updates searchspaces to point to migrated configs
|
||||||
3. Removes the is_default column from new_llm_configs (roles now determine which config to use)
|
3. Drops the old llm_configs table (no longer used)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
@ -47,7 +47,6 @@ def upgrade():
|
||||||
system_instructions,
|
system_instructions,
|
||||||
use_default_system_instructions,
|
use_default_system_instructions,
|
||||||
citations_enabled,
|
citations_enabled,
|
||||||
is_default,
|
|
||||||
search_space_id,
|
search_space_id,
|
||||||
created_at
|
created_at
|
||||||
)
|
)
|
||||||
|
|
@ -59,11 +58,10 @@ def upgrade():
|
||||||
lc.model_name,
|
lc.model_name,
|
||||||
lc.api_key,
|
lc.api_key,
|
||||||
lc.api_base,
|
lc.api_base,
|
||||||
COALESCE(lc.litellm_params, '{}'::jsonb),
|
COALESCE(lc.litellm_params::json, '{}'::json),
|
||||||
'' as system_instructions, -- Use defaults
|
'' as system_instructions, -- Use defaults
|
||||||
TRUE as use_default_system_instructions,
|
TRUE as use_default_system_instructions,
|
||||||
TRUE as citations_enabled,
|
TRUE as citations_enabled,
|
||||||
FALSE as is_default,
|
|
||||||
lc.search_space_id,
|
lc.search_space_id,
|
||||||
COALESCE(lc.created_at, NOW())
|
COALESCE(lc.created_at, NOW())
|
||||||
FROM llm_configs lc
|
FROM llm_configs lc
|
||||||
|
|
@ -130,23 +128,7 @@ def upgrade():
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
# STEP 3: Drop the is_default column from new_llm_configs
|
# STEP 3: Drop the old llm_configs table (data has been migrated)
|
||||||
# (role assignments now determine which config to use)
|
|
||||||
op.execute(
|
|
||||||
"""
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'new_llm_configs' AND column_name = 'is_default'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE new_llm_configs DROP COLUMN is_default;
|
|
||||||
END IF;
|
|
||||||
END$$;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# STEP 4: Drop the old llm_configs table (data has been migrated)
|
|
||||||
op.execute("DROP TABLE IF EXISTS llm_configs CASCADE")
|
op.execute("DROP TABLE IF EXISTS llm_configs CASCADE")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -213,7 +195,7 @@ def downgrade():
|
||||||
nlc.api_key,
|
nlc.api_key,
|
||||||
nlc.api_base,
|
nlc.api_base,
|
||||||
'English' as language, -- Default language
|
'English' as language, -- Default language
|
||||||
COALESCE(nlc.litellm_params, '{}'::jsonb),
|
COALESCE(nlc.litellm_params::jsonb, '{}'::jsonb),
|
||||||
nlc.search_space_id,
|
nlc.search_space_id,
|
||||||
nlc.created_at
|
nlc.created_at
|
||||||
FROM new_llm_configs nlc
|
FROM new_llm_configs nlc
|
||||||
|
|
@ -227,18 +209,3 @@ def downgrade():
|
||||||
END$$;
|
END$$;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add back the is_default column to new_llm_configs
|
|
||||||
op.execute(
|
|
||||||
"""
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'new_llm_configs' AND column_name = 'is_default'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE new_llm_configs ADD COLUMN is_default BOOLEAN NOT NULL DEFAULT FALSE;
|
|
||||||
END IF;
|
|
||||||
END$$;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ export function DashboardClientLayout({
|
||||||
/>
|
/>
|
||||||
<SidebarInset className="h-full ">
|
<SidebarInset className="h-full ">
|
||||||
<main className="flex flex-col h-full">
|
<main className="flex flex-col h-full">
|
||||||
<header className="sticky top-0 z-50 flex h-16 shrink-0 items-center gap-2 bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 border-b">
|
<header className="sticky top-0 flex h-16 shrink-0 items-center gap-2 bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 border-b">
|
||||||
<div className="flex items-center justify-between w-full gap-2 px-4">
|
<div className="flex items-center justify-between w-full gap-2 px-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
|
|
@ -265,7 +265,7 @@ export function DashboardClientLayout({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="grow flex-1 overflow-auto min-h-[calc(100vh-64px)]">{children}</div>
|
<div className="flex-1 overflow-hidden">{children}</div>
|
||||||
</main>
|
</main>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,7 @@ export default function ConnectorsPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto py-8 max-w-6xl">
|
<div className="container mx-auto py-8 px-4 max-w-6xl min-h-[calc(100vh-64px)]">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ export default function DocumentsTable() {
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className="w-full px-6 py-4"
|
className="w-full px-6 py-4 min-h-[calc(100vh-64px)]"
|
||||||
>
|
>
|
||||||
<DocumentsFilters
|
<DocumentsFilters
|
||||||
typeCounts={typeCounts ?? {}}
|
typeCounts={typeCounts ?? {}}
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ export default function LogsManagePage() {
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className="w-full px-6 py-4 space-y-6"
|
className="w-full px-6 py-4 space-y-6 min-h-[calc(100vh-64px)]"
|
||||||
>
|
>
|
||||||
{/* Summary Dashboard */}
|
{/* Summary Dashboard */}
|
||||||
<LogsSummaryDashboard
|
<LogsSummaryDashboard
|
||||||
|
|
|
||||||
|
|
@ -694,11 +694,11 @@ export default function NewChatPage() {
|
||||||
<LinkPreviewToolUI />
|
<LinkPreviewToolUI />
|
||||||
<DisplayImageToolUI />
|
<DisplayImageToolUI />
|
||||||
<ScrapeWebpageToolUI />
|
<ScrapeWebpageToolUI />
|
||||||
<div className="flex flex-col h-[calc(100vh-64px)] max-h-[calc(100vh-64px)] overflow-hidden">
|
<div className="flex flex-col h-[calc(100vh-64px)] overflow-hidden">
|
||||||
<ChatHeader searchSpaceId={searchSpaceId} />
|
<Thread
|
||||||
<div className="flex-1 min-h-0 overflow-hidden">
|
messageThinkingSteps={messageThinkingSteps}
|
||||||
<Thread messageThinkingSteps={messageThinkingSteps} />
|
header={<ChatHeader searchSpaceId={searchSpaceId} />}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AssistantRuntimeProvider>
|
</AssistantRuntimeProvider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export default function AddSourcesPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto py-8 px-4">
|
<div className="container mx-auto py-8 px-4 min-h-[calc(100vh-64px)]">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
ThreadPrimitive,
|
ThreadPrimitive,
|
||||||
useAssistantState,
|
useAssistantState,
|
||||||
useThreadViewport,
|
useThreadViewport,
|
||||||
|
useMessage,
|
||||||
} from "@assistant-ui/react";
|
} from "@assistant-ui/react";
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import {
|
import {
|
||||||
|
|
@ -75,6 +76,8 @@ import { cn } from "@/lib/utils";
|
||||||
*/
|
*/
|
||||||
interface ThreadProps {
|
interface ThreadProps {
|
||||||
messageThinkingSteps?: Map<string, ThinkingStep[]>;
|
messageThinkingSteps?: Map<string, ThinkingStep[]>;
|
||||||
|
/** Optional header component to render at the top of the viewport (sticky) */
|
||||||
|
header?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context to pass thinking steps to AssistantMessage
|
// Context to pass thinking steps to AssistantMessage
|
||||||
|
|
@ -267,20 +270,21 @@ const ThinkingStepsScrollHandler: FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Thread: FC<ThreadProps> = ({ messageThinkingSteps = new Map() }) => {
|
export const Thread: FC<ThreadProps> = ({ messageThinkingSteps = new Map() }) => {
|
||||||
|
export const Thread: FC<ThreadProps> = ({ messageThinkingSteps = new Map(), header }) => {
|
||||||
return (
|
return (
|
||||||
<ThinkingStepsContext.Provider value={messageThinkingSteps}>
|
<ThinkingStepsContext.Provider value={messageThinkingSteps}>
|
||||||
<ThreadPrimitive.Root
|
<ThreadPrimitive.Root
|
||||||
className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
|
className="aui-root aui-thread-root @container flex h-full min-h-0 flex-col bg-background"
|
||||||
style={{
|
style={{
|
||||||
["--thread-max-width" as string]: "44rem",
|
["--thread-max-width" as string]: "44rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThreadPrimitive.Viewport
|
<ThreadPrimitive.Viewport
|
||||||
turnAnchor="top"
|
turnAnchor="top"
|
||||||
className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
|
className="aui-thread-viewport relative flex flex-1 min-h-0 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
|
||||||
>
|
>
|
||||||
{/* Auto-scroll handler for thinking steps - must be inside Viewport */}
|
{/* Optional sticky header for model selector etc. */}
|
||||||
<ThinkingStepsScrollHandler />
|
{header && <div className="sticky top-0 z-10 mb-4">{header}</div>}
|
||||||
|
|
||||||
<AssistantIf condition={({ thread }) => thread.isEmpty}>
|
<AssistantIf condition={({ thread }) => thread.isEmpty}>
|
||||||
<ThreadWelcome />
|
<ThreadWelcome />
|
||||||
|
|
@ -375,7 +379,7 @@ const ThreadWelcome: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="aui-thread-welcome-root mx-auto flex w-full max-w-(--thread-max-width) grow flex-col items-center px-4 relative">
|
<div className="aui-thread-welcome-root mx-auto flex w-full max-w-(--thread-max-width) grow flex-col items-center px-4 relative">
|
||||||
{/* Greeting positioned above the composer - fixed position */}
|
{/* Greeting positioned above the composer - fixed position */}
|
||||||
<div className="aui-thread-welcome-message absolute bottom-[calc(50%+5rem)] left-0 right-0 flex flex-col items-center text-center z-10">
|
<div className="aui-thread-welcome-message absolute bottom-[calc(50%+5rem)] left-0 right-0 flex flex-col items-center text-center">
|
||||||
<h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-2 animate-in text-5xl delay-100 duration-500 ease-out fill-mode-both">
|
<h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-2 animate-in text-5xl delay-100 duration-500 ease-out fill-mode-both">
|
||||||
{greeting}
|
{greeting}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,7 @@ export function ChatHeader({ searchSpaceId }: ChatHeaderProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Header Bar */}
|
|
||||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/30 bg-background/80 backdrop-blur-sm">
|
|
||||||
<ModelSelector onEdit={handleEditConfig} onAddNew={handleAddNew} />
|
<ModelSelector onEdit={handleEditConfig} onAddNew={handleAddNew} />
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Config Sidebar */}
|
|
||||||
<ModelConfigSidebar
|
<ModelConfigSidebar
|
||||||
open={sidebarOpen}
|
open={sidebarOpen}
|
||||||
onOpenChange={handleSidebarClose}
|
onOpenChange={handleSidebarClose}
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,10 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
|
||||||
role="combobox"
|
role="combobox"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 gap-2 px-3 rounded-xl border border-border/50 bg-background/50 backdrop-blur-sm",
|
"h-9 gap-2 px-3 rounded-xl border border-border/30 bg-background/50 backdrop-blur-sm",
|
||||||
"hover:bg-muted/80 hover:border-border transition-all duration-200",
|
"hover:bg-muted/80 hover:border-border/30 transition-all duration-200",
|
||||||
"text-sm font-medium text-foreground",
|
"text-sm font-medium text-foreground",
|
||||||
|
"focus-visible:ring-0 focus-visible:ring-offset-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -206,11 +207,14 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
className="w-[360px] p-0 rounded-xl shadow-lg border-border/50"
|
className="w-[360px] p-0 rounded-xl shadow-lg border-border/30"
|
||||||
align="start"
|
align="start"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
>
|
>
|
||||||
<Command shouldFilter={false} className="rounded-xl relative">
|
<Command
|
||||||
|
shouldFilter={false}
|
||||||
|
className="rounded-xl relative [&_[data-slot=command-input-wrapper]]:border-0 [&_[data-slot=command-input-wrapper]]:px-0 [&_[data-slot=command-input-wrapper]]:gap-2"
|
||||||
|
>
|
||||||
{/* Switching overlay */}
|
{/* Switching overlay */}
|
||||||
{isSwitching && (
|
{isSwitching && (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center bg-background/80 backdrop-blur-sm rounded-xl">
|
<div className="absolute inset-0 z-10 flex items-center justify-center bg-background/80 backdrop-blur-sm rounded-xl">
|
||||||
|
|
@ -221,8 +225,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center gap-2 border-b px-3 py-2 bg-muted/30">
|
<div className="flex items-center gap-2 border-b border-border/30 px-3 py-2">
|
||||||
<Bot className="size-4 text-muted-foreground" />
|
|
||||||
<CommandInput
|
<CommandInput
|
||||||
placeholder="Search models..."
|
placeholder="Search models..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
|
|
@ -300,7 +303,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{filteredGlobalConfigs.length > 0 && filteredUserConfigs.length > 0 && (
|
{filteredGlobalConfigs.length > 0 && filteredUserConfigs.length > 0 && (
|
||||||
<CommandSeparator className="my-1" />
|
<CommandSeparator className="my-1 bg-border/30" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* User Configs Section */}
|
{/* User Configs Section */}
|
||||||
|
|
@ -362,7 +365,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Add New Config Button */}
|
{/* Add New Config Button */}
|
||||||
<div className="p-2 border-t border-border/50 bg-muted/20">
|
<div className="p-2 bg-muted/20">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,8 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||||
<SheetContent side="left" className="w-80 p-0 flex flex-col">
|
<SheetContent side="left" className="w-80 p-0 flex flex-col border-0">
|
||||||
<SheetHeader className="mx-3 px-4 py-4 border-b space-y-3">
|
<SheetHeader className="mx-3 px-4 pt-4 pb-0 space-y-2">
|
||||||
<SheetTitle>{t("all_chats") || "All Chats"}</SheetTitle>
|
<SheetTitle>{t("all_chats") || "All Chats"}</SheetTitle>
|
||||||
<SheetDescription className="sr-only">
|
<SheetDescription className="sr-only">
|
||||||
{t("all_chats_description") || "Browse and manage all your chats"}
|
{t("all_chats_description") || "Browse and manage all your chats"}
|
||||||
|
|
@ -175,7 +175,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
|
||||||
placeholder={t("search_chats") || "Search chats..."}
|
placeholder={t("search_chats") || "Search chats..."}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="pl-9 pr-8 h-9"
|
className="pl-9 pr-8 h-9 border-0 focus-visible:ring-0 focus-visible:border-0 shadow-none"
|
||||||
/>
|
/>
|
||||||
{searchQuery && (
|
{searchQuery && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -193,7 +193,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
|
||||||
|
|
||||||
{/* Tab toggle for active/archived (only show when not searching) */}
|
{/* Tab toggle for active/archived (only show when not searching) */}
|
||||||
{!isSearchMode && (
|
{!isSearchMode && (
|
||||||
<div className="flex border-b mx-3">
|
<div className="flex border-b mx-3 -mt-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowArchived(false)}
|
onClick={() => setShowArchived(false)}
|
||||||
|
|
|
||||||
|
|
@ -159,8 +159,8 @@ export function AllNotesSidebar({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||||
<SheetContent side="left" className="w-80 p-0 flex flex-col">
|
<SheetContent side="left" className="w-80 p-0 flex flex-col border-0">
|
||||||
<SheetHeader className="mx-3 px-4 py-4 border-b space-y-3">
|
<SheetHeader className="mx-3 px-4 pt-4 pb-2 border-b space-y-2">
|
||||||
<SheetTitle>{t("all_notes") || "All Notes"}</SheetTitle>
|
<SheetTitle>{t("all_notes") || "All Notes"}</SheetTitle>
|
||||||
<SheetDescription className="sr-only">
|
<SheetDescription className="sr-only">
|
||||||
{t("all_notes_description") || "Browse and manage all your notes"}
|
{t("all_notes_description") || "Browse and manage all your notes"}
|
||||||
|
|
@ -174,7 +174,7 @@ export function AllNotesSidebar({
|
||||||
placeholder={t("search_notes") || "Search notes..."}
|
placeholder={t("search_notes") || "Search notes..."}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="pl-9 pr-8 h-9"
|
className="pl-9 pr-8 h-9 border-0 focus-visible:ring-0 focus-visible:border-0 shadow-none"
|
||||||
/>
|
/>
|
||||||
{searchQuery && (
|
{searchQuery && (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue