Replace the current panel-based dashboard (Dockview with GettingStarted, Templates, Runtimes, Activity panels) with a minimal, conversational “Portal” — a single centered input that progressively reveals functionality. New users see a prompt and nothing else. Returning users see a whisper-quiet list of recent items. Setup happens inline when the user first tries to act without an API key.
#09090d or theme equivalent)PanelLeft and PanelBottom wrappers — the Portal renders full-bleed with only AppHeader above it┌──────────────────────────────────────────┐
│ NODETOOL Editor Chat │
│ │
│ │
│ │
│ What shall we build? │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Type here... [↑]│ │
│ └──────────────────────────────────┘ │
│ │
│ │
│ │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ NODETOOL Editor Chat │
│ │
│ │
│ Welcome back. │
│ What's next? │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Type here... [↑]│ │
│ └──────────────────────────────────┘ │
│ │
│ 💬 Summarize research papers 2h │
│ ⚡ Image Enhance Pipeline 5h │
│ 💬 Categorize emails 1d │
│ │
└──────────────────────────────────────────┘
updated_at descending for both workflows and threadsuseDashboardData (workflows) and useChatService (threads)/chat routeuseChatService directly (which hard-codes navigate() calls). Instead, Portal interacts with GlobalChatStore directly for message state and uses the chat message display components for rendering. The sendMessage, onNewThread, etc. functions are called on the store directly, and Portal manages its own view state without route changes.DEFAULT_MODEL from config/constants. User can change model after transition via the model selector that appears.SecretsStore.fetchSecrets() and checking if any known provider key names exist (e.g., OPENAI_API_KEY, ANTHROPIC_API_KEY)http://localhost:11434), show a green checkmark instead and proceed.SecretsStore.updateSecret()selectedModel is updated to a sensible default for the chosen provider (OpenAI → gpt-4o, Anthropic → claude-sonnet-4-20250514, Ollama → first available model). Then the original message is sent automatically./editor/{workflowId} via handleWorkflowClickonSelectThread(threadId), transition Portal to chatting state, render chat inlinePortal.tsx (replaces Dashboard.tsx for this route)Manages state: idle |
setup |
chatting |
idle: heading + input + recents (+ search overlay if typing matches)setup: chat layout with the setup flow rendered as an inline message. Intermediate state — transitions to chatting after provider is configured.chatting: chat messages + input at bottomidle state, not a separate stateuseChatService (which hard-codes navigation). Instead interacts with GlobalChatStore directly for message operations and thread management.useDashboardData for workflows/templates, useWorkflowActions for navigationsetup vs chattingidle state, calls store’s newThread(). The button appears as a subtle icon near the input in chatting state.PortalInput.tsxonSend, onSearchChange, showModelSelector, selectedModel, onModelChange, agentMode, onAgentModeToggleshowModelSelector is true: model dropdown + agent toggle appear inlineonSearchChangePortalRecents.tsxupdated_at field for sortingworkflows, threads, onWorkflowClick, onThreadClickPortalSetupFlow.tsxSecretsStore.updateSecret(), then triggers the pending message sendonComplete(provider), pendingMessageAppHeader — kept as-is, potentially with lighter styling on this routeGlobalChatStore — direct store access for message state, thread management, send operations (replaces useChatService which has hardcoded navigation)DockviewReact and all dockview infrastructureDashboardHeaderAddPanelDropdownLayoutMenuGettingStartedPanelWelcomePanelSetupPanelTemplatesPanelRuntimesPanelActivityPanelpanelConfig.ts usage (panels still exist for other contexts)panelComponents.tsx usagedefaultLayouts.ts usagePanelLeft and PanelBottom wrappers from the dashboard route definitionNote: These components are not deleted from the codebase — they may be used elsewhere or could be re-added. They are simply no longer imported or rendered by the dashboard route.
Portal
├── GlobalChatStore (direct Zustand store access, no useChatService)
│ ├── status, messages, threads, currentThreadId
│ ├── sendMessage(), newThread(), selectThread(), deleteThread()
│ └── progress, statusMessage, stopGeneration()
├── useDashboardData()
│ ├── sortedWorkflows
│ └── startTemplates (for search results)
├── useWorkflowActions()
│ ├── handleWorkflowClick
│ └── handleCreateNewWorkflow
├── SecretsStore
│ └── fetchSecrets() → check for known provider key names
└── selectedModel state (defaults to DEFAULT_MODEL, updated by setup flow or model selector)
IDLE ──[user types]──→ IDLE (search overlay appears if matches found)
IDLE ──[user sends message]──→ CHECK_PROVIDER (synchronous check, not a rendered state)
CHECK_PROVIDER ──[has provider key in secrets]──→ CHATTING
CHECK_PROVIDER ──[no provider keys]──→ SETUP
SETUP ──[key saved + model set]──→ CHATTING (sends pending message)
IDLE ──[clicks recent workflow]──→ NAVIGATE_TO_EDITOR (route change)
IDLE ──[clicks recent chat]──→ CHATTING (loads thread via GlobalChatStore)
CHATTING ──[new chat action]──→ IDLE (calls store.newThread(), resets view state)
Note: CHECK_PROVIDER is a synchronous transition check, not a rendered component state. The component state type is idle | setup | chatting.
useChatService error handling.useChatService error statesupdated_at, max 5DEFAULT_MODEL when no model explicitly selected