Disable What's New cards

Move active What's New content into an empty slide list so the startup modal stays quiet until new cards are added. Keep manual Open available with an empty state, and update the static test plus DOX contract for dormant periods.
This commit is contained in:
Alessandro 2026-07-02 16:44:22 +02:00
parent bcdc99e6cc
commit 0a9e15be87
7 changed files with 64 additions and 81 deletions

View file

@ -93,5 +93,5 @@ Direct child DOX files:
| [_text_editor/AGENTS.md](_text_editor/AGENTS.md) | Native text read, write, and patch tool. |
| [_time_travel/AGENTS.md](_time_travel/AGENTS.md) | Workspace history, diff, travel, snapshot, and revert flows. |
| [_whatsapp_integration/AGENTS.md](_whatsapp_integration/AGENTS.md) | WhatsApp Baileys bridge integration. |
| [_whats_new/AGENTS.md](_whats_new/AGENTS.md) | Version-gated What's New showcase modal and startup trigger. |
| [_whats_new/AGENTS.md](_whats_new/AGENTS.md) | Version-gated What's New showcase modal, card list, and startup trigger. |
| [_whisper_stt/AGENTS.md](_whisper_stt/AGENTS.md) | Whisper speech-to-text integration. |

View file

@ -2,20 +2,22 @@
## Purpose
- Own the built-in version-gated "What's New" modal for showcasing Agent Zero features after updates.
- Own the built-in version-gated "What's New" modal for showcasing Agent Zero features after updates, dormant when no cards are configured.
## Ownership
- `plugin.yaml` owns metadata and always-enabled status.
- `webui/main.html` owns the canonical modal opened by startup and the Builtin Plugins `Open` button.
- `webui/whats-new.html` is a compatibility redirect to `webui/main.html`.
- `webui/whats-new-slides.js` owns the current card list; an empty list disables automatic display.
- `webui/` owns the Alpine store, copy, and showcase media assets.
- `extensions/webui/initFw_end/` owns the startup trigger that opens the modal once per newer installed version unless the user has permanently opted out.
- `extensions/webui/initFw_end/` owns the startup trigger that opens the modal once per newer installed version when cards exist unless the user has permanently opted out.
## Local Contracts
- Closing, Skip, or Done records only the current installed version as seen.
- Future updates should auto-open the modal again unless the user checks the modal's permanent opt-out checkbox.
- Future updates with cards should auto-open the modal again unless the user checks the modal's permanent opt-out checkbox.
- Do not auto-open the modal when `webui/whats-new-slides.js` exports no cards.
- The permanent opt-out is stored in browser-local state under `a0_whats_new_never_show`.
- Honor the legacy `a0_whats_new_seen_version` browser-local marker as the last seen version.
- Keep the modal copy concise, left-aligned, and paired with feature media.
@ -24,7 +26,7 @@
## Work Guidance
- Add showcase assets under `webui/assets/` and reference them through `/plugins/_whats_new/webui/assets/...`.
- Add showcase cards in `webui/whats-new-slides.js`; add assets under `webui/assets/` and reference them through `/plugins/_whats_new/webui/assets/...`.
- Keep the startup extension idempotent and tolerant of missing or non-comparable version labels.
- Prefer release-line comparisons over development commit-distance comparisons so local development builds do not reopen the modal on every commit.
- Preserve `webui/main.html` so the plugin list exposes the standard `Open` action.
@ -32,7 +34,8 @@
## Verification
- Run `pytest tests/test_whats_new_static.py` after changing the modal, trigger, or assets.
- Smoke-test startup display, slide navigation, dismissal, same-version reload behavior, newer-version display behavior, opt-out behavior, and manual Builtin Plugins `Open` behavior in the WebUI.
- When the card list is empty, smoke-test no startup modal and the manual Builtin Plugins `Open` empty state when practical.
- When cards exist, smoke-test startup display, slide navigation, dismissal, same-version reload behavior, newer-version display behavior, opt-out behavior, and manual Builtin Plugins `Open` behavior in the WebUI.
## Child DOX Index

View file

