-
-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathChatWidgetShell.tsx
More file actions
96 lines (85 loc) · 3.46 KB
/
Copy pathChatWidgetShell.tsx
File metadata and controls
96 lines (85 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { type CSSProperties, useEffect } from "react";
import { Outlet, useParams } from "@tanstack/react-router";
import { useChatWidgetStore } from "../../chatWidgetStore";
import { useAppSettings } from "../../appSettings";
import { CommandPalette } from "../CommandPalette";
import { ScreenshotTool, ScreenshotButton } from "../ScreenshotTool";
import ThreadSidebar from "../Sidebar";
import { Sidebar, SidebarProvider, SidebarRail } from "../ui/sidebar";
import { ChatWidgetBubble } from "./ChatWidgetBubble";
import { ChatWidgetPanel } from "./ChatWidgetPanel";
const THREAD_SIDEBAR_WIDTH_STORAGE_KEY = "chat_thread_sidebar_width";
const THREAD_SIDEBAR_MIN_WIDTH = 13 * 16;
const THREAD_MAIN_CONTENT_MIN_WIDTH = 40 * 16;
/**
* Top-level orchestrator for the chat widget mode.
* Renders either the minimized bubble or the expanded panel with the
* full chat layout inside it.
*/
export function ChatWidgetShell() {
const mode = useChatWidgetStore((s) => s.mode);
const setLastThreadId = useChatWidgetStore((s) => s.setLastThreadId);
const { settings } = useAppSettings();
const sidebarBorderOpacity =
settings.sidebarOpacity >= 1 ? 1 : Math.max(settings.sidebarOpacity, 0.18);
// Track the current thread for the bubble to navigate back to.
const params = useParams({ strict: false }) as { threadId?: string };
useEffect(() => {
if (params.threadId) {
setLastThreadId(params.threadId);
}
}, [params.threadId, setLastThreadId]);
// Listen for notification taps to auto-expand.
useEffect(() => {
const expand = useChatWidgetStore.getState().expand;
const onNotificationTap = ((_event: CustomEvent<{ threadId?: string }>) => {
expand();
// Navigation to the thread is handled by the notification system.
}) as EventListener;
window.addEventListener("okcode:notification-tap", onNotificationTap);
return () => {
window.removeEventListener("okcode:notification-tap", onNotificationTap);
};
}, []);
const expanded = mode === "expanded";
return (
<>
{/* The bubble is always mounted but only visible when minimized */}
{!expanded && <ChatWidgetBubble />}
{/* Global utilities available in both modes */}
<CommandPalette />
<ScreenshotTool />
{!expanded && (
<div className="fixed bottom-4 right-4 z-50">
<ScreenshotButton />
</div>
)}
{/* Expanded panel with the full chat layout */}
<ChatWidgetPanel expanded={expanded}>
<SidebarProvider defaultOpen={false}>
<Sidebar
side="left"
collapsible="offcanvas"
className="border-r-2 border-border/60 bg-card/80 text-foreground backdrop-blur-sm shadow-[2px_0_12px_-4px_rgba(0,0,0,0.08)] dark:border-border/40 dark:bg-card/60 dark:shadow-[2px_0_16px_-4px_rgba(0,0,0,0.3)]"
style={
{
"--sidebar-background-opacity": settings.sidebarOpacity,
"--sidebar-border-opacity": sidebarBorderOpacity,
} as CSSProperties
}
resizable={{
minWidth: THREAD_SIDEBAR_MIN_WIDTH,
shouldAcceptWidth: ({ nextWidth, wrapper }) =>
wrapper.clientWidth - nextWidth >= THREAD_MAIN_CONTENT_MIN_WIDTH,
storageKey: THREAD_SIDEBAR_WIDTH_STORAGE_KEY,
}}
>
<ThreadSidebar />
<SidebarRail />
</Sidebar>
<Outlet />
</SidebarProvider>
</ChatWidgetPanel>
</>
);
}