mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
Refine welcome screen setup surfaces
Fold the blocking onboarding state into the welcome composer, refresh the account and channel tiles, and keep system resources aligned with the renewed Welcome Screen layout. Update model-config, OAuth, and welcome static tests to pin the new CTA, dismissal, copy, and layout contracts.
This commit is contained in:
parent
0e262f7b87
commit
489acfe8c6
10 changed files with 555 additions and 185 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -11,37 +11,35 @@
|
|||
|
||||
<section class="discovery-features-panel" x-show="$store.discoveryStore.featureCards.length > 0">
|
||||
<header class="discovery-panel-header">
|
||||
<h2>Connect Channels</h2>
|
||||
<p>Extend Agent Zero across your favorite platforms.</p>
|
||||
<div class="discovery-panel-copy">
|
||||
<h2>Connect Channels</h2>
|
||||
<p>Extend Agent Zero across your favorite platforms.</p>
|
||||
</div>
|
||||
<button class="discovery-dismiss discovery-panel-dismiss"
|
||||
type="button"
|
||||
x-show="$store.discoveryStore.featureCards.some(card => card.dismissible !== false)"
|
||||
@click.stop="$store.discoveryStore.dismissFeatureCards()"
|
||||
aria-label="Dismiss Connect Channels"
|
||||
title="Dismiss">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="discovery-features">
|
||||
<template x-for="card in $store.discoveryStore.featureCards" :key="card.id">
|
||||
<article class="discovery-feature-card"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@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)">
|
||||
|
||||
<div class="discovery-feature-head">
|
||||
<div class="discovery-feature-thumb">
|
||||
<template x-if="card.thumbnail">
|
||||
<img :src="card.thumbnail" :alt="card.title" loading="lazy">
|
||||
</template>
|
||||
<template x-if="!card.thumbnail && card.icon">
|
||||
<span class="material-symbols-outlined discovery-feature-icon" x-text="card.icon"></span>
|
||||
</template>
|
||||
</div>
|
||||
<h4 class="discovery-feature-title" x-text="card.title"></h4>
|
||||
</div>
|
||||
|
||||
<button class="btn btn primary"
|
||||
type="button"
|
||||
@click.stop="$store.discoveryStore.executeCta(card.cta_action)">
|
||||
<span x-text="card.cta_text"></span>
|
||||
</button>
|
||||
</article>
|
||||
<button class="discovery-feature-card"
|
||||
type="button"
|
||||
@click="$store.discoveryStore.executeCta(card.cta_action)">
|
||||
<span class="discovery-feature-thumb">
|
||||
<template x-if="card.thumbnail">
|
||||
<img :src="card.thumbnail" :alt="card.title" loading="lazy">
|
||||
</template>
|
||||
<template x-if="!card.thumbnail && card.icon">
|
||||
<span class="material-symbols-outlined discovery-feature-icon" x-text="card.icon"></span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="discovery-feature-title" x-text="`${card.cta_text || 'Connect'} ${card.title}`"></span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -58,11 +56,16 @@
|
|||
</button>
|
||||
|
||||
<header class="discovery-account-header">
|
||||
<span class="material-symbols-outlined discovery-account-icon">key</span>
|
||||
<div class="discovery-account-copy">
|
||||
<h2 x-text="card.title"></h2>
|
||||
<p x-show="card.description" x-text="card.description"></p>
|
||||
</div>
|
||||
<button class="btn btn primary discovery-account-cta"
|
||||
type="button"
|
||||
@click="$store.discoveryStore.executeCta(card.cta_action)">
|
||||
<span x-text="card.cta_text"></span>
|
||||
<span class="material-symbols-outlined">arrow_forward</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="discovery-account-chips" x-show="(card.account_chips || []).length > 0">
|
||||
|
|
@ -95,12 +98,6 @@
|
|||
</template>
|
||||
</div>
|
||||
|
||||
<button class="btn btn primary discovery-account-cta"
|
||||
type="button"
|
||||
@click="$store.discoveryStore.executeCta(card.cta_action)">
|
||||
<span x-text="card.cta_text"></span>
|
||||
<span class="material-symbols-outlined">arrow_forward</span>
|
||||
</button>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
"""<div class="onboarding-banner-btn-container" style="margin-top: 12px;">"""
|
||||
"""<button class="btn btn-ok" onclick="window.openModal('/plugins/_onboarding/webui/onboarding.html');return false;">"""
|
||||
"""Start Onboarding</button>"""
|
||||
"""</div>"""
|
||||
)
|
||||
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.<br>
|
||||
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}.<br>
|
||||
These presets will not work until you provide the required API keys.<br>
|
||||
{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"
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 '<h2>Quick Actions</h2>' 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
|
||||
|
|
|
|||
|
|
@ -15,11 +15,31 @@
|
|||
|
||||
<main class="welcome-shell">
|
||||
<header class="welcome-hero">
|
||||
<h1>Welcome back <span aria-hidden="true">👋</span></h1>
|
||||
<p>Agent Zero is ready to help.</p>
|
||||
<h1>Hello! I'm Agent Zero <span aria-hidden="true">👋</span></h1>
|
||||
<p x-text="$store.welcomeStore.heroSubtitle"></p>
|
||||
</header>
|
||||
|
||||
<section class="welcome-composer" aria-label="Start a new chat">
|
||||
<section class="welcome-composer"
|
||||
:class="{ 'is-setup-required': $store.welcomeStore.hasBlockingSetupBanner }"
|
||||
:aria-label="$store.welcomeStore.hasBlockingSetupBanner ? 'Configure models to start chatting' : 'Start a new chat'">
|
||||
<template x-if="$store.welcomeStore.hasBlockingSetupBanner">
|
||||
<button type="button"
|
||||
class="welcome-setup-composer"
|
||||
@click="$store.welcomeStore.openBlockingSetup()"
|
||||
aria-label="Start onboarding">
|
||||
<span class="welcome-setup-icon" aria-hidden="true">
|
||||
<span class="material-symbols-outlined">warning</span>
|
||||
</span>
|
||||
<span class="welcome-setup-copy">
|
||||
<span class="welcome-setup-title">Configure your models to start chatting</span>
|
||||
<span class="welcome-setup-detail">Insert your API key in the onboarding wizard.</span>
|
||||
</span>
|
||||
<span class="welcome-setup-cta">
|
||||
<span>Start Onboarding</span>
|
||||
<span class="material-symbols-outlined" aria-hidden="true">arrow_forward</span>
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<x-component path="chat/attachments/inputPreview.html"></x-component>
|
||||
<x-component path="chat/input/chat-bar-input.html"></x-component>
|
||||
</section>
|
||||
|
|
@ -37,6 +57,14 @@
|
|||
<div class="welcome-banner-title" x-text="banner.title"></div>
|
||||
<div class="welcome-banner-html" x-html="banner.html"></div>
|
||||
</div>
|
||||
<button class="welcome-banner-cta"
|
||||
type="button"
|
||||
x-show="banner.cta_text && banner.cta_action"
|
||||
@click.stop="$store.welcomeStore.executeBannerAction(banner.cta_action)"
|
||||
:aria-label="banner.cta_text">
|
||||
<span x-text="banner.cta_text"></span>
|
||||
<span class="material-symbols-outlined" aria-hidden="true">arrow_forward</span>
|
||||
</button>
|
||||
<button class="welcome-banner-dismiss"
|
||||
type="button"
|
||||
x-show="banner.dismissible !== false"
|
||||
|
|
@ -131,7 +159,24 @@
|
|||
x-transition.opacity>
|
||||
<header class="welcome-panel-header">
|
||||
<h2 x-text="$store.welcomeStore.systemResourceBanner?.title || 'System Resources'"></h2>
|
||||
<span class="material-symbols-outlined">monitoring</span>
|
||||
<div class="welcome-panel-actions">
|
||||
<button class="welcome-panel-icon-button"
|
||||
type="button"
|
||||
:disabled="$store.welcomeStore.bannersLoading"
|
||||
@click.stop="$store.welcomeStore.refreshBanners(true)"
|
||||
title="Refresh"
|
||||
aria-label="Refresh System Resources">
|
||||
<span class="material-symbols-outlined">refresh</span>
|
||||
</button>
|
||||
<button class="welcome-panel-icon-button"
|
||||
type="button"
|
||||
x-show="$store.welcomeStore.systemResourceBanner?.dismissible !== false"
|
||||
@click.stop="$store.welcomeStore.dismissBanner($store.welcomeStore.systemResourceBanner?.id || 'system-resources')"
|
||||
title="Dismiss"
|
||||
aria-label="Dismiss System Resources">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="welcome-system-body"
|
||||
x-html="$store.welcomeStore.systemResourceBanner?.html || ''"></div>
|
||||
|
|
@ -189,6 +234,7 @@
|
|||
gap: 1.55rem;
|
||||
width: min(100%, 1220px);
|
||||
margin: 0 auto;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
.welcome-hero h1 {
|
||||
|
|
@ -217,10 +263,110 @@
|
|||
display: contents;
|
||||
}
|
||||
|
||||
.welcome-composer.is-setup-required > x-component {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.welcome-setup-composer {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
min-height: 4.9rem;
|
||||
padding: 0.74rem 0.7rem 0.74rem 0.86rem;
|
||||
border: 1px solid color-mix(in srgb, var(--color-border) 72%, transparent);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--color-panel) 64%, var(--color-background) 36%);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
|
||||
text-align: left;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.035),
|
||||
0 18px 46px rgba(0, 0, 0, 0.18);
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
background-color 0.18s ease,
|
||||
box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
.welcome-setup-composer:hover,
|
||||
.welcome-setup-composer:focus-visible {
|
||||
border-color: color-mix(in srgb, var(--color-border) 88%, var(--color-text) 12%);
|
||||
background: color-mix(in srgb, var(--color-panel) 74%, var(--color-background) 26%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.045),
|
||||
0 22px 54px rgba(0, 0, 0, 0.22);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.welcome-setup-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 8px;
|
||||
color: #f59e0b;
|
||||
background: color-mix(in srgb, #f59e0b 9%, transparent);
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, #f59e0b 12%, transparent);
|
||||
}
|
||||
|
||||
.welcome-setup-icon .material-symbols-outlined {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.welcome-setup-copy {
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.welcome-setup-title {
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: 520;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.welcome-setup-detail {
|
||||
color: color-mix(in srgb, var(--color-text) 58%, transparent);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.welcome-setup-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.36rem;
|
||||
min-height: 2.75rem;
|
||||
border-radius: 12px;
|
||||
padding: 0.62rem 0.95rem;
|
||||
background-color: #4248f1;
|
||||
color: #fff;
|
||||
font-size: 0.92rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
transition: background-color 0.18s ease;
|
||||
}
|
||||
|
||||
.welcome-setup-composer:hover .welcome-setup-cta,
|
||||
.welcome-setup-composer:focus-visible .welcome-setup-cta {
|
||||
background-color: #353bc5;
|
||||
}
|
||||
|
||||
.welcome-setup-cta .material-symbols-outlined {
|
||||
font-size: 1.24rem;
|
||||
}
|
||||
|
||||
.welcome-composer .input-row {
|
||||
min-height: 4.9rem;
|
||||
padding: 0.58rem 0.7rem 0.58rem 0.86rem;
|
||||
border-radius: 16px;
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--color-panel) 64%, var(--color-background) 36%);
|
||||
}
|
||||
|
||||
|
|
@ -261,64 +407,64 @@
|
|||
}
|
||||
|
||||
.welcome-banner {
|
||||
--welcome-banner-accent: #2f6bff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.78rem;
|
||||
padding: 0.9rem 1rem;
|
||||
border-radius: 12px;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
min-height: 5.45rem;
|
||||
padding: 1rem 1.1rem;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.welcome-banner.banner-info {
|
||||
border-left: 4px solid #2f6bff;
|
||||
--welcome-banner-accent: #2f6bff;
|
||||
}
|
||||
|
||||
.welcome-banner.banner-warning {
|
||||
border-left: 4px solid #f59e0b;
|
||||
--welcome-banner-accent: #f59e0b;
|
||||
}
|
||||
|
||||
.welcome-banner.banner-error {
|
||||
border-left: 4px solid #ef4444;
|
||||
--welcome-banner-accent: #ef4444;
|
||||
}
|
||||
|
||||
.welcome-banner-icon {
|
||||
display: flex;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
display: inline-flex;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #2f6bff;
|
||||
}
|
||||
|
||||
.banner-warning .welcome-banner-icon {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.banner-error .welcome-banner-icon {
|
||||
color: #ef4444;
|
||||
border-radius: 8px;
|
||||
color: var(--welcome-banner-accent);
|
||||
background: color-mix(in srgb, var(--welcome-banner-accent) 9%, transparent);
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--welcome-banner-accent) 12%, transparent);
|
||||
}
|
||||
|
||||
.welcome-banner-icon .material-symbols-outlined {
|
||||
font-size: 1.25rem;
|
||||
font-size: 1.28rem;
|
||||
}
|
||||
|
||||
.welcome-banner-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: 0.05rem;
|
||||
}
|
||||
|
||||
.welcome-banner-title {
|
||||
margin-bottom: 0.25rem;
|
||||
margin-bottom: 0.28rem;
|
||||
color: var(--color-text);
|
||||
font-size: 0.92rem;
|
||||
font-weight: 650;
|
||||
font-size: 1rem;
|
||||
font-weight: 560;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.welcome-banner-html {
|
||||
color: color-mix(in srgb, var(--color-text) 68%, transparent);
|
||||
font-size: 0.84rem;
|
||||
max-width: 72ch;
|
||||
color: color-mix(in srgb, var(--color-text) 58%, transparent);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
|
|
@ -331,6 +477,55 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.welcome-banner-html .onboarding-banner-btn-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 0.55rem;
|
||||
margin-top: 0.9rem !important;
|
||||
}
|
||||
|
||||
.welcome-banner-cta,
|
||||
.welcome-banner-html .btn,
|
||||
.welcome-banner-html button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 2.35rem;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
padding: 0.55rem 0.9rem;
|
||||
background-color: #4248f1;
|
||||
color: #fff;
|
||||
font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 460;
|
||||
line-height: 1.2;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
transition: background-color 0.18s ease;
|
||||
}
|
||||
|
||||
.welcome-banner-cta {
|
||||
align-self: center;
|
||||
justify-self: end;
|
||||
gap: 0.36rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.welcome-banner-cta .material-symbols-outlined {
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.welcome-banner-cta:hover,
|
||||
.welcome-banner-cta:focus-visible,
|
||||
.welcome-banner-html .btn:hover,
|
||||
.welcome-banner-html .btn:focus-visible,
|
||||
.welcome-banner-html button:hover,
|
||||
.welcome-banner-html button:focus-visible {
|
||||
background-color: #353bc5;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.welcome-banner-dismiss,
|
||||
.welcome-undismiss-button {
|
||||
display: inline-flex;
|
||||
|
|
@ -348,6 +543,7 @@
|
|||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 8px;
|
||||
margin: -0.2rem -0.2rem 0 0;
|
||||
}
|
||||
|
||||
.welcome-banner-dismiss:hover,
|
||||
|
|
@ -451,18 +647,33 @@
|
|||
|
||||
.welcome-lower-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.06fr) minmax(320px, 0.94fr);
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-areas:
|
||||
"accounts accounts channels channels"
|
||||
"system system system system";
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.welcome-lower-grid .discovery-slot {
|
||||
grid-column: auto;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.welcome-lower-grid .discovery-features-panel {
|
||||
grid-area: channels;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.welcome-lower-grid .discovery-account-card {
|
||||
grid-area: accounts;
|
||||
}
|
||||
|
||||
.welcome-lower-grid .discovery-undismiss {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.welcome-system-card {
|
||||
grid-area: system;
|
||||
min-width: 0;
|
||||
border-radius: 8px;
|
||||
padding: 1.6rem 1.7rem;
|
||||
|
|
@ -477,10 +688,41 @@
|
|||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.welcome-panel-header .material-symbols-outlined {
|
||||
.welcome-panel-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.welcome-panel-icon-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: color-mix(in srgb, var(--color-text) 64%, transparent);
|
||||
cursor: pointer;
|
||||
font-family: var(--font-family-main, "Rubik", Arial, Helvetica, sans-serif);
|
||||
}
|
||||
|
||||
.welcome-panel-icon-button:hover,
|
||||
.welcome-panel-icon-button:focus-visible {
|
||||
color: var(--color-text);
|
||||
font-size: 1.35rem;
|
||||
opacity: 0.86;
|
||||
background: color-mix(in srgb, var(--color-border) 45%, transparent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.welcome-panel-icon-button:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.welcome-panel-icon-button .material-symbols-outlined {
|
||||
font-size: 1.18rem;
|
||||
}
|
||||
|
||||
.system-resource-body {
|
||||
|
|
@ -560,6 +802,14 @@
|
|||
|
||||
.welcome-lower-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"accounts"
|
||||
"channels"
|
||||
"system";
|
||||
}
|
||||
|
||||
.welcome-shell {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -571,6 +821,7 @@
|
|||
|
||||
.welcome-shell {
|
||||
gap: 1.2rem;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
.welcome-actions {
|
||||
|
|
@ -593,6 +844,54 @@
|
|||
min-height: 4.35rem;
|
||||
}
|
||||
|
||||
.welcome-setup-composer {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: start;
|
||||
gap: 0.8rem;
|
||||
min-height: 4.35rem;
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
.welcome-setup-cta {
|
||||
grid-column: 2;
|
||||
justify-self: start;
|
||||
min-height: 2.45rem;
|
||||
border-radius: 8px;
|
||||
padding: 0.55rem 0.85rem;
|
||||
}
|
||||
|
||||
.welcome-banner {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 0.82rem;
|
||||
padding: 0.95rem;
|
||||
}
|
||||
|
||||
.welcome-banner-dismiss {
|
||||
position: absolute;
|
||||
top: 0.7rem;
|
||||
right: 0.7rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.welcome-banner-content {
|
||||
padding-right: 1.9rem;
|
||||
}
|
||||
|
||||
.welcome-banner-html {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.welcome-banner-html .onboarding-banner-btn-container {
|
||||
justify-content: flex-start;
|
||||
margin-top: 0.75rem !important;
|
||||
}
|
||||
|
||||
.welcome-banner-cta {
|
||||
grid-column: 2;
|
||||
justify-self: start;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.welcome-system-card {
|
||||
padding: 1.15rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ const model = {
|
|||
get sortedBanners() {
|
||||
return [...this.banners]
|
||||
.filter((b) => b.id !== "system-resources")
|
||||
.filter((b) => b.id !== "missing-api-key")
|
||||
.filter((b) => b.type !== "hero" && b.type !== "feature")
|
||||
.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
},
|
||||
|
|
@ -151,6 +152,43 @@ const model = {
|
|||
return this.banners.find((b) => b.id === "system-resources") || null;
|
||||
},
|
||||
|
||||
get blockingSetupBanner() {
|
||||
return this.banners.find((b) => b.id === "missing-api-key") || null;
|
||||
},
|
||||
|
||||
get hasBlockingSetupBanner() {
|
||||
return Boolean(this.blockingSetupBanner);
|
||||
},
|
||||
|
||||
get heroSubtitle() {
|
||||
if (this.hasBlockingSetupBanner) {
|
||||
return "One setup step before chatting.";
|
||||
}
|
||||
return "How can I help you today?";
|
||||
},
|
||||
|
||||
openBlockingSetup() {
|
||||
const path =
|
||||
this.blockingSetupBanner?.auto_modal_path ||
|
||||
"/plugins/_onboarding/webui/onboarding.html";
|
||||
window.openModal(path);
|
||||
},
|
||||
|
||||
executeBannerAction(action) {
|
||||
if (!action) return;
|
||||
|
||||
if (action.startsWith("open-modal:")) {
|
||||
const path = action.slice("open-modal:".length);
|
||||
if (path) window.openModal(path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.startsWith("open-url:")) {
|
||||
const url = action.slice("open-url:".length);
|
||||
if (url) window.open(url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dismiss a banner by ID.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue