-- allium: 1 -- bDS Chat Panel -- Scope: UI content area — AI chat surface -- Distilled from: ChatPanel.tsx -- Describes the layout and behaviour of the chat panel. use "./i18n.allium" as i18n -- ─── Chat panel ─────────────────────────────────────────────── value ChatPanelView { conversation_id: String? needs_api_key: Boolean title: String -- conversation title or "New Chat" selected_model_id: String? messages: List is_streaming: Boolean input_text: String } value ChatMessage { role: String -- user | assistant | system content: String -- user: plain text; assistant: GFM markdown tool_markers: List is_streaming: Boolean -- true while accumulating } value ToolMarker { tool_name: String args_preview: String -- string args truncated to config.chat_tool_args_max_length is_complete: Boolean -- checkmark when done, dot when in-progress } value ModelSelectorDropdown { groups: List selected_model_id: String? } surface ModelSelectorDropdownSurface { context dropdown: ModelSelectorDropdown exposes: dropdown.selected_model_id when dropdown.selected_model_id != null for group in dropdown.groups: group.provider_name for model in group.models: model.model_id model.display_name model.context_window model.max_output_tokens } value ModelProviderGroup { provider_name: String -- e.g. "OpenAI", "Ollama", "LM Studio" models: List } value ModelEntry { model_id: String display_name: String context_window: Integer max_output_tokens: Integer } config { chat_tool_args_max_length: Integer = 30 chat_input_max_height: Integer = 200 } surface ChatPanelSurface { context panel: ChatPanelView exposes: panel.needs_api_key panel.title panel.selected_model_id panel.is_streaming panel.input_text for msg in panel.messages: msg.role msg.content msg.is_streaming for tm in msg.tool_markers: tm.tool_name tm.args_preview tm.is_complete provides: ChatSendMessage(panel.conversation_id, panel.input_text) when panel.input_text != "" and not panel.is_streaming ChatAbortStreaming(panel.conversation_id) when panel.is_streaming ChatSelectModel(panel.conversation_id, model_id) ChatOpenSettings() when panel.needs_api_key @guarantee ApiKeyRequiredScreen -- Shown when needs_api_key is true. -- Key icon, title, description text, "Open Settings" button. -- No chat functionality available until API key is set. @guarantee HeaderLayout -- Left: conversation title (or "New Chat"), CSS ellipsis on overflow. -- Right: model selector button opening dropdown. @guarantee ModelSelectorDropdown -- Dropdown groups models by provider (section headers). -- Each entry: model display name. -- Expandable details: context window, max output tokens. -- Selection is per-conversation override, persisted with conversation. -- Changing model mid-conversation applies to subsequent messages only. @guarantee WelcomeScreen -- Shown when no messages and not streaming. -- Robot icon, title, description, 5 tip bullet points. @guarantee MessageRendering -- User messages: plain text. -- Assistant messages: rendered as GFM Markdown. -- External images blocked (CSP), shown as links. -- Tool markers: checkmark (done) or dot (in-progress) icon, -- tool name, args truncated to config.chat_tool_args_max_length for strings. -- Streaming: accumulating markdown + tool markers, thinking dots animation. @guarantee AutoScroll -- Message area auto-scrolls to bottom on new messages. @guarantee InputArea -- Abort/Stop button: square stop icon, visible only during streaming. -- Auto-growing textarea: max config.chat_input_max_height px. -- Enter sends message. Shift+Enter inserts newline. -- Send button: up-arrow icon, disabled when input is empty or streaming. @guarantee AssistantActionDispatch -- Assistant tool calls can trigger navigation actions: -- open_post(id), open_media(id), open_settings(), etc. -- Actions dispatched through store, same as user clicks. -- Navigation actions open tabs with pin intent. @guarantee TokenTracking -- Token usage tracked per conversation. -- Displayed in status bar, not in the chat panel itself. }