@ -1,4 +1,5 @@
import { getModalStack, isModalOpen, openModal } from "/js/modals.js";
import { slides } from "/plugins/_whats_new/webui/whats-new-slides.js";
const MODAL_PATH = "/plugins/_whats_new/webui/main.html";
const LEGACY_MODAL_PATH = "/plugins/_whats_new/webui/whats-new.html";
@ -109,6 +110,7 @@ function shouldNeverShow() {
}
function shouldShowWhatsNew(version = currentVersion()) {
if (!slides.length) return false;
if (shouldNeverShow()) return false;
if (!version) return false;

View file

@ -10,7 +10,11 @@
<div x-data>
<template x-if="$store.whatsNew">
<div class="whats-new-shell" x-init="$store.whatsNew.onOpen()" x-destroy="$store.whatsNew.cleanup()">
<section class="whats-new-media-panel" :aria-label="$store.whatsNew.currentSlide.mediaLabel">
<section
class="whats-new-media-panel"
x-show="$store.whatsNew.hasSlides()"
:aria-label="$store.whatsNew.currentSlide.mediaLabel"
>
<template x-if="$store.whatsNew.currentSlide.mediaType === 'video'">
<video
class="whats-new-media"
@ -35,7 +39,10 @@
<div class="whats-new-eyebrow" x-text="$store.whatsNew.currentSlide.eyebrow"></div>
<h3 x-text="$store.whatsNew.currentSlide.title"></h3>
<p class="whats-new-summary" x-text="$store.whatsNew.currentSlide.summary"></p>
<ul class="whats-new-bullets">
<ul
class="whats-new-bullets"
x-show="$store.whatsNew.currentSlide.bullets.length"
>
<template x-for="item in $store.whatsNew.currentSlide.bullets" :key="item">
<li x-text="item"></li>
</template>
@ -44,7 +51,7 @@
<div class="modal-footer whats-new-footer" data-modal-footer>
<div class="whats-new-footer-left">
<div class="whats-new-footer-progress">
<div class="whats-new-footer-progress" x-show="$store.whatsNew.hasSlides()">
<span class="whats-new-progress-label" x-text="$store.whatsNew.progressLabel()"></span>
<div class="whats-new-progress-dots" role="tablist" aria-label="What's New slides">
<template x-for="(slide, index) in $store.whatsNew.slides" :key="slide.id">
@ -60,7 +67,7 @@
</template>
</div>
</div>
<label class="whats-new-never-show">
<label class="whats-new-never-show" x-show="$store.whatsNew.hasSlides()">
<input
type="checkbox"
:checked="$store.whatsNew.neverShowAgain"
@ -70,7 +77,14 @@
</label>
</div>
<div class="whats-new-footer-actions">
<button type="button" class="btn btn-cancel" @click="$store.whatsNew.skip()">Skip</button>
<button
type="button"
class="btn btn-cancel"
x-show="$store.whatsNew.hasSlides()"
@click="$store.whatsNew.skip()"
>
Skip
</button>
<button
type="button"
class="btn btn-field"

View file

@ -0,0 +1 @@
export const slides = [];

View file

@ -1,59 +1,18 @@
import { createStore } from "/js/AlpineStore.js";
import { closeModal } from "/js/modals.js";
import { slides } from "/plugins/_whats_new/webui/whats-new-slides.js";
const ASSET_BASE = "/plugins/_whats_new/webui/assets";
const NEVER_SHOW_STORAGE_KEY = "a0_whats_new_never_show";
const slides = [
{
id: "parallel-tools",
eyebrow: "Parallel execution",
title: "Parallel tool calls and subagents",
summary:
"Agent Zero can now split work across parallel tool and subagents calls and combine concurrent steps results.",
mediaType: "video",
media: `${ASSET_BASE}/parallel-subs.webm`,
mediaLabel:
"Four Agent Zero subagents working in parallel while the parent agent coordinates the result.",
bullets: [
"Launch coordinated subagents to explore separate paths at the same time.",
"Run mixed batches together: search queries, code execution, file reads, writes, and more.",
"Merge the results back into one answer without waiting through every call in sequence.",
],
},
{
id: "mcp-configuration",
eyebrow: "MCP configuration",
title: "Redesigned MCP configuration UI",
summary:
"Global Settings and Projects now share a cleaner MCP setup flow for command and Remote URL transports.",
mediaType: "image",
media: `${ASSET_BASE}/mcp-servers.png`,
mediaLabel:
"The redesigned MCP servers screen showing server cards, transport controls, and raw JSON mode.",
bullets: [
"Configure npx, uvx, or custom command servers with clearer fields.",
"Connect Remote URL transports from the same accessible editor.",
"Switch to Raw JSON when you want to paste or move configurations between clients.",
],
},
{
id: "skills-scanner",
eyebrow: "Agent security",
title: "Skills Scanner powered by Snyk Agent Scan",
summary:
"Scan your agent skills and MCP-connected surfaces for prompt injections and vulnerabilities.",
mediaType: "image",
media: `${ASSET_BASE}/skills-scanner.png`,
mediaLabel:
"The Skills Scanner screen showing Snyk Agent Scan controls and scan guidance.",
bullets: [
"Review skills with the same scanning flow you already use for plugins.",
"Find prompt-injection risks and vulnerable instructions before they reach runtime.",
"Catch risky skill instructions early, before they become part of an agent workflow.",
],
},
];
const emptySlide = {
eyebrow: "What's New",
title: "No new updates right now",
summary: "New highlights will appear here when there are fresh Agent Zero updates.",
mediaType: "none",
media: "",
mediaLabel: "No new updates right now.",
bullets: [],
};
function storageValue(key) {
try {
@ -109,7 +68,11 @@ export const store = createStore("whatsNew", {
},
get currentSlide() {
return this.slides[this.currentIndex] || this.slides[0];
return this.slides[this.currentIndex] || emptySlide;
},
hasSlides() {
return this.slides.length > 0;
},
isFirst() {
@ -121,6 +84,7 @@ export const store = createStore("whatsNew", {
},
progressLabel() {
if (!this.hasSlides()) return "";
return `${this.currentIndex + 1} of ${this.slides.length}`;
},

View file

@ -5,9 +5,12 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
WHATS_NEW_PLUGIN = PROJECT_ROOT / "plugins/_whats_new"
def test_whats_new_modal_uses_showcase_assets_and_branded_footer():
def test_whats_new_modal_handles_empty_showcase_state():
html = (WHATS_NEW_PLUGIN / "webui/main.html").read_text(encoding="utf-8")
store = (WHATS_NEW_PLUGIN / "webui/whats-new-store.js").read_text(encoding="utf-8")
slide_data = (WHATS_NEW_PLUGIN / "webui/whats-new-slides.js").read_text(
encoding="utf-8"
)
assert "What's New in Agent Zero" in html
assert "data-modal-footer" in html
@ -16,25 +19,19 @@ def test_whats_new_modal_uses_showcase_assets_and_branded_footer():
assert "type=\"checkbox\"" in html
assert "Don't show automatically again" in html
assert "/plugins/_whats_new/webui/whats-new-store.js" in html
assert "/plugins/_whats_new/webui/assets" in store
assert "/plugins/_whats_new/webui/whats-new-slides.js" in store
assert "a0_whats_new_never_show" in store
assert "No new updates right now" in store
assert "hasSlides()" in html + store
assert "export const slides = [];" in slide_data
for asset in ["parallel-subs.webm", "mcp-servers.png", "skills-scanner.png"]:
assert asset in html + store
assert (PROJECT_ROOT / "plugins/_whats_new/webui/assets" / asset).exists()
assert "Parallel tool calls and subagents" in store
assert (
"Agent Zero can now split work across parallel tool and subagents calls and combine concurrent steps results."
in store
)
assert "Redesigned MCP configuration UI" in store
assert "Skills Scanner powered by Snyk Agent Scan" in store
assert "Remote URL transports" in store
assert "Raw JSON" in store
assert "prompt-injection risks" in store
assert "Catch risky skill instructions early" in store
assert "Include MCP servers in the same pass" not in store
old_cards = html + store + slide_data
assert "Parallel tool calls and subagents" not in old_cards
assert "Redesigned MCP configuration UI" not in old_cards
assert "Skills Scanner powered by Snyk Agent Scan" not in old_cards
assert "parallel-subs.webm" not in old_cards
assert "mcp-servers.png" not in old_cards
assert "skills-scanner.png" not in old_cards
def test_whats_new_legacy_modal_path_redirects_to_main_screen():
@ -53,6 +50,8 @@ def test_whats_new_startup_trigger_is_version_gated_with_opt_out():
assert "globalThis.gitinfo?.version" in content
assert "a0_whats_new_seen_version" in content
assert "a0_whats_new_never_show" in content
assert "/plugins/_whats_new/webui/whats-new-slides.js" in content
assert "slides.length" in content
assert "/plugins/_whats_new/webui/main.html" in content
assert "/plugins/_whats_new/webui/whats-new.html" in content
assert "compareVersions" in content