From 1a2e897c794994e489fad86768308b99e1ddbd8c Mon Sep 17 00:00:00 2001
From: Pragadeesh122
Date: Fri, 19 Jun 2026 20:10:11 -0500
Subject: [PATCH 1/2] feat(onboarding): tighten first-run empty states and
upload feedback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
First-run flow improvements for the create-project -> upload -> first
grounded answer path (JOB-5):
- Empty-project chat area now guides the user to upload their first
document (working drag/click dropzone) instead of showing generic
tool prompts; shows an "indexing" state and surfaces upload errors.
- Once documents are indexed, the empty state suggests grounded
questions (summarize / ask about your documents).
- Upload failures are surfaced inline (type/size pre-validation mirrors
the backend) instead of failing silently to the console.
- Per-document status line now explains "Indexing…" and shows failure
reasons.
- No-projects sidebar state is now a real CTA ("Create your first
project") and project creation shows a spinner + inline errors.
- Fixed an incorrect "Up to 25 MB" hint — the backend allows 100 MB;
added a shared lib/upload.ts so client limits track the backend.
Co-Authored-By: Paperclip
---
frontend/components/ChatArea.tsx | 151 ++++++++++++++++++++++++-
frontend/components/ProjectPage.tsx | 19 ++++
frontend/components/ProjectSidebar.tsx | 45 ++++++--
frontend/components/Sidebar.tsx | 72 ++++++++----
frontend/lib/upload.ts | 32 ++++++
5 files changed, 290 insertions(+), 29 deletions(-)
create mode 100644 frontend/lib/upload.ts
diff --git a/frontend/components/ChatArea.tsx b/frontend/components/ChatArea.tsx
index bd58ca0..b579dc3 100644
--- a/frontend/components/ChatArea.tsx
+++ b/frontend/components/ChatArea.tsx
@@ -6,8 +6,14 @@ import { BookOpenText } from '@phosphor-icons/react/dist/ssr/BookOpenText';
import { Table } from '@phosphor-icons/react/dist/ssr/Table';
import { Compass } from '@phosphor-icons/react/dist/ssr/Compass';
import { Sparkle } from '@phosphor-icons/react/dist/ssr/Sparkle';
+import { FileArrowUp } from '@phosphor-icons/react/dist/ssr/FileArrowUp';
+import { SpinnerGap } from '@phosphor-icons/react/dist/ssr/SpinnerGap';
+import { WarningCircle } from '@phosphor-icons/react/dist/ssr/WarningCircle';
+import { ListBullets } from '@phosphor-icons/react/dist/ssr/ListBullets';
+import { Question } from '@phosphor-icons/react/dist/ssr/Question';
import { ThreadPrimitive } from '@assistant-ui/react';
import type { ChatAttachment, Message, ProjectDocument } from '@/lib/types';
+import { PROJECT_DOC_ACCEPT, PROJECT_DOC_MAX_MB, PROJECT_DOC_SUPPORTED_LABEL } from '@/lib/upload';
import MessageBubble from './MessageBubble';
import ChatInput from './ChatInput';
@@ -40,6 +46,31 @@ const SUGGESTIONS: SuggestionCard[] = [
},
];
+// Shown once a project has at least one indexed document — orients the user
+// toward their first grounded answer instead of generic tool prompts.
+const DOC_SUGGESTIONS: SuggestionCard[] = [
+ {
+ label: 'Summarize the key points',
+ query: 'Summarize the key points from my documents.',
+ icon: ,
+ },
+ {
+ label: 'Ask a question about your documents',
+ query: 'Based on my documents, ',
+ icon: ,
+ },
+ {
+ label: 'Search the knowledge base',
+ query: 'Search the knowledge base for information about ',
+ icon: ,
+ },
+ {
+ label: 'Browse the web',
+ query: 'Browse the web and find information about ',
+ icon: ,
+ },
+];
+
// RunaxAI logo: interconnected network graph with central pulse spark.
// Emerald palette to match locked brand accent (skill §3 Rule 2).
export function RunaxLogo({ size = 40 }: { size?: number }) {
@@ -93,6 +124,9 @@ interface ChatAreaProps {
onStop?: () => void;
projectId?: string;
projectDocuments?: ProjectDocument[];
+ onUploadFile?: (file: File) => void;
+ isUploading?: boolean;
+ uploadError?: string | null;
attachments?: ChatAttachment[];
onAttachmentsChange?: (next: ChatAttachment[]) => void;
sessionFileCount?: number;
@@ -110,12 +144,17 @@ export default function ChatArea({
onStop,
projectId,
projectDocuments,
+ onUploadFile,
+ isUploading = false,
+ uploadError,
attachments,
onAttachmentsChange,
sessionFileCount = 0,
sessionBytes = 0,
}: ChatAreaProps) {
const inputRef = useRef(null);
+ const heroFileInputRef = useRef(null);
+ const [heroDragOver, setHeroDragOver] = useState(false);
// Keyboard shortcuts: / to focus input, Escape to blur
useEffect(() => {
@@ -227,6 +266,25 @@ export default function ChatArea({
const isEmpty = messages.length === 0;
+ // Project-aware first-run state. In a project context we guide the user from
+ // an empty project -> first upload -> first grounded answer.
+ const isProjectContext = Boolean(projectId);
+ const docs = projectDocuments ?? [];
+ const readyDocs = docs.filter((d) => d.status === 'ready');
+ const indexingDocs = docs.filter(
+ (d) => d.status === 'processing' || d.status === 'uploading'
+ );
+ const needsFirstDocument =
+ isProjectContext && Boolean(onUploadFile) && readyDocs.length === 0;
+ const suggestions = readyDocs.length > 0 ? DOC_SUGGESTIONS : SUGGESTIONS;
+
+ const handleHeroFile = useCallback(
+ (file: File | undefined) => {
+ if (file && onUploadFile) onUploadFile(file);
+ },
+ [onUploadFile]
+ );
+
return (
{/* Messages area */}
@@ -237,7 +295,90 @@ export default function ChatArea({
aria-live="polite"
aria-label="Chat messages"
>
- {isEmpty ? (
+ {isEmpty && needsFirstDocument ? (
+ /* First-run: empty project — guide the user to upload their first doc */
+
+
+
+
+
+
+
+
+ {indexingDocs.length > 0
+ ? 'Indexing your document…'
+ : 'Add a document to get started'}
+
+
+ {indexingDocs.length > 0
+ ? 'This usually takes a few seconds. You can start chatting as soon as it’s ready.'
+ : `Upload a ${PROJECT_DOC_SUPPORTED_LABEL} file and RunaxAI will answer questions grounded in it.`}
+
+ Prefer to explore first? You can also just start chatting below — RunaxAI can
+ search the web and query databases without a document.
+
+
+
+ ) : isEmpty ? (
/* Welcome / empty state */
@@ -251,14 +392,18 @@ export default function ChatArea({
RunaxAI
- AI with full tool access — document search, database queries, and web browsing
+ {readyDocs.length > 0
+ ? `Ask anything about your ${readyDocs.length} indexed document${
+ readyDocs.length === 1 ? '' : 's'
+ } — or use web search and database queries.`
+ : 'AI with full tool access — document search, database queries, and web browsing'}