diff --git a/extensions/python/banners/_30_system_resources.py b/extensions/python/banners/_30_system_resources.py index 25c65a670..3c1949f4f 100644 --- a/extensions/python/banners/_30_system_resources.py +++ b/extensions/python/banners/_30_system_resources.py @@ -69,34 +69,37 @@ class SystemResourcesCheck(Extension): "priority": 10, "title": "System Resources", "html": ( - "
" - "
" - "
" - "
" - "
CPU
" - f"
{cpu_value}
" + "
" + "
" + "
" + "
CPU
" + f"
{cpu_value}
" "
" f"{cpu_bar}" "
" - "
" - "
" - "
RAM
" - f"
{ram_value}
" + "
" + "
" + "
RAM
" + f"
{ram_value}
" "
" f"{ram_bar}" "
" - "
" - "
" - "
Disk
" - f"
{disk_value}
" + "
" + "
" + "
Disk
" + f"
{disk_value}
" "
" f"{disk_bar}" "
" + "
" + "
" + "
Load (1/5/15)
" + f"
{load_value}
" + "
" + "
" + "
Net (since boot)
" + f"
{net_sent} sent / {net_recv} recv
" "
" - "
" - "
" - f"
Load (1/5/15)
{load_value}
" - f"
Net (since boot)
{net_sent} sent / {net_recv} recv
" "
" "
" ), @@ -117,9 +120,8 @@ class SystemResourcesCheck(Extension): color = "#22c55e" return ( - "
" - f"
" + "
" + f"" "
" ) diff --git a/plugins/_discovery/AGENTS.md b/plugins/_discovery/AGENTS.md index d2440d5ab..0a41f079b 100644 --- a/plugins/_discovery/AGENTS.md +++ b/plugins/_discovery/AGENTS.md @@ -15,6 +15,7 @@ - Discovery cards must be accurate, dismissible where appropriate, and avoid advertising already-complete setup. - CTA actions must match supported welcome-screen action contracts. - Keep card IDs unique and plugin-prefixed. +- The Welcome screen `welcome-actions-end` surface renders feature channel cards plus the compact OAuth account-provider card; other hero discovery cards stay out of the lower welcome grid. ## Work Guidance diff --git a/plugins/_discovery/extensions/python/banners/10_discovery_cards.py b/plugins/_discovery/extensions/python/banners/10_discovery_cards.py index c0bc5cdee..bba6c1c85 100644 --- a/plugins/_discovery/extensions/python/banners/10_discovery_cards.py +++ b/plugins/_discovery/extensions/python/banners/10_discovery_cards.py @@ -87,11 +87,11 @@ class DiscoveryCardsExtension(Extension): banners.append({ "id": "discovery-telegram", "type": "feature", - "title": "Connect Telegram", - "description": "Chat with Agent Zero from Telegram wherever you are.", + "title": "Telegram", + "description": "Chat on Telegram wherever you are.", "thumbnail": "/plugins/_discovery/webui/assets/thumb-telegram.png", "icon": "send", - "cta_text": "Setup", + "cta_text": "Connect", "cta_action": "open-plugin-config:_telegram_integration", "dismissible": True, "priority": 50, @@ -105,11 +105,11 @@ class DiscoveryCardsExtension(Extension): banners.append({ "id": "discovery-email", "type": "feature", - "title": "Setup Email", + "title": "Email", "description": "Let Agent Zero read and send emails on your behalf.", "thumbnail": "/plugins/_discovery/webui/assets/thumb-email.png", "icon": "mail", - "cta_text": "Open Setup", + "cta_text": "Connect", "cta_action": "open-plugin-config:_email_integration", "dismissible": True, "priority": 50, @@ -121,11 +121,11 @@ class DiscoveryCardsExtension(Extension): banners.append({ "id": "discovery-whatsapp", "type": "feature", - "title": "Connect WhatsApp", + "title": "WhatsApp", "description": "Send and receive WhatsApp messages through A0.", "thumbnail": "/plugins/_discovery/webui/assets/thumb-whatsapp.png", "icon": "chat", - "cta_text": "Setup", + "cta_text": "Connect", "cta_action": "open-plugin-config:_whatsapp_integration", "dismissible": True, "priority": 50, diff --git a/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html b/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html index fd68a9dc4..cf54f176b 100644 --- a/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html +++ b/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html @@ -19,14 +19,6 @@ @click="$store.discoveryStore.executeCta(card.cta_action)" @keydown.enter.prevent="$store.discoveryStore.executeCta(card.cta_action)" @keydown.space.prevent="$store.discoveryStore.executeCta(card.cta_action)"> -
@@ -40,8 +32,6 @@

-

-
diff --git a/plugins/_discovery/webui/discovery-store.js b/plugins/_discovery/webui/discovery-store.js index 520ba3ec4..8dbb52919 100644 --- a/plugins/_discovery/webui/discovery-store.js +++ b/plugins/_discovery/webui/discovery-store.js @@ -154,6 +154,10 @@ const model = { return this.cards.filter((card) => card.type === "hero" && card.placement === "after-features"); }, + get oauthAccountCards() { + return this.bottomHeroCards.filter((card) => card.id === "discovery-oauth-accounts"); + }, + get heroCards() { return [...this.topHeroCards, ...this.bottomHeroCards]; }, diff --git a/plugins/_whisper_stt/extensions/webui/chat-input-box-end/microphone-button.html b/plugins/_whisper_stt/extensions/webui/chat-input-box-end/microphone-button.html index c6fb47f3e..aed23ba3a 100644 --- a/plugins/_whisper_stt/extensions/webui/chat-input-box-end/microphone-button.html +++ b/plugins/_whisper_stt/extensions/webui/chat-input-box-end/microphone-button.html @@ -1,58 +1,75 @@
- \ No newline at end of file + diff --git a/webui/components/chat/input/input-store.js b/webui/components/chat/input/input-store.js index cc860420e..0ace742bf 100644 --- a/webui/components/chat/input/input-store.js +++ b/webui/components/chat/input/input-store.js @@ -56,6 +56,7 @@ const model = { chatMoreMenuOpen: false, progressText: "", progressActive: false, + _caretIndex: 0, toggleChatMoreMenu() { this.chatMoreMenuOpen = !this.chatMoreMenuOpen; @@ -76,12 +77,17 @@ const model = { }, get inputPlaceholder() { + if (!chatsStore.selected) return "Ask anything to start a new chat"; const state = this._getSendState(); if (state === "all") return "Press Enter to send queued messages"; if (this.showProgressPlaceholder) return ""; return "Type your message here..."; }, + get isCodeContext() { + return this._isCaretInsideFence(this.message, this._caretIndex); + }, + get showProgressPlaceholder() { return ( this._getSendState() !== "all" && @@ -102,7 +108,7 @@ const model = { const state = this._getSendState(); if (state === "all") return "send_and_archive"; if (state === "queue") return "schedule_send"; - return "send"; + return "arrow_forward"; }, // Computed: send button CSS class @@ -129,15 +135,59 @@ const model = { async sendMessage() { // Capture sent prompt to per-chat history (bash-style) try { this._pushHistory(this.message); } catch (_e) { /* ignore */ } + + if (!chatsStore.selected && (this.message.trim() || attachmentsStore?.attachments?.length > 0)) { + const ctxid = await chatsStore.newChat(); + if (!ctxid && !chatsStore.selected) return; + } + // Delegate to the global function if (globalThis.sendMessage) { await globalThis.sendMessage(); } }, - adjustTextareaHeight() { - const chatInput = document.getElementById("chat-input"); - if (chatInput) { + _composerTextareas(target = null) { + if (target?.tagName === "TEXTAREA") return [target]; + return ["chat-input"] + .map((id) => document.getElementById(id)) + .filter(Boolean); + }, + + _activeTextarea() { + const active = document.activeElement; + if (active?.id === "chat-input") { + return active; + } + return document.getElementById("chat-input"); + }, + + _syncCaretFromEvent($event = null) { + const ta = ($event && $event.target?.tagName === "TEXTAREA") + ? $event.target + : this._activeTextarea(); + if (!ta) { + this._caretIndex = this.message.length; + return; + } + this._caretIndex = Number.isFinite(ta.selectionStart) ? ta.selectionStart : this.message.length; + }, + + _isCaretInsideFence(text, index) { + const source = String(text || ""); + const caret = Math.max(0, Math.min(source.length, Number(index) || 0)); + const matches = source.slice(0, caret).match(/```/g); + return Boolean(matches && matches.length % 2 === 1); + }, + + handleInput($event) { + this._syncCaretFromEvent($event); + this.adjustTextareaHeight($event); + }, + + adjustTextareaHeight($event = null) { + const target = $event?.target || null; + for (const chatInput of this._composerTextareas(target)) { if (!this.message) chatInput.value = ""; chatInput.style.height = "auto"; chatInput.style.height = chatInput.scrollHeight + "px"; @@ -284,7 +334,7 @@ const model = { }, focus() { - const chatInput = document.getElementById("chat-input"); + const chatInput = this._activeTextarea(); if (chatInput) { chatInput.focus(); } @@ -379,10 +429,11 @@ const model = { _setCaretStart() { queueMicrotask(() => { - const ta = document.getElementById("chat-input"); + const ta = this._activeTextarea(); if (ta) { try { ta.setSelectionRange(0, 0); } catch (_e) { /* ignore */ } try { ta.scrollTop = 0; } catch (_e) { /* ignore */ } + this._syncCaretFromEvent({ target: ta }); } this.adjustTextareaHeight(); }); @@ -390,11 +441,12 @@ const model = { _setCaretEnd() { queueMicrotask(() => { - const ta = document.getElementById("chat-input"); + const ta = this._activeTextarea(); if (ta) { const end = ta.value.length; try { ta.setSelectionRange(end, end); } catch (_e) { /* ignore */ } try { ta.scrollTop = ta.scrollHeight; } catch (_e) { /* ignore */ } + this._syncCaretFromEvent({ target: ta }); } this.adjustTextareaHeight(); }); @@ -402,7 +454,7 @@ const model = { historyPrev($event) { if ($event && ($event.isComposing || $event.keyCode === 229)) return; - const ta = ($event && $event.target) ? $event.target : document.getElementById("chat-input"); + const ta = ($event && $event.target) ? $event.target : this._activeTextarea(); if (!ta) return; const start = ta.selectionStart; const end = ta.selectionEnd; @@ -424,7 +476,7 @@ const model = { historyNext($event) { if ($event && ($event.isComposing || $event.keyCode === 229)) return; - const ta = ($event && $event.target) ? $event.target : document.getElementById("chat-input"); + const ta = ($event && $event.target) ? $event.target : this._activeTextarea(); if (!ta) return; const value = ta.value; const start = ta.selectionStart; @@ -447,6 +499,7 @@ const model = { this.message = ""; attachmentsStore.clearAttachments(); this.chatMoreMenuOpen = false; + this._caretIndex = 0; this._historyIndex = null; this._draft = ""; this.adjustTextareaHeight(); diff --git a/webui/components/sidebar/chats/chats-store.js b/webui/components/sidebar/chats/chats-store.js index 1469c08d2..4dc563ac8 100644 --- a/webui/components/sidebar/chats/chats-store.js +++ b/webui/components/sidebar/chats/chats-store.js @@ -210,12 +210,13 @@ const model = { if (response.ok) { await this.selectChat(response.ctxid); document.dispatchEvent(new CustomEvent("chat-created", { detail: { ctxid: response.ctxid } })); - return; + return response.ctxid; } } catch (e) { toastFetchError("Error creating new chat", e); } + return null; }, deselectChat(){ diff --git a/webui/components/welcome/AGENTS.md b/webui/components/welcome/AGENTS.md index 4237418aa..624209b3b 100644 --- a/webui/components/welcome/AGENTS.md +++ b/webui/components/welcome/AGENTS.md @@ -14,10 +14,13 @@ - Keep banner and discovery card behavior compatible with Python `banners` extensions. - Supported banner/card CTA actions must stay synchronized with plugin discovery contracts. - Do not show setup prompts for already configured plugins when backend status can prevent it. +- The welcome screen mounts the shared chat composer to start a new chat; keep it mutually exclusive with the normal chat input DOM. +- Render `system-resources` as the dedicated System Resources panel, not as a generic alert banner. ## Work Guidance - Coordinate visual card or CTA changes with plugin discovery and onboarding surfaces. +- Keep the lower welcome grid focused on Connect Channels, OAuth accounts, and System Resources unless the target design changes. ## Verification diff --git a/webui/components/welcome/welcome-screen.html b/webui/components/welcome/welcome-screen.html index 95a4192c3..d27472373 100644 --- a/webui/components/welcome/welcome-screen.html +++ b/webui/components/welcome/welcome-screen.html @@ -12,534 +12,598 @@
-
- -
- -
- add_circle -

New Chat

-
-
- folder -

Projects

-
-
- memory -

Memory

-
+
+
+

Welcome back

+

Agent Zero is ready to help.

+
-
- schedule -

Tasks

-
-
- settings -

Settings

-
-
- extension -

Plugins

-
+
+ + +
-
- folder_open -

Files

+
+
+ + +
-
- language -

Visit Website

-
- -
- - + + +
+

Quick Actions

+
+ + + + + + + + +
+
+ +
+ + +
+
+

+ monitoring +
+
+
+
+
- -
- - - -
-
diff --git a/webui/components/welcome/welcome-store.js b/webui/components/welcome/welcome-store.js index 13fc5d730..521b94dbf 100644 --- a/webui/components/welcome/welcome-store.js +++ b/webui/components/welcome/welcome-store.js @@ -1,5 +1,4 @@ import { createStore } from "/js/AlpineStore.js"; -import { getContext } from "/index.js"; import { store as chatsStore } from "/components/sidebar/chats/chats-store.js"; import { store as memoryStore } from "/plugins/_memory/webui/memory-dashboard-store.js"; import { store as projectsStore } from "/components/projects/projects-store.js"; @@ -143,10 +142,15 @@ const model = { get sortedBanners() { return [...this.banners] + .filter((b) => b.id !== "system-resources") .filter((b) => b.type !== "hero" && b.type !== "feature") .sort((a, b) => (b.priority || 0) - (a.priority || 0)); }, + get systemResourceBanner() { + return this.banners.find((b) => b.id === "system-resources") || null; + }, + /** * Dismiss a banner by ID. * diff --git a/webui/index.html b/webui/index.html index 5a7889211..d7a2ee70c 100644 --- a/webui/index.html +++ b/webui/index.html @@ -96,8 +96,12 @@ -
- +
+
@@ -135,11 +139,15 @@
-
- - - - +
+
diff --git a/webui/index.js b/webui/index.js index 9320ad064..de5b64bc3 100644 --- a/webui/index.js +++ b/webui/index.js @@ -14,6 +14,7 @@ import { store as chatTopStore } from "/components/chat/top-section/chat-top-sto import { store as _tooltipsStore } from "/components/tooltips/tooltip-store.js"; import { store as messageQueueStore } from "/components/chat/message-queue/message-queue-store.js"; import { store as syncStore } from "/components/sync/sync-store.js" +import { store as welcomeStore } from "/components/welcome/welcome-store.js"; import { getUserHour12, getUserTimezone } from "/js/time-utils.js"; globalThis.fetchApi = api.fetchApi; // TODO - backward compatibility for non-modular scripts, remove once refactored to alpine @@ -58,7 +59,7 @@ export async function sendMessage() { if (message || hasAttachments) { // Check if agent is busy - queue instead of sending - if (chatsStore.selectedContext.running || messageQueueStore.hasQueue) { + if (chatsStore.selectedContext?.running || messageQueueStore.hasQueue) { const success = messageQueueStore.addToQueue(message, attachmentsWithUrls); // no await for the queue // if (success) { @@ -231,12 +232,7 @@ globalThis.loadKnowledge = async function () { }; function adjustTextareaHeight() { - const chatInputEl = document.getElementById("chat-input"); - if (chatInputEl) { - if (!inputStore.message) chatInputEl.value = ""; - chatInputEl.style.height = "auto"; - chatInputEl.style.height = chatInputEl.scrollHeight + "px"; - } + inputStore.adjustTextareaHeight(); } export const sendJsonData = async function (url, data) { @@ -593,7 +589,7 @@ export const deselectChat = function () { sessionStorage.removeItem("lastSelectedTask"); // Clear the chat history - chatHistory.innerHTML = ""; + if (chatHistory) chatHistory.innerHTML = ""; }; globalThis.deselectChat = deselectChat;