mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-04-28 03:30:23 +00:00
Add the always-enabled `_discovery` plugin to turn the welcome screen into a discovery surface for the Plugin Hub and A0 integrations. Includes a hero card plus Telegram, Email, and WhatsApp feature cards, with persistent dismiss/restore state, CTA routing to plugin config screens, and self-contained placeholder artwork. Implemented entirely through the existing WebUI extension mechanism with no core welcome-screen changes. stores cleanup layout polish and onboarding integration Move feature card titles beside thumbnails for better space efficiency and visibility. Restructure card markup and styles to support a fluid grid layout and horizontal alignment. Integrate discovery cards into the final onboarding step via a new 'onboarding-success-end' extension point, ensuring new users see extension opportunities immediately after setup. Hide discovery cards on the dashboard while the missing API key onboarding banner is visible to reduce UI noise and user confusion during initial config. update discovery card initialization and loading logic Enhance the discovery store to fetch cards from the API, improving the dynamic loading of discovery cards based on user context. This change optimizes the user experience by ensuring relevant cards are displayed immediately after onboarding and when modals are closed. And on top of that, there's a proper backend for these new cards.
483 lines
16 KiB
HTML
483 lines
16 KiB
HTML
<script type="module">
|
|
import { store } from "/plugins/_discovery/webui/discovery-store.js";
|
|
</script>
|
|
|
|
<div x-data>
|
|
<template x-if="$store.discoveryStore">
|
|
<div class="discovery-slot"
|
|
@modal-closed.window="$store.discoveryStore.refreshCards()"
|
|
x-create="$store.discoveryStore.refreshCards()"
|
|
x-show="$store.discoveryStore.cards.length > 0 || $store.discoveryStore.hasDismissedCards">
|
|
|
|
<div class="discovery-section" style="margin-top: 2rem;">
|
|
|
|
<div class="discovery-features" x-show="$store.discoveryStore.featureCards.length > 0" style="text-align: left;">
|
|
<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)">
|
|
<button class="discovery-dismiss discovery-dismiss-small"
|
|
type="button"
|
|
x-show="card.dismissible"
|
|
@click.stop="$store.discoveryStore.dismissCard(card.id)"
|
|
:aria-label="`Dismiss ${card.title}`"
|
|
title="Dismiss">
|
|
<span class="material-symbols-outlined">close</span>
|
|
</button>
|
|
|
|
<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>
|
|
|
|
<p class="discovery-feature-desc" x-text="card.description"></p>
|
|
|
|
<button class="btn btn primary"
|
|
type="button"
|
|
@click.stop="$store.discoveryStore.executeCta(card.cta_action)">
|
|
<span x-text="card.cta_text"></span>
|
|
<span class="material-symbols-outlined">arrow_forward</span>
|
|
</button>
|
|
</article>
|
|
</template>
|
|
</div>
|
|
|
|
|
|
<template x-for="card in $store.discoveryStore.heroCards" :key="card.id">
|
|
<article class="discovery-hero"
|
|
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)">
|
|
<button class="discovery-dismiss"
|
|
type="button"
|
|
x-show="card.dismissible"
|
|
@click.stop="$store.discoveryStore.dismissCard(card.id)"
|
|
:aria-label="`Dismiss ${card.title}`"
|
|
title="Dismiss">
|
|
<span class="material-symbols-outlined">close</span>
|
|
</button>
|
|
|
|
<div class="discovery-hero-content">
|
|
<h3 class="discovery-hero-title" x-text="card.title"></h3>
|
|
<p class="discovery-hero-desc" x-text="card.description"></p>
|
|
<button class="btn btn-ok"
|
|
type="button"
|
|
@click.stop="$store.discoveryStore.executeCta(card.cta_action)">
|
|
<span x-text="card.cta_text"></span>
|
|
<span class="material-symbols-outlined">arrow_forward</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="discovery-hero-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-hero-icon" x-text="card.icon"></span>
|
|
</template>
|
|
</div>
|
|
</article>
|
|
</template>
|
|
|
|
<div class="discovery-undismiss" x-show="$store.discoveryStore.hasDismissedCards">
|
|
<button type="button"
|
|
class="discovery-undismiss-btn"
|
|
@click="$store.discoveryStore.undismissCards()">
|
|
<span class="material-symbols-outlined">undo</span>
|
|
<span>Show dismissed suggestions</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.discovery-slot {
|
|
grid-column: 1 / -1;
|
|
width: 100%;
|
|
}
|
|
|
|
.discovery-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
width: 100%;
|
|
margin-top: 0.1rem;
|
|
}
|
|
|
|
.discovery-hero,
|
|
.discovery-feature-card {
|
|
position: relative;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 12px;
|
|
background: var(--color-panel);
|
|
text-align: left;
|
|
transition:
|
|
border-color 0.2s ease,
|
|
background-color 0.2s ease,
|
|
box-shadow 0.2s ease,
|
|
transform 0.2s ease;
|
|
}
|
|
|
|
.discovery-hero:hover,
|
|
.discovery-feature-card:hover,
|
|
.discovery-hero:focus-visible,
|
|
.discovery-feature-card:focus-visible {
|
|
border-color: color-mix(in srgb, var(--color-primary) 72%, white 6%);
|
|
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.16);
|
|
outline: none;
|
|
}
|
|
|
|
.discovery-hero {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
min-height: 152px;
|
|
overflow: hidden;
|
|
padding: 1.25rem 1.25rem 1.25rem 1.5rem;
|
|
background:
|
|
linear-gradient(
|
|
135deg,
|
|
color-mix(in srgb, var(--color-primary) 10%, transparent),
|
|
transparent 58%
|
|
),
|
|
radial-gradient(
|
|
circle at 92% 24%,
|
|
color-mix(in srgb, var(--color-primary) 12%, transparent),
|
|
transparent 42%
|
|
),
|
|
var(--color-panel);
|
|
}
|
|
|
|
.discovery-hero-content,
|
|
.discovery-hero-thumb {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.discovery-hero-content {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.discovery-hero-title {
|
|
margin: 0 0 0.4rem;
|
|
color: var(--color-text);
|
|
font-size: 1.08rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.01em;
|
|
}
|
|
|
|
.discovery-hero-desc {
|
|
margin: 0 0 1rem;
|
|
color: var(--color-text);
|
|
font-size: 0.86rem;
|
|
line-height: 1.5;
|
|
max-width: 38ch;
|
|
}
|
|
|
|
.discovery-cta-link,
|
|
.discovery-undismiss-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.35rem;
|
|
cursor: pointer;
|
|
transition:
|
|
background-color 0.2s ease,
|
|
border-color 0.2s ease,
|
|
color 0.2s ease,
|
|
transform 0.2s ease;
|
|
}
|
|
|
|
.discovery-hero-thumb {
|
|
flex: 0 0 132px;
|
|
width: 132px;
|
|
height: 108px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.discovery-hero-thumb img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
filter: drop-shadow(0 12px 18px rgba(0, 0, 0, 0.12));
|
|
}
|
|
|
|
.discovery-hero-icon {
|
|
font-size: 3rem;
|
|
color: var(--color-highlight-dark);
|
|
}
|
|
|
|
.discovery-features {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.discovery-feature-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
min-height: 188px;
|
|
padding: 1rem;
|
|
background:
|
|
linear-gradient(
|
|
180deg,
|
|
color-mix(in srgb, var(--color-primary) 4%, transparent),
|
|
transparent 38%
|
|
),
|
|
var(--color-panel);
|
|
}
|
|
|
|
.discovery-feature-head {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
width: 100%;
|
|
min-width: 0;
|
|
}
|
|
|
|
.discovery-feature-thumb {
|
|
flex: 0 0 auto;
|
|
width: 52px;
|
|
height: 52px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 14px;
|
|
overflow: hidden;
|
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
}
|
|
|
|
.discovery-feature-thumb img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.discovery-feature-icon {
|
|
font-size: 1.45rem;
|
|
color: var(--color-highlight-dark);
|
|
}
|
|
|
|
.discovery-feature-title {
|
|
margin: 0;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
color: var(--color-text);
|
|
font-size: 0.92rem;
|
|
font-weight: 600;
|
|
line-height: 1.25;
|
|
}
|
|
|
|
.discovery-feature-desc {
|
|
margin: 0.55rem 0 0;
|
|
padding-bottom: var(--spacing-md);
|
|
color: var(--color-text);
|
|
font-size: 0.8rem;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.discovery-feature-card > .btn {
|
|
margin-top: auto;
|
|
}
|
|
|
|
.discovery-cta-link {
|
|
margin-top: 0.95rem;
|
|
border: 1px solid color-mix(in srgb, var(--color-primary) 34%, var(--color-border));
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--color-primary);
|
|
padding: 0.42rem 0.72rem;
|
|
font-size: 0.79rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.discovery-cta-link:hover,
|
|
.discovery-cta-link:focus-visible {
|
|
background: var(--color-primary);
|
|
border-color: var(--color-primary);
|
|
color: #fff;
|
|
outline: none;
|
|
}
|
|
|
|
.discovery-dismiss {
|
|
position: absolute;
|
|
top: 0.65rem;
|
|
right: 0.65rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--color-secondary);
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
transition:
|
|
opacity 0.2s ease,
|
|
background-color 0.2s ease,
|
|
color 0.2s ease;
|
|
z-index: 2;
|
|
}
|
|
|
|
.discovery-dismiss-small {
|
|
top: 0.55rem;
|
|
right: 0.55rem;
|
|
width: 26px;
|
|
height: 26px;
|
|
}
|
|
|
|
.discovery-hero:hover .discovery-dismiss,
|
|
.discovery-hero:focus-visible .discovery-dismiss,
|
|
.discovery-feature-card:hover .discovery-dismiss,
|
|
.discovery-feature-card:focus-visible .discovery-dismiss {
|
|
opacity: 0.68;
|
|
}
|
|
|
|
.discovery-dismiss:hover,
|
|
.discovery-dismiss:focus-visible {
|
|
opacity: 1;
|
|
color: var(--color-text);
|
|
background: color-mix(in srgb, var(--color-border) 84%, transparent);
|
|
outline: none;
|
|
}
|
|
|
|
.discovery-dismiss .material-symbols-outlined {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.discovery-undismiss {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.discovery-undismiss-btn {
|
|
border: none;
|
|
background: transparent;
|
|
padding: 0;
|
|
color: var(--color-secondary);
|
|
font-size: 0.8rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.discovery-undismiss-btn:hover,
|
|
.discovery-undismiss-btn:focus-visible {
|
|
color: var(--color-primary);
|
|
outline: none;
|
|
}
|
|
|
|
.discovery-cta-link .material-symbols-outlined,
|
|
.discovery-undismiss-btn .material-symbols-outlined {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
@media (hover: none) {
|
|
.discovery-dismiss {
|
|
opacity: 0.68;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.discovery-section {
|
|
gap: 0.85rem;
|
|
}
|
|
|
|
.discovery-hero {
|
|
min-height: 0;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.discovery-hero-thumb {
|
|
flex-basis: 92px;
|
|
width: 92px;
|
|
height: 76px;
|
|
}
|
|
|
|
.discovery-features {
|
|
grid-template-columns: 1fr;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.discovery-feature-card {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
gap: 0.75rem;
|
|
min-height: 0;
|
|
align-items: center;
|
|
padding: 0.85rem 1rem;
|
|
}
|
|
|
|
.discovery-feature-head {
|
|
grid-column: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.discovery-cta-link {
|
|
margin-top: 0;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 520px) {
|
|
.discovery-slot {
|
|
margin-top: 0.15rem;
|
|
}
|
|
|
|
.discovery-hero {
|
|
padding-right: 0.95rem;
|
|
}
|
|
|
|
.discovery-hero-thumb {
|
|
display: none;
|
|
}
|
|
|
|
.discovery-hero-title {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.discovery-feature-card {
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
padding-right: 3rem;
|
|
}
|
|
|
|
.discovery-feature-card > .btn {
|
|
justify-self: end;
|
|
}
|
|
}
|
|
</style>
|
|
</div>
|