Dashboard → Portal Redesign

Summary

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.

Design Principles

Visual Structure

Layout

AppHeader

Idle State (New User)

┌──────────────────────────────────────────┐
│  NODETOOL          Editor   Chat         │
│                                          │
│                                          │
│                                          │
│        What shall we build?              │
│                                          │
│  ┌──────────────────────────────────┐    │
│  │ Type here...                  [↑]│    │
│  └──────────────────────────────────┘    │
│                                          │
│                                          │
│                                          │
└──────────────────────────────────────────┘

Idle State (Returning User)

┌──────────────────────────────────────────┐
│  NODETOOL          Editor   Chat         │
│                                          │
│                                          │
│        Welcome back.                     │
│        What's next?                      │
│                                          │
│  ┌──────────────────────────────────┐    │
│  │ Type here...                  [↑]│    │
│  └──────────────────────────────────┘    │
│                                          │
│  💬 Summarize research papers       2h   │
│  ⚡ Image Enhance Pipeline          5h   │
│  💬 Categorize emails               1d   │
│                                          │
└──────────────────────────────────────────┘

Determining New vs Returning User

Interaction Flows

Flow 1: User Sends a Message (Happy Path)

  1. User types in the input and presses send (Enter to send, Shift+Enter for newline)
  2. The heading (“What shall we build?”) fades out via CSS transition
  3. The input animates to the bottom of the viewport
  4. Chat messages render inline within the Portal component — we reuse the existing chat message components but render them directly, not via the /chat route
  5. Model selector and agent toggle appear inside the input bar (slide in)
  6. This is a state change within the Portal component — no route navigation. The Portal does NOT use useChatService 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.
  7. Default model for initial send: DEFAULT_MODEL from config/constants. User can change model after transition via the model selector that appears.

Flow 2: No API Key Configured (First-Time Setup)

  1. User types a message and sends it
  2. The portal transitions to chat layout (same as Flow 1)
  3. Instead of sending to the API, the component checks for configured providers by fetching secrets via SecretsStore.fetchSecrets() and checking if any known provider key names exist (e.g., OPENAI_API_KEY, ANTHROPIC_API_KEY)
  4. An “assistant” message appears with an inline provider picker — intentionally limited to three options for simplicity:
  5. User clicks a provider → inline API key input appears (except Ollama, see above)
  6. User enters key → key is saved via SecretsStore.updateSecret()
  7. After saving, the 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.
  8. No immediate key validation — if the key is bad, the provider error surfaces naturally in the chat stream.
  9. Small note: “You can add more providers later in settings”

Flow 3: Clicking a Recent Item

Components

New Components

Portal.tsx (replaces Dashboard.tsx for this route)

PortalInput.tsx

PortalRecents.tsx

PortalSetupFlow.tsx

Reused Components

Removed Components (from dashboard route)

Note: 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.

Transitions & Animation

Portal → Chat Transition

Search Results

Data Flow

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)

State Machine

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.

Error Handling

Testing Considerations