From 911a80eaed8018dd65719d4ac4088d32672cbfd2 Mon Sep 17 00:00:00 2001 From: Francis Eytan Dortort Date: Thu, 5 Feb 2026 14:07:26 -0500 Subject: [PATCH] feat(ui): add onboarding view for empty projects Replace the sterile three-panel empty state with a welcoming getting-started view showing action cards for creating a thread or configuring an AI provider. Only shown when a project has no threads and no documents. Co-Authored-By: Claude Opus 4.5 --- src/renderer/App.tsx | 34 +++++--- src/renderer/components/EmptyProjectView.tsx | 88 ++++++++++++++++++++ 2 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 src/renderer/components/EmptyProjectView.tsx diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index b11bbd9..1833f59 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -9,6 +9,7 @@ import { DocumentPanel } from './features/document/DocumentPanel' import { NewProjectDialog } from './features/project/NewProjectDialog' import { SettingsDialog } from './features/settings/SettingsDialog' import { ADRPromptDialog } from './features/document/ADRPromptDialog' +import { EmptyProjectView } from './components/EmptyProjectView' import { useUIStore } from './stores/uiStore' import { useProjectStore } from './stores/projectStore' import { useThreadStore } from './stores/threadStore' @@ -34,17 +35,22 @@ export function App() { const updateDocuments = useProjectStore((s) => s.updateDocuments) const updateThreads = useProjectStore((s) => s.updateThreads) + const threads = useThreadStore((s) => s.threads) const setThreads = useThreadStore((s) => s.setThreads) const addThread = useThreadStore((s) => s.addThread) const addMessage = useThreadStore((s) => s.addMessage) const addStreamingMessage = useThreadStore((s) => s.addStreamingMessage) const setStreamingMessageId = useThreadStore((s) => s.setStreamingMessageId) + const documents = useDocumentStore((s) => s.documents) const setDocuments = useDocumentStore((s) => s.setDocuments) const addDocument = useDocumentStore((s) => s.addDocument) const loadSettings = useSettingsStore((s) => s.loadSettings) + // Check if the active project has any content + const hasContent = threads.length > 0 || documents.length > 0 + // Listen for AI streaming chunks and completion via Electron IPC useEffect(() => { if (!window.keystoneIPC) return @@ -346,16 +352,24 @@ export function App() { {sidebarOpen && } {activeProject ? ( - - } - right={} - /> + hasContent ? ( + + } + right={} + /> + ) : ( + setShowSettings(true)} + /> + ) ) : (
diff --git a/src/renderer/components/EmptyProjectView.tsx b/src/renderer/components/EmptyProjectView.tsx new file mode 100644 index 0000000..7c0c439 --- /dev/null +++ b/src/renderer/components/EmptyProjectView.tsx @@ -0,0 +1,88 @@ +interface EmptyProjectViewProps { + projectName: string + onNewThread: () => void + onOpenSettings: () => void +} + +export function EmptyProjectView({ projectName, onNewThread, onOpenSettings }: EmptyProjectViewProps) { + return ( +
+
+

+ Get started with {projectName} +

+

+ Start by creating your first conversation thread, or configure an AI provider in settings. +

+ +
+ {/* Card 1 - Start a Conversation */} +
+
+
+ + + +
+

+ Start a Conversation +

+

+ Create a thread to discuss architecture, requirements, or design decisions with AI +

+ +
+
+ + {/* Card 2 - Configure AI Provider */} +
+
+
+ + + +
+

+ Configure AI Provider +

+

+ Connect an AI provider like OpenAI, Anthropic, or Google to enable AI-assisted conversations +

+ +
+
+
+
+
+ ) +}