From cfa26a482bafdbbbfaebc8b7cc26591e5c899ed5 Mon Sep 17 00:00:00 2001 From: Urjit Chakraborty <135136842+urjitc@users.noreply.github.com> Date: Tue, 27 Jan 2026 01:50:41 -0500 Subject: [PATCH 1/6] commit 1 --- package.json | 1 + src/app/globals.css | 4 +- .../assistant-ui/AssistantDropzone.tsx | 12 +- .../AssistantTextSelectionManager.tsx | 6 +- .../SafeAssistantRuntimeProvider.tsx | 102 ------- .../assistant-ui/SpeechToTextButton.tsx | 10 +- .../assistant-ui/WorkspaceRuntimeProvider.tsx | 7 +- .../assistant-ui/assistant-loader.tsx | 6 +- src/components/assistant-ui/attachment.tsx | 49 ++-- src/components/assistant-ui/file.tsx | 235 +++++++++++++++ src/components/assistant-ui/image.tsx | 264 +++++++++++++++++ src/components/assistant-ui/markdown-text.tsx | 23 +- src/components/assistant-ui/reasoning.tsx | 275 ++++++++++++++++++ src/components/assistant-ui/sources.tsx | 148 ++++++++++ .../assistant-ui/thread-list-dropdown.tsx | 16 +- src/components/assistant-ui/thread.tsx | 69 +++-- src/components/chat/AppChatHeader.tsx | 6 +- src/components/pdf/PdfPanelHeader.tsx | 8 +- .../workspace-canvas/SidebarQuickActions.tsx | 10 +- .../workspace-canvas/WorkspaceContent.tsx | 8 +- .../workspace-canvas/WorkspaceHeader.tsx | 128 ++++---- .../workspace-canvas/WorkspaceSection.tsx | 23 +- ...se-blocknote-selection-context-provider.ts | 8 +- .../use-selected-actions-context-provider.ts | 8 +- .../ai/use-workspace-context-provider.ts | 8 +- 25 files changed, 1121 insertions(+), 313 deletions(-) delete mode 100644 src/components/assistant-ui/SafeAssistantRuntimeProvider.tsx create mode 100644 src/components/assistant-ui/file.tsx create mode 100644 src/components/assistant-ui/image.tsx create mode 100644 src/components/assistant-ui/reasoning.tsx create mode 100644 src/components/assistant-ui/sources.tsx diff --git a/package.json b/package.json index 42b00c54..740139cb 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "sonner": "^2.0.7", "streamdown": "^2.1.0", "tailwind-merge": "^3.4.0", + "tw-shimmer": "^0.4.3", "typewriter-effect": "^2.22.0", "zod": "^4.3.4", "zustand": "^5.0.9" diff --git a/src/app/globals.css b/src/app/globals.css index b32e7bfc..1224d48f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @import "tw-animate-css"; @import "@daveyplate/better-auth-ui/css"; +@import "tw-shimmer"; /* Path to your installed `@blocknote/shadcn` package. */ @source "../node_modules/@blocknote/shadcn"; @@ -247,8 +248,6 @@ margin-top: -0.125rem !important; } - - .bn-block-content[data-content-type="checkListItem"] input[type="checkbox"] { margin-top: 0.25rem !important; flex-shrink: 0 !important; @@ -257,7 +256,6 @@ cursor: pointer !important; } - .bn-block-content[data-content-type="checkListItem"] .bn-inline-content { margin-top: 0 !important; flex: 1 !important; diff --git a/src/components/assistant-ui/AssistantDropzone.tsx b/src/components/assistant-ui/AssistantDropzone.tsx index bf5e7ee7..150f44d5 100644 --- a/src/components/assistant-ui/AssistantDropzone.tsx +++ b/src/components/assistant-ui/AssistantDropzone.tsx @@ -1,7 +1,7 @@ "use client"; import { useDropzone } from "react-dropzone"; -import { useAssistantApi } from "@assistant-ui/react"; +import { useAui } from "@assistant-ui/react"; import { useWorkspaceStore } from "@/lib/stores/workspace-store"; import { Upload } from "lucide-react"; import { useCallback, useState, useRef } from "react"; @@ -16,7 +16,7 @@ interface AssistantDropzoneProps { * Accepts all supported file types and adds them as attachments to the chat composer. */ export function AssistantDropzone({ children }: AssistantDropzoneProps) { - const api = useAssistantApi(); + const aui = useAui(); const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId); const [isDragging, setIsDragging] = useState(false); @@ -31,7 +31,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) { const onDrop = useCallback( async (acceptedFiles: File[]) => { - if (!currentWorkspaceId || !api) return; + if (!currentWorkspaceId || !aui) return; const MAX_FILES = 10; const MAX_FILE_SIZE_MB = 10; @@ -93,7 +93,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) { // Add each file to the composer const addPromises = validFiles.map(async (file) => { try { - await api.composer().addAttachment(file); + await aui.composer().addAttachment(file); } catch (error) { console.error("Failed to add attachment:", error); // Remove from processing set on error so it can be retried @@ -124,7 +124,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) { }, 200); } }, - [api, currentWorkspaceId] + [aui, currentWorkspaceId] ); // Clear processing state when drag ends (user drags away or cancels) @@ -139,7 +139,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) { onDrop, noClick: true, // Don't trigger on click, only drag and drop noKeyboard: true, // Don't trigger on keyboard - disabled: !currentWorkspaceId || !api, // Disable if no workspace is selected or api is not available + disabled: !currentWorkspaceId || !aui, // Disable if no workspace is selected or api is not available accept: { 'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp'], 'video/*': ['.mp4', '.webm', '.avi', '.mov', '.mkv'], diff --git a/src/components/assistant-ui/AssistantTextSelectionManager.tsx b/src/components/assistant-ui/AssistantTextSelectionManager.tsx index cf701b64..8ecb4d03 100644 --- a/src/components/assistant-ui/AssistantTextSelectionManager.tsx +++ b/src/components/assistant-ui/AssistantTextSelectionManager.tsx @@ -15,7 +15,7 @@ import SelectableText, { import { useUIStore, selectReplySelections } from "@/lib/stores/ui-store"; import { getHighlightColorById } from "@/lib/utils/highlight-colors"; import { useWorkspaceStore } from "@/lib/stores/workspace-store"; -import { useAssistantState } from "@assistant-ui/react"; +import { useAuiState } from "@assistant-ui/react"; import { useQueryClient } from "@tanstack/react-query"; import { Dialog, @@ -105,8 +105,8 @@ export default function AssistantTextSelectionManager({ // This component is always rendered within AssistantRuntimeProvider, so the hook is safe to use // Use threadListItem.id if available, otherwise fall back to mainThreadId // Using safe hooks to handle race condition during thread switching (GitHub issue #2722) - const threadListItemId = useAssistantState(({ threadListItem }) => (threadListItem as any)?.id); - const mainThreadId = useAssistantState(({ threads }) => (threads as any)?.mainThreadId); + const threadListItemId = useAuiState(({ threadListItem }) => (threadListItem as any)?.id); + const mainThreadId = useAuiState(({ threads }) => (threads as any)?.mainThreadId); const currentThreadId = threadListItemId || mainThreadId; diff --git a/src/components/assistant-ui/SafeAssistantRuntimeProvider.tsx b/src/components/assistant-ui/SafeAssistantRuntimeProvider.tsx deleted file mode 100644 index 468bcef4..00000000 --- a/src/components/assistant-ui/SafeAssistantRuntimeProvider.tsx +++ /dev/null @@ -1,102 +0,0 @@ -"use client"; - -import { Component, ReactNode } from "react"; -import { AssistantRuntimeProvider } from "@assistant-ui/react"; -import { AssistantRuntime } from "@assistant-ui/react"; - -interface Props { - runtime: AssistantRuntime; - children: ReactNode; -} - -interface State { - hasError: boolean; - remountKey: number; -} - -export class SafeAssistantRuntimeProvider extends Component { - constructor(props: Props) { - super(props); - this.state = { hasError: false, remountKey: 0 }; - } - - static getDerivedStateFromError(error: unknown) { - return { hasError: true }; - } - - componentDidCatch(error: unknown, errorInfo: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - - // Check if this is a known transient runtime error - const isTransientError = - errorMessage.includes("tapLookupResources") || - errorMessage.includes("Resource not found") || - errorMessage.includes("ThreadListItemRuntime is not available"); - - if (isTransientError) { - console.warn("[SafeAssistantRuntimeProvider] Caught transient runtime error, attempting graceful recovery:", error); - // Attempt to recover by resetting error state and incrementing remount key after a short delay - // This allows the tree to re-mount with a fresh key, hopefully after the race condition passes - setTimeout(() => { - if (this.state.hasError) { - this.setState({ - hasError: false, - remountKey: this.state.remountKey + 1 - }); - } - }, 100); - } else { - console.error("[SafeAssistantRuntimeProvider] Unhandled error:", error, errorInfo); - // For non-transient errors, still try to recover after a longer delay - setTimeout(() => { - if (this.state.hasError) { - this.setState({ - hasError: false, - remountKey: this.state.remountKey + 1 - }); - } - }, 500); - } - } - - componentDidUpdate(prevProps: Props) { - // If runtime changed, try to recover from error state - if (prevProps.runtime !== this.props.runtime && this.state.hasError) { - this.setState({ - hasError: false, - remountKey: this.state.remountKey + 1 - }); - } - } - - render() { - if (this.state.hasError) { - // During error state, render a minimal invisible placeholder - // This prevents gray screen while avoiding hook errors from children - // The setTimeout in componentDidCatch will trigger recovery - return ( -