"use client"; import type { ReactNode } from "react"; import { useCallback, useState } from "react"; import { useAui, useAuiState } from "@assistant-ui/react"; import { ActionIcon, Box, Button, Center, Group, Loader, Stack, Text, ThemeIcon, Tooltip } from "@mantine/core"; import { IconAlertTriangle, IconArrowsMaximize, IconMessage, IconRobot } from "@tabler/icons-react"; import { useI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "@homarr/widgets"; import classes from "./assistant-panel.module.css"; import { AssistantConversationSurface } from "./assistant-panel"; import { getPendingAssistantAction } from "./assistant-pending-action"; import { useAssistantPreferences, useHomarrAssistant } from "./assistant-context"; import { AssistantComposerSurfaceProvider } from "./assistant-runtime-provider"; export const AssistantBoardWidget = (props: WidgetComponentProps<"assistant">) => { const assistant = useHomarrAssistant(); if (!assistant.enabled) { return ; } return ( ); }; const EnabledAssistantBoardWidget = ({ options, width, height, isEditMode }: WidgetComponentProps<"assistant">) => { const t = useI18n(); const assistant = useHomarrAssistant(); const preferences = useAssistantPreferences(); const aui = useAui(); const currentThreadId = useAuiState((state) => state.threadListItem.remoteId); const messages = useAuiState((state) => state.thread.messages); const isLoading = useAuiState((state) => state.thread.isLoading); const latestAssistantMessage = messages.findLast((message) => message.role === "assistant"); const pendingAction = getPendingAssistantAction(latestAssistantMessage); const pinnedConversation = options.conversationMode === "pinned" ? options.conversation : null; const pinnedConversationIsActive = pinnedConversation === null || pinnedConversation.value === currentThreadId; const [switching, setSwitching] = useState(false); const [switchErrorConversationId, setSwitchErrorConversationId] = useState(null); const hasSwitchError = switchErrorConversationId === pinnedConversation?.value; const selectModel = useCallback( (modelId: string) => { preferences.setModelId(modelId); const threadListItem = aui.threadListItem(); if (!threadListItem.getState().remoteId) return; threadListItem.updateCustom({ ...threadListItem.getState().custom, modelId }); }, [aui, preferences], ); const openPinnedConversation = async () => { if (!pinnedConversation) return; setSwitchErrorConversationId(null); setSwitching(true); try { await aui.threads().switchToThread(pinnedConversation.value); } catch { setSwitchErrorConversationId(pinnedConversation.value); } finally { setSwitching(false); } }; if (options.conversationMode === "pinned" && pinnedConversation === null) { return ( ); } if (!pinnedConversationIsActive && pinnedConversation) { return ( ); } if (width < 300 || height < 280) { return ; } return ( ); }; const CompactAssistantWidget = ({ onOpen }: { onOpen: () => void }) => { const t = useI18n(); return ( {t("widget.assistant.compact.title")} {t("widget.assistant.compact.description")} ); }; interface AssistantWidgetStateProps { icon: typeof IconRobot; title: string; description: string; color?: string; children?: ReactNode; } const AssistantWidgetState = ({ icon: Icon, title, description, color = "red", children, }: AssistantWidgetStateProps) => (
{title} {description} {children}
); const UnavailableAssistantWidget = ({ description }: { description: string | null }) => { const t = useI18n(); return ( ); };