Files
dataroom/features/ai/components/viewer-chat-provider.tsx
zeekay a15864b16b ci: amd64-only build (matches hanzo runner pool topology)
hanzo-build-linux-amd64 is the only ARC runner pool installed on
hanzo-k8s today (arm64 pool is paused until DigitalOcean ships
arm64 droplets, per ~/work/hanzo/.github/RUNNERS.md). The arm64
job was waiting on spark-hanzoai-arm64 — not provisioned in this
cluster — so every build sat in queue forever and the amd64 leg
got cancelled.

Set platforms: linux/amd64 on the reusable build call to skip
arm64 entirely. Re-add arm64 if/when DO ships arm64 droplets and
the spark runner pool comes back online.
2026-06-07 11:38:49 -07:00

177 lines
3.9 KiB
TypeScript

"use client";
import {
type ReactNode,
createContext,
useCallback,
useContext,
useState,
} from "react";
import { DataroomFolder } from "@prisma/client";
import { cn } from "@/lib/utils";
// ============================================================================
// Types
// ============================================================================
export interface DataroomDocumentForChat {
dataroomDocumentId: string;
id: string;
name: string;
folderId: string | null;
}
interface ViewerChatConfig {
dataroomId?: string;
dataroomName?: string;
documentId?: string;
documentName?: string;
linkId?: string;
viewId?: string;
viewerId?: string;
}
interface ViewerChatContextType {
// State
isOpen: boolean;
isEnabled: boolean;
config: ViewerChatConfig;
documents: DataroomDocumentForChat[];
folders: DataroomFolder[];
// Actions
open: () => void;
close: () => void;
toggle: () => void;
}
// ============================================================================
// Context
// ============================================================================
const ViewerChatContext = createContext<ViewerChatContextType | null>(null);
/**
* Hook to access chat state and actions.
* Throws if used outside ViewerChatProvider.
*/
export function useViewerChat() {
const context = useContext(ViewerChatContext);
if (!context) {
throw new Error("useViewerChat must be used within ViewerChatProvider");
}
return context;
}
/**
* Safe hook that returns null if not within ViewerChatProvider.
* Useful for components that may or may not be within a chat context.
*/
export function useViewerChatSafe() {
return useContext(ViewerChatContext);
}
// ============================================================================
// Provider
// ============================================================================
interface ViewerChatProviderProps {
children: ReactNode;
enabled?: boolean;
dataroomId?: string;
dataroomName?: string;
documentId?: string;
documentName?: string;
linkId?: string;
viewId?: string;
viewerId?: string;
documents?: DataroomDocumentForChat[];
folders?: DataroomFolder[];
}
export function ViewerChatProvider({
children,
enabled = false,
dataroomId,
dataroomName,
documentId,
documentName,
linkId,
viewId,
viewerId,
documents = [],
folders = [],
}: ViewerChatProviderProps) {
const [isOpen, setIsOpen] = useState(false);
const open = useCallback(() => setIsOpen(true), []);
const close = useCallback(() => setIsOpen(false), []);
const toggle = useCallback(() => setIsOpen((prev) => !prev), []);
const config: ViewerChatConfig = {
dataroomId,
dataroomName,
documentId,
documentName,
linkId,
viewId,
viewerId,
};
const value: ViewerChatContextType = {
isOpen,
isEnabled: enabled,
config,
documents,
folders,
open,
close,
toggle,
};
return (
<ViewerChatContext.Provider value={value}>
{children}
</ViewerChatContext.Provider>
);
}
// ============================================================================
// Layout Component - Applies padding when chat is open
// ============================================================================
interface ViewerChatLayoutProps {
children: ReactNode;
className?: string;
}
/**
* Layout wrapper that applies padding when chat panel is open.
* Use this to wrap content that should shrink when chat opens.
*/
export function ViewerChatLayout({
children,
className,
}: ViewerChatLayoutProps) {
const context = useViewerChatSafe();
// If not in provider or not enabled, just render children
if (!context || !context.isEnabled) {
return <>{children}</>;
}
return (
<div
className={cn(
"transition-all duration-300",
context.isOpen && "pr-[400px]",
className,
)}
>
{children}
</div>
);
}