diff --git a/plugins/_discovery/extensions/python/banners/10_discovery_cards.py b/plugins/_discovery/extensions/python/banners/10_discovery_cards.py
index bba6c1c85..460f52ee8 100644
--- a/plugins/_discovery/extensions/python/banners/10_discovery_cards.py
+++ b/plugins/_discovery/extensions/python/banners/10_discovery_cards.py
@@ -138,7 +138,7 @@ class DiscoveryCardsExtension(Extension):
"type": "hero",
"placement": "after-features",
"title": "Your AI accounts",
- "description": "Link account-backed providers such as ChatGPT/Codex, GitHub Copilot, Gemini API, and xAI Grok."
+ "description": "Use your subscription-backed logins for model access."
if not connected_count
else "",
"cta_text": "Connect accounts" if not connected_count else "Manage accounts",
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 cf54f176b..c7ca47b30 100644
--- a/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html
+++ b/plugins/_discovery/extensions/webui/onboarding-success-end/discovery-cards.html
@@ -276,8 +276,8 @@
.discovery-feature-thumb {
flex: 0 0 auto;
- width: 44px;
- height: 44px;
+ width: 32px;
+ height: 32px;
display: flex;
align-items: center;
justify-content: center;
@@ -570,8 +570,8 @@
}
.discovery-feature-thumb {
- width: 46px;
- height: 46px;
+ width: 32px;
+ height: 32px;
}
.discovery-feature-desc {
diff --git a/plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html b/plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html
index c0c9b6810..1d6ad7ca9 100644
--- a/plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html
+++ b/plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html
@@ -11,37 +11,35 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -58,11 +56,16 @@
@@ -95,12 +98,6 @@
-
@@ -135,15 +132,23 @@
.discovery-features-panel {
min-height: 100%;
- padding: 1.7rem 1.55rem 1.55rem;
+ padding: 1.25rem 1.45rem;
}
.discovery-panel-header {
display: grid;
- gap: 0.42rem;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 0.7rem;
+ align-items: start;
margin-bottom: 1.28rem;
}
+ .discovery-panel-copy {
+ display: grid;
+ gap: 0.42rem;
+ min-width: 0;
+ }
+
.discovery-panel-header h2 {
margin: 0;
color: var(--color-text);
@@ -162,31 +167,32 @@
.discovery-features {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.7rem;
+ gap: 0.65rem;
}
.discovery-feature-card {
- position: relative;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- text-align: left;
- }
-
- .discovery-feature-head {
- display: flex;
- flex-direction: row;
+ display: inline-flex;
align-items: center;
- gap: 0.75rem;
- width: 100%;
+ gap: 0.5rem;
min-width: 0;
- margin-bottom: 1rem;
+ min-height: 3.1rem;
+ border: 1px solid color-mix(in srgb, var(--color-border) 52%, transparent);
+ border-radius: 8px;
+ padding: 0.56rem 0.58rem;
+ background: color-mix(in srgb, var(--color-background) 50%, transparent);
+ color: inherit;
+ cursor: pointer;
+ font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
+ text-align: left;
+ transition:
+ border-color 0.18s ease,
+ background-color 0.18s ease;
}
.discovery-feature-thumb {
flex: 0 0 auto;
- width: 52px;
- height: 52px;
+ width: 32px;
+ height: 32px;
display: flex;
align-items: center;
justify-content: center;
@@ -212,34 +218,15 @@
flex: 1 1 auto;
min-width: 0;
color: var(--color-text);
- font-size: 0.96rem;
+ font-size: 0.84rem;
font-weight: 520;
line-height: 1.25;
}
- .discovery-feature-desc {
- margin: 1.12rem 0 0;
- padding-bottom: var(--spacing-md);
- color: color-mix(in srgb, var(--color-text) 72%, transparent);
- font-size: 0.86rem;
- line-height: 1.48;
- }
-
- .discovery-feature-card > .btn {
- margin-top: auto;
- min-width: 6.1rem;
- min-height: 2.35rem;
- border: 0;
- border-radius: 8px;
- color: #fff;
- font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
- font-size: 0.88rem;
- font-weight: 460;
- }
-
- .discovery-feature-card > .btn:hover,
- .discovery-feature-card > .btn:focus-visible {
- filter: brightness(1.06);
+ .discovery-feature-card:hover,
+ .discovery-feature-card:focus-visible {
+ border-color: color-mix(in srgb, var(--color-border) 88%, var(--color-text) 12%);
+ background: color-mix(in srgb, var(--color-panel) 86%, var(--color-background) 14%);
outline: none;
}
@@ -272,10 +259,12 @@
height: 26px;
}
- .discovery-feature-card:hover .discovery-dismiss,
- .discovery-feature-card:focus-visible .discovery-dismiss,
- .discovery-account-card:hover .discovery-dismiss,
- .discovery-account-card:focus-within .discovery-dismiss {
+ .discovery-panel-dismiss {
+ position: static;
+ opacity: 0.68;
+ }
+
+ .discovery-account-card > .discovery-dismiss {
opacity: 0.68;
}
@@ -294,32 +283,19 @@
.discovery-account-card {
position: relative;
display: grid;
- gap: 1.1rem;
- padding: 1.35rem 1.45rem 1.4rem;
+ gap: 1rem;
+ padding: 1.25rem 1.45rem 1.25rem;
}
.discovery-account-header {
display: grid;
- grid-template-columns: auto minmax(0, 1fr);
+ grid-template-columns: minmax(0, 1fr) auto;
gap: 0.95rem;
align-items: center;
min-width: 0;
padding-right: 2.1rem;
}
- .discovery-account-icon {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 3rem;
- height: 3rem;
- border-radius: 10px;
- color: #2f6bff;
- background: color-mix(in srgb, var(--color-primary) 10%, transparent);
- box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 12%, transparent);
- font-size: 1.7rem;
- }
-
.discovery-account-copy {
display: grid;
gap: 0.3rem;
@@ -357,7 +333,7 @@
min-height: 2rem;
padding: 0.32rem 0.58rem;
border: 1px solid color-mix(in srgb, var(--color-border) 52%, transparent);
- border-radius: 999px;
+ border-radius: 8px;
background: color-mix(in srgb, var(--color-background) 50%, transparent);
color: color-mix(in srgb, var(--color-text) 74%, transparent);
font-size: 0.8rem;
@@ -464,7 +440,7 @@
}
.discovery-account-cta {
- justify-self: start;
+ justify-self: end;
display: inline-flex;
align-items: center;
justify-content: center;
@@ -479,6 +455,7 @@
font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
font-size: 0.88rem;
font-weight: 460;
+ white-space: nowrap;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
@@ -536,33 +513,14 @@
}
.discovery-feature-card {
- display: grid;
- grid-template-columns: minmax(0, 1fr) auto;
- gap: 0.75rem;
+ display: inline-flex;
min-height: 0;
- align-items: center;
- }
-
- .discovery-feature-head {
- grid-column: 1;
- min-width: 0;
- margin-bottom: 0;
+ padding: 0.62rem 0.7rem;
}
.discovery-feature-thumb {
- width: 46px;
- height: 46px;
- }
-
- .discovery-feature-desc {
- display: none;
- }
-
- .discovery-feature-card > .btn {
- grid-column: 2;
- grid-row: 1;
- align-self: center;
- margin-top: 0;
+ width: 32px;
+ height: 32px;
}
.discovery-account-usage-row {
@@ -577,12 +535,7 @@
}
.discovery-feature-card {
- grid-template-columns: minmax(0, 1fr) auto;
- padding-right: 3rem;
- }
-
- .discovery-feature-card > .btn {
- justify-self: end;
+ padding-right: 0.7rem;
}
.discovery-account-card {
@@ -594,11 +547,11 @@
padding-right: 2.2rem;
}
- .discovery-account-icon {
- width: 2.55rem;
- height: 2.55rem;
- font-size: 1.45rem;
+ .discovery-account-cta {
+ grid-column: 1 / -1;
+ justify-self: start;
}
+
}
diff --git a/plugins/_discovery/webui/discovery-store.js b/plugins/_discovery/webui/discovery-store.js
index 8dbb52919..2feb1756f 100644
--- a/plugins/_discovery/webui/discovery-store.js
+++ b/plugins/_discovery/webui/discovery-store.js
@@ -64,6 +64,25 @@ const model = {
this.hasDismissedCards = true;
},
+ dismissFeatureCards() {
+ const featureIds = this.featureCards
+ .filter((card) => card.dismissible !== false)
+ .map((card) => card.id)
+ .filter(Boolean);
+
+ if (!featureIds.length) return;
+
+ const dismissed = this._getDismissedIds();
+ const dismissSet = new Set(featureIds);
+ for (const id of dismissSet) {
+ dismissed.add(id);
+ }
+ this._persistDismissedIds(dismissed);
+
+ this.cards = this.cards.filter((card) => !dismissSet.has(card.id));
+ this.hasDismissedCards = true;
+ },
+
undismissCards() {
localStorage.removeItem(STORAGE_KEY);
this.refreshCards();
diff --git a/plugins/_model_config/extensions/python/banners/_20_missing_api_key.py b/plugins/_model_config/extensions/python/banners/_20_missing_api_key.py
index c5722435c..1d58b3d8e 100644
--- a/plugins/_model_config/extensions/python/banners/_20_missing_api_key.py
+++ b/plugins/_model_config/extensions/python/banners/_20_missing_api_key.py
@@ -7,12 +7,8 @@ class MissingApiKeyCheck(Extension):
"""Check if API keys are configured for selected model providers."""
LOCAL_PROVIDERS = {"ollama", "lm_studio", "llama_cpp", "omlx", "vllm"}
- CONFIGURE_MODEL_SETTINGS_LINK = (
- """"""
- """"""
- """
"""
- )
+ ONBOARDING_MODAL_PATH = "/plugins/_onboarding/webui/onboarding.html"
+ ONBOARDING_CTA_TEXT = "Start Onboarding"
async def execute(self, banners: list = [], frontend_context: dict = {}, **kwargs):
cfg = plugins.get_plugin_config("_model_config") or {}
@@ -25,11 +21,12 @@ class MissingApiKeyCheck(Extension):
"priority": 100,
"title": "Welcome to Agent Zero!",
"html": f"""You're almost ready to chat. Please configure your models to continue.
- Insert your API key in the onboarding wizard.
- {self.CONFIGURE_MODEL_SETTINGS_LINK}""",
+ Insert your API key in the onboarding wizard.""",
+ "cta_text": self.ONBOARDING_CTA_TEXT,
+ "cta_action": f"open-modal:{self.ONBOARDING_MODAL_PATH}",
"dismissible": False,
"source": "backend",
- "auto_modal_path": "/plugins/_onboarding/webui/onboarding.html",
+ "auto_modal_path": self.ONBOARDING_MODAL_PATH,
"auto_modal_reason": "missing-api-key",
"auto_modal_priority": 100,
"auto_modal_surfaces": ["welcome", "chat-created"],
@@ -69,8 +66,9 @@ class MissingApiKeyCheck(Extension):
"priority": 90,
"title": "Missing API Key for model presets",
"html": f"""No API key configured for preset models: {preset_list}.
- These presets will not work until you provide the required API keys.
- {self.CONFIGURE_MODEL_SETTINGS_LINK}""",
+ These presets will not work until you provide the required API keys.""",
+ "cta_text": self.ONBOARDING_CTA_TEXT,
+ "cta_action": f"open-modal:{self.ONBOARDING_MODAL_PATH}",
"dismissible": True,
"source": "backend"
})
diff --git a/tests/test_model_config_api_keys.py b/tests/test_model_config_api_keys.py
index 1153630fd..1b592c555 100644
--- a/tests/test_model_config_api_keys.py
+++ b/tests/test_model_config_api_keys.py
@@ -82,6 +82,9 @@ async def test_missing_api_key_banner_exposes_missing_providers(monkeypatch):
)
row = next(b for b in banners if b.get("id") == "missing-api-key")
assert row.get("missing_providers") == fake
+ assert row["cta_text"] == "Start Onboarding"
+ assert row["cta_action"] == "open-modal:/plugins/_onboarding/webui/onboarding.html"
+ assert "onboarding-banner-btn-container" not in row["html"]
def test_model_config_frontend_tracks_inline_api_key_edits():
@@ -267,6 +270,8 @@ def test_missing_api_key_banner_includes_auto_modal_metadata(monkeypatch):
assert row["type"] == "warning"
assert row["dismissible"] is False
assert row["missing_providers"] == fake
+ assert row["cta_text"] == "Start Onboarding"
+ assert row["cta_action"] == "open-modal:/plugins/_onboarding/webui/onboarding.html"
def test_provider_key_modes_for_local_and_ollama_cloud():
diff --git a/tests/test_oauth_static.py b/tests/test_oauth_static.py
index 4b7b165bd..419a93178 100644
--- a/tests/test_oauth_static.py
+++ b/tests/test_oauth_static.py
@@ -179,6 +179,8 @@ def test_oauth_discovery_card_renders_in_welcome_account_panel():
assert "discovery-oauth-accounts" in discovery_cards
assert "Your AI accounts" in discovery_cards
+ assert "Use your subscription-backed logins for model access." in discovery_cards
+ assert "Link account-backed providers such as" not in discovery_cards
assert "Connected OAuth accounts" not in discovery_cards
assert "discovery-codex-oauth" not in discovery_cards
assert "5h and weekly limits are ready." not in discovery_cards
@@ -188,6 +190,9 @@ def test_oauth_discovery_card_renders_in_welcome_account_panel():
assert "account_chips" in discovery_cards
assert "discovery-account-card" in welcome_cards
assert "discovery-account-chip" in welcome_cards
+ assert "discovery-account-icon" not in welcome_cards
+ assert ".discovery-account-chip {\n display: inline-grid;" in welcome_cards
+ assert " border-radius: 8px;" in welcome_cards
assert "discovery-account-usage" in welcome_cards
assert "formatRemainingPercent(window)" in welcome_cards
assert "formatRemainingPercent(window)" in discovery_store
diff --git a/tests/test_welcome_composer_static.py b/tests/test_welcome_composer_static.py
index 11ab62311..0738fb8a9 100644
--- a/tests/test_welcome_composer_static.py
+++ b/tests/test_welcome_composer_static.py
@@ -10,17 +10,70 @@ def _read(relative_path: str) -> str:
def test_welcome_screen_embeds_shared_new_chat_composer() -> None:
welcome = _read("webui/components/welcome/welcome-screen.html")
+ welcome_store = _read("webui/components/welcome/welcome-store.js")
index = _read("webui/index.html")
discovery_cards = _read(
"plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html"
)
+ discovery_store = _read("plugins/_discovery/webui/discovery-store.js")
assert 'class="welcome-composer"' in welcome
assert 'path="chat/attachments/inputPreview.html"' in welcome
assert 'path="chat/input/chat-bar-input.html"' in welcome
+ assert "x-text=\"$store.welcomeStore.heroSubtitle\"" in welcome
+ assert "Hello! I'm Agent Zero" in welcome
+ assert "'is-setup-required': $store.welcomeStore.hasBlockingSetupBanner" in welcome
+ assert 'class="welcome-setup-composer"' in welcome
+ assert "Configure your models to start chatting" in welcome
+ assert "Start Onboarding" in welcome
+ assert "openBlockingSetup()" in welcome
+ assert '.filter((b) => b.id !== "missing-api-key")' in welcome_store
+ assert "get blockingSetupBanner()" in welcome_store
+ assert "get heroSubtitle()" in welcome_store
+ assert 'return "One setup step before chatting.";' in welcome_store
+ assert 'return "How can I help you today?";' in welcome_store
+ assert "openBlockingSetup()" in welcome_store
assert 'Quick Actions
' in welcome
assert 'class="welcome-lower-grid"' in welcome
assert 'x-extension id="welcome-actions-end"' in welcome
+ assert "grid-template-columns: repeat(4, minmax(0, 1fr));" in welcome
+ assert '"accounts accounts channels channels"' in welcome
+ assert '"system system system system"' in welcome
+ assert "align-items: stretch;" in welcome
+ assert ".welcome-lower-grid .discovery-slot {\n display: contents;" in welcome
+ assert ".welcome-lower-grid .discovery-account-card {\n grid-area: accounts;" in welcome
+ assert "grid-area: system;" in welcome
+ assert ".welcome-composer .input-row {\n min-height: 4.9rem;" in welcome
+ assert "border-radius: 8px;" in welcome
+ assert 'aria-label="Refresh System Resources"' in welcome
+ assert "refreshBanners(true)" in welcome
+ assert 'aria-label="Dismiss System Resources"' in welcome
+ assert "dismissBanner($store.welcomeStore.systemResourceBanner?.id || 'system-resources')" in welcome
+ assert "welcome-panel-status-icon" not in welcome
+ assert "monitoring" not in welcome
+ assert ".welcome-banner {\n --welcome-banner-accent: #2f6bff;" in welcome
+ assert "grid-template-columns: auto minmax(0, 1fr) auto auto;" in welcome
+ assert ".welcome-banner.banner-warning {\n --welcome-banner-accent: #f59e0b;" in welcome
+ assert "border-left: 4px" not in welcome
+ assert 'class="welcome-banner-cta"' in welcome
+ assert "executeBannerAction(banner.cta_action)" in welcome
+ assert "executeBannerAction(action)" in welcome_store
+ assert 'action.startsWith("open-modal:")' in welcome_store
+ assert ".welcome-banner-html .onboarding-banner-btn-container" in welcome
+ assert "justify-content: flex-end;" in welcome
+ assert "justify-content: flex-start;" in welcome
+ assert ".welcome-banner-html .btn,\n .welcome-banner-html button" in welcome
+ assert "background-color: #4248f1;" in welcome
+ assert 'aria-label="Dismiss Connect Channels"' in discovery_cards
+ assert "dismissFeatureCards()" in discovery_cards
+ assert "dismissFeatureCards() {" in discovery_store
+ assert 'class="discovery-feature-card"\n type="button"' in discovery_cards
+ assert "`${card.cta_text || 'Connect'} ${card.title}`" in discovery_cards
+ assert "discovery-feature-card > .btn" not in discovery_cards
+ assert "discovery-feature-head" not in discovery_cards
+ assert "discovery-account-header" in discovery_cards
+ assert "discovery-account-cta" in discovery_cards
+ assert "discovery-account-icon" not in discovery_cards
assert "x-if=\"$store.welcomeStore && $store.welcomeStore.isVisible\"" in index
assert "x-if=\"!$store.welcomeStore || !$store.welcomeStore.isVisible\"" in index
diff --git a/webui/components/welcome/welcome-screen.html b/webui/components/welcome/welcome-screen.html
index d27472373..15a29fc29 100644
--- a/webui/components/welcome/welcome-screen.html
+++ b/webui/components/welcome/welcome-screen.html
@@ -15,11 +15,31 @@
-
+
+
+
+
@@ -37,6 +57,14 @@
+