feat(home): Getting Started + Overview + Usage modes sections (release 0.54.7) (#291)
* feat(home): Getting Started + Overview + Usage modes sections
Three new content cards rendered between the install-hero and the
existing connector tiles on /home. Order: Getting Started → Overview
→ Usage modes → connectors.
- Getting Started — dismissible card with two clickable rows linking
to /setup (install flow) and /setup-advanced (deeper reference).
Subsumes the legacy `.advanced-pointer` row that sat above the news
section. Per-device dismiss via a generic localStorage handler:
`.home-card-close[data-dismiss-key="..."]` inside a <section> wires
itself up at page load — drop in any future dismissible card without
per-card JS.
- Overview — operator-owned HTML body sourced from the new
`instance.overview` yaml field (env override
`AGNES_INSTANCE_OVERVIEW`). HTML in, HTML out via the same `| safe`
filter as news_intro. Empty default hides the section entirely,
keeping the OSS vendor-neutral; operators paste their product
framing / privacy posture into instance.yaml. New helper
`get_instance_overview()` in app/instance_config.py mirrors
`get_instance_logo_svg()`.
- Usage modes — three OSS-shipped tiles (Terminal / VS Code / Claude
Desktop · claude.ai) explaining each surface and linking to the
matching /setup-advanced anchors. Closes the gap for users
wondering "where do I actually run this".
Supporting changes:
- setup_advanced.html gains a new `#claude-app` section between
#vscode and #workspace, anchored by the Usage modes Claude Desktop
tile. Covers the marketplace registration paths and when to prefer
the terminal. Added to the table of contents.
- Three new tests in test_web_home_page.py pin the Getting Started
card markup, the Overview-on-when-yaml-set path, and the
Overview-off-by-default path. All 13 tests in the file pass.
Operator follow-up (separate infra PR — NOT this PR): paste the
Foundry-specific Overview body into instance.yaml's
`instance.overview` field. OSS ships with an empty default.
* fix(home): Overview is operator-owned content — drop dismiss button
Earlier iteration added a close X to the Overview section to match
the Getting Started card's dismiss UX. Wrong call: Overview is
operator-authored reference content (privacy posture, telemetry
policy, project framing) and a per-device localStorage hide means
returning users who want to re-read the policy can't recover it
without clearing storage.
Reverts the close button + the data-dismiss-key on the Overview
section. Test inverted to assert the dismiss key is absent (defends
against a future drive-by adding it back). Getting Started still
dismisses — that's procedural getting-started content users
legitimately stop needing once they've finished setup. Overview is
always reachable; whole section is still opt-in at the operator
level via the empty-yaml default.
* fix(home): Terminal usage-mode tile is informational (no click-through)
The setup hero above /home's Usage modes already walks the user
through the Claude Code CLI install — the Terminal tile click-through
to /setup just round-trips back to content the user already scrolled
past. Switch Terminal to a non-anchor <div> and scope the hover
affordance to a.home-usage-item so VS Code + Claude Desktop tiles
keep their click-through (those legitimately deep-link into
/setup-advanced anchors).
* fix(home): point Usage modes guidance at ~/{workspace}/Projects/ subfolder
The bundled plugin scopes the session-analysis loop and the
central-catalog sync to ~/<workspace>/Projects/, not the workspace
root itself — that convention already appears in the install hero's
Step 4 manual-fallback note ('Don't create ~/<workspace>/Projects/
manually — the bundled plugin offers to set it up after install').
Usage modes' footer guidance now matches: 'create every project
under ~/<workspace>/Projects/'. Also calls out that the
session-analysis loop is scoped to that root so users understand
why working outside the workspace dir is invisible to the platform.
This commit is contained in:
parent
14ddaf1e8e
commit
1e87354d7e
9 changed files with 401 additions and 11 deletions
29
CHANGELOG.md
29
CHANGELOG.md
|
|
@ -10,6 +10,35 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.54.7] — 2026-05-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `instance.overview` yaml field (env override
|
||||||
|
`AGNES_INSTANCE_OVERVIEW`) — operator-authored HTML body rendered in
|
||||||
|
the new Overview section on `/home`. HTML in, HTML out via the same
|
||||||
|
`| safe` filter as `news_intro`. Empty default hides the section,
|
||||||
|
keeping the OSS vendor-neutral.
|
||||||
|
- `/home` Getting Started card — dismissible, two clickable rows
|
||||||
|
linking to `/setup` (install) and `/setup-advanced` (deeper
|
||||||
|
reference). Per-device dismiss via localStorage key
|
||||||
|
`agnes_home_gs_dismissed`. Generic `.home-card-close[data-dismiss-key]`
|
||||||
|
+ `<section>` pattern — drop-in for any future dismissible card.
|
||||||
|
- `/home` Usage modes section — three OSS-shipped tiles (Terminal /
|
||||||
|
VS Code / Claude Desktop · claude.ai) explaining each surface and
|
||||||
|
linking to the relevant `/setup-advanced` anchors.
|
||||||
|
- `setup_advanced.html` `#claude-app` section anchored by the Usage
|
||||||
|
modes tile — covers the marketplace registration paths (git
|
||||||
|
smart-HTTP + ZIP fallback) and when to prefer the terminal anyway.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `/home` legacy `.advanced-pointer` row (the "Going deeper —
|
||||||
|
Advanced setup" link that sat above the news section) removed —
|
||||||
|
the same link now lives in the new Getting Started card. Supporting
|
||||||
|
`.advanced-pointer` CSS stays in place as dead style to keep the
|
||||||
|
diff focused.
|
||||||
|
|
||||||
## [0.54.6] — 2026-05-13
|
## [0.54.6] — 2026-05-13
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,22 @@ def get_instance_logo_svg() -> str:
|
||||||
return (raw or "").strip()
|
return (raw or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance_overview() -> str:
|
||||||
|
"""Operator-authored Overview body rendered on ``/home``. Markdown is
|
||||||
|
NOT auto-converted — operators paste HTML (matches the existing
|
||||||
|
``news_intro`` ``| safe`` filter). Empty default = section hidden,
|
||||||
|
keeping the OSS vendor-neutral when an instance ships without
|
||||||
|
operator-specific framing.
|
||||||
|
|
||||||
|
Resolution: ``AGNES_INSTANCE_OVERVIEW`` env > ``instance.overview``
|
||||||
|
YAML > ``""``. Mirrors :func:`get_instance_logo_svg`.
|
||||||
|
"""
|
||||||
|
raw = os.environ.get("AGNES_INSTANCE_OVERVIEW")
|
||||||
|
if raw is None:
|
||||||
|
raw = get_value("instance", "overview", default="")
|
||||||
|
return (raw or "").strip()
|
||||||
|
|
||||||
|
|
||||||
def get_workspace_dir_name() -> str:
|
def get_workspace_dir_name() -> str:
|
||||||
"""Filesystem-safe folder name for the analyst's local workspace
|
"""Filesystem-safe folder name for the analyst's local workspace
|
||||||
(``~/<workspace_dir_name>``). Defaults to :func:`get_instance_brand`
|
(``~/<workspace_dir_name>``). Defaults to :func:`get_instance_brand`
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ from app.instance_config import (
|
||||||
get_gws_oauth_credentials, get_home_automode_visibility,
|
get_gws_oauth_credentials, get_home_automode_visibility,
|
||||||
get_instance_admin_email, get_atlassian_base_url,
|
get_instance_admin_email, get_atlassian_base_url,
|
||||||
get_instance_brand, get_workspace_dir_name,
|
get_instance_brand, get_workspace_dir_name,
|
||||||
get_instance_logo_svg,
|
get_instance_logo_svg, get_instance_overview,
|
||||||
)
|
)
|
||||||
from app.web.connector_prompts import all_connector_prompts
|
from app.web.connector_prompts import all_connector_prompts
|
||||||
from src.repositories.sync_state import SyncStateRepository
|
from src.repositories.sync_state import SyncStateRepository
|
||||||
|
|
@ -345,6 +345,7 @@ def _build_context(
|
||||||
INSTANCE_SUBTITLE = get_instance_subtitle()
|
INSTANCE_SUBTITLE = get_instance_subtitle()
|
||||||
INSTANCE_COPYRIGHT = ""
|
INSTANCE_COPYRIGHT = ""
|
||||||
LOGO_SVG = get_instance_logo_svg()
|
LOGO_SVG = get_instance_logo_svg()
|
||||||
|
INSTANCE_OVERVIEW = get_instance_overview()
|
||||||
TELEGRAM_BOT_USERNAME = os.environ.get("TELEGRAM_BOT_USERNAME", "")
|
TELEGRAM_BOT_USERNAME = os.environ.get("TELEGRAM_BOT_USERNAME", "")
|
||||||
SSH_ALIAS = "data-analyst"
|
SSH_ALIAS = "data-analyst"
|
||||||
SERVER_HOST = os.environ.get("SERVER_HOST", "")
|
SERVER_HOST = os.environ.get("SERVER_HOST", "")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,155 @@
|
||||||
}
|
}
|
||||||
.home-mock .install-hero .lead strong { font-weight: 600; }
|
.home-mock .install-hero .lead strong { font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── Getting Started + Overview + Usage modes (added in PR #289) ───
|
||||||
|
Three new content cards rendered between the install-hero and the
|
||||||
|
connector tiles. Getting Started + Usage modes ship in OSS;
|
||||||
|
Overview body comes from instance.overview yaml (operator-owned),
|
||||||
|
hidden when unset. Generic dismiss pattern: any element with
|
||||||
|
`<button class="home-card-close" data-dismiss-key="...">` gets
|
||||||
|
per-device localStorage dismissibility for free. */
|
||||||
|
|
||||||
|
.home-mock .home-getting-started,
|
||||||
|
.home-mock .home-overview,
|
||||||
|
.home-mock .home-usage {
|
||||||
|
position: relative;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--hp-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 22px 24px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||||||
|
}
|
||||||
|
.home-mock .home-getting-started > header h2,
|
||||||
|
.home-mock .home-overview > h2,
|
||||||
|
.home-mock .home-usage > header h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
color: var(--hp-text-primary);
|
||||||
|
}
|
||||||
|
.home-mock .home-getting-started > header p,
|
||||||
|
.home-mock .home-usage > header p {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--hp-text-secondary);
|
||||||
|
margin: 0 0 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-mock .home-card-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--hp-text-muted);
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.home-mock .home-card-close:hover,
|
||||||
|
.home-mock .home-card-close:focus-visible {
|
||||||
|
background: var(--hp-border-light);
|
||||||
|
color: var(--hp-text-primary);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-mock .home-gs-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid var(--hp-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
margin-top: 8px;
|
||||||
|
transition: border-color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item:hover {
|
||||||
|
border-color: var(--hp-primary);
|
||||||
|
background: var(--hp-primary-light);
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item .ico {
|
||||||
|
font-size: 20px;
|
||||||
|
flex: 0 0 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item .text {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item .text strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--hp-text-primary);
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item .text .desc {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--hp-text-secondary);
|
||||||
|
}
|
||||||
|
.home-mock .home-gs-item .arrow {
|
||||||
|
color: var(--hp-text-muted);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-mock .home-overview-body {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--hp-text-primary);
|
||||||
|
}
|
||||||
|
.home-mock .home-overview-body p { margin: 0 0 10px; }
|
||||||
|
.home-mock .home-overview-body p:last-child { margin-bottom: 0; }
|
||||||
|
.home-mock .home-overview-body a { color: var(--hp-primary); }
|
||||||
|
|
||||||
|
.home-mock .home-usage-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.home-mock .home-usage-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid var(--hp-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
transition: border-color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
/* Hover affordance only on the anchor variants (VS Code, Claude
|
||||||
|
Desktop). The Terminal tile renders as a plain <div> — the setup
|
||||||
|
hero above already covers the terminal install path, so a
|
||||||
|
click-through from here would round-trip to content the user just
|
||||||
|
scrolled past. */
|
||||||
|
.home-mock a.home-usage-item:hover {
|
||||||
|
border-color: var(--hp-primary);
|
||||||
|
background: var(--hp-primary-light);
|
||||||
|
}
|
||||||
|
.home-mock .home-usage-item .ico { font-size: 20px; }
|
||||||
|
.home-mock .home-usage-item strong { font-weight: 600; }
|
||||||
|
.home-mock .home-usage-item span {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--hp-text-secondary);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.home-mock .home-usage-foot {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--hp-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.home-mock .home-usage-grid { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
.home-mock .what-is {
|
.home-mock .what-is {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
@ -1365,6 +1514,88 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
||||||
BEFORE Step 3's install runs ~20 commands. Gated by the same
|
BEFORE Step 3's install runs ~20 commands. Gated by the same
|
||||||
`home_automode.show` flag at the call site. #}
|
`home_automode.show` flag at the call site. #}
|
||||||
|
|
||||||
|
{# Getting Started card — dismissible per-device via localStorage.
|
||||||
|
Two clickable rows pointing at the install flow (/setup) and the
|
||||||
|
deeper reference (/setup-advanced). Subsumes the legacy
|
||||||
|
`.advanced-pointer` row that used to sit above the news section. #}
|
||||||
|
<section class="home-getting-started" id="homeGettingStarted">
|
||||||
|
<button type="button" class="home-card-close"
|
||||||
|
data-dismiss-key="agnes_home_gs_dismissed"
|
||||||
|
aria-label="Dismiss Getting Started">×</button>
|
||||||
|
<header>
|
||||||
|
<h2>Getting Started</h2>
|
||||||
|
<p>Two quick next steps to get the most out of {{ instance_brand }}.</p>
|
||||||
|
</header>
|
||||||
|
<a class="home-gs-item" href="/setup">
|
||||||
|
<span class="ico" aria-hidden="true">🚀</span>
|
||||||
|
<div class="text">
|
||||||
|
<strong>Setup {{ instance_brand }} in your Claude Code</strong>
|
||||||
|
<span class="desc">One-time install: copies a setup script to your clipboard, paste into Claude Code, done in ~10 minutes.</span>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
<a class="home-gs-item" href="/setup-advanced">
|
||||||
|
<span class="ico" aria-hidden="true">🛠️</span>
|
||||||
|
<div class="text">
|
||||||
|
<strong>Go deeper into your AI workspace</strong>
|
||||||
|
<span class="desc">VS Code layout, recommended plugins, multi-model second opinions, custom skills + rules + hooks, project workflows.</span>
|
||||||
|
</div>
|
||||||
|
<span class="arrow" aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{# Overview section — operator-owned, opt-in. Body comes from the
|
||||||
|
`instance.overview` yaml field via get_instance_overview()
|
||||||
|
(`AGNES_INSTANCE_OVERVIEW` env override). Empty value hides the
|
||||||
|
whole section, keeping the OSS vendor-neutral. #}
|
||||||
|
{# Overview is operator-owned reference content, not per-user
|
||||||
|
chrome — no dismiss button on purpose. A one-time per-device
|
||||||
|
hide would mean returning users who wanted to re-read the
|
||||||
|
privacy posture / telemetry policy can't get back to it
|
||||||
|
without clearing localStorage. The whole section is opt-in at
|
||||||
|
the operator level (empty yaml → section absent), which is
|
||||||
|
the right axis of control. #}
|
||||||
|
{% if config.INSTANCE_OVERVIEW %}
|
||||||
|
<section class="home-overview">
|
||||||
|
<h2>Overview</h2>
|
||||||
|
<div class="home-overview-body">{{ config.INSTANCE_OVERVIEW | safe }}</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# Usage modes — Terminal / VS Code / Claude Desktop · claude.ai.
|
||||||
|
Generic OSS-shipped copy linking to /setup and to /setup-advanced
|
||||||
|
anchors. Helps onboarded users find the right surface once they
|
||||||
|
know which workflow fits them. #}
|
||||||
|
<section class="home-usage">
|
||||||
|
<header>
|
||||||
|
<h2>Where you can use {{ instance_brand }}</h2>
|
||||||
|
<p>Same workspace, three surfaces. Pick whichever fits your flow — all three share the same plugins, data access, and credentials.</p>
|
||||||
|
</header>
|
||||||
|
<div class="home-usage-grid">
|
||||||
|
{# Terminal tile is informational — the setup hero above already
|
||||||
|
walks through the Claude Code CLI install, so a click-through
|
||||||
|
here would round-trip back to content the user just scrolled
|
||||||
|
past. Rendered as a non-anchor div; hover/cursor styles only
|
||||||
|
apply to the anchor variants below. #}
|
||||||
|
<div class="home-usage-item">
|
||||||
|
<span class="ico" aria-hidden="true">⌨️</span>
|
||||||
|
<strong>Terminal</strong>
|
||||||
|
<span>Default. Run <code>claude</code> from any project under <code>~/{{ workspace_dir }}</code>. Lowest overhead, fastest feedback loop.</span>
|
||||||
|
</div>
|
||||||
|
<a class="home-usage-item" href="/setup-advanced#vscode">
|
||||||
|
<span class="ico" aria-hidden="true">📑</span>
|
||||||
|
<strong>VS Code</strong>
|
||||||
|
<span>Split-terminal layout — conversation on one side, diffs and tool output on the other. See <em>Go deeper</em> for the recommended layout.</span>
|
||||||
|
</a>
|
||||||
|
<a class="home-usage-item" href="/setup-advanced#claude-app">
|
||||||
|
<span class="ico" aria-hidden="true">💻</span>
|
||||||
|
<strong>Claude Desktop / claude.ai</strong>
|
||||||
|
<span>Connect this {{ instance_brand }} instance's plugin marketplace to your Claude Desktop or claude.ai account — same plugins, no terminal required.</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p class="home-usage-foot">For the deepest integration, create every project under <code>~/{{ workspace_dir }}/Projects/</code> — existing or new. The bundled plugin keeps each project in sync with the central catalog automatically, and the session-analysis loop is scoped to that root.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<details class="setup-collapsible" data-section="connectors" open>
|
<details class="setup-collapsible" data-section="connectors" open>
|
||||||
<summary>
|
<summary>
|
||||||
<span class="ico" aria-hidden="true">🔗</span>
|
<span class="ico" aria-hidden="true">🔗</span>
|
||||||
|
|
@ -1492,14 +1723,10 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="advanced-pointer" href="/setup-advanced">
|
{# Legacy `.advanced-pointer` row removed — same link now lives in
|
||||||
<span class="ico">🛠️</span>
|
the Getting Started card at the top of /home. `.advanced-pointer`
|
||||||
<div class="advanced-pointer-text">
|
CSS (~line 721) is harmless dead style; left in place to keep
|
||||||
<strong>Going deeper — Advanced setup</strong>
|
this diff focused. #}
|
||||||
VS Code split-terminal layout, recommended Claude Code plugins (with copy-able install commands), multi-model second opinions (Codex + Gemini), custom skills + rules + hooks, project workflows, plan tier guidance.
|
|
||||||
</div>
|
|
||||||
<span class="arrow">→</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{% if news_intro %}
|
{% if news_intro %}
|
||||||
<section class="home-news">
|
<section class="home-news">
|
||||||
|
|
@ -1778,6 +2005,29 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
||||||
applyMinimize(nowOn);
|
applyMinimize(nowOn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Generic dismiss-card handler ─────────────────────────────────
|
||||||
|
// Any `<button class="home-card-close" data-dismiss-key="...">`
|
||||||
|
// inside a `<section>` becomes dismissible: clicking sets the
|
||||||
|
// localStorage key to '1' and hides the section; on next page
|
||||||
|
// load the section starts hidden if the key is set. Used by the
|
||||||
|
// Getting Started card; future dismissible cards drop in with
|
||||||
|
// zero per-card JS.
|
||||||
|
document.querySelectorAll('.home-card-close[data-dismiss-key]').forEach(function (btn) {
|
||||||
|
var key = btn.getAttribute('data-dismiss-key');
|
||||||
|
var section = btn.closest('section');
|
||||||
|
if (!section || !key) return;
|
||||||
|
try {
|
||||||
|
if (localStorage.getItem(key) === '1') {
|
||||||
|
section.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) { /* private-mode: render visible, no-op */ }
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
try { localStorage.setItem(key, '1'); } catch (e) { /* ignore */ }
|
||||||
|
section.hidden = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,7 @@
|
||||||
<h2>On this page</h2>
|
<h2>On this page</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#vscode">VS Code as your workspace</a></li>
|
<li><a href="#vscode">VS Code as your workspace</a></li>
|
||||||
|
<li><a href="#claude-app">Claude Desktop & claude.ai</a></li>
|
||||||
<li><a href="#workspace">~/{{ workspace_dir }} workspace anatomy</a></li>
|
<li><a href="#workspace">~/{{ workspace_dir }} workspace anatomy</a></li>
|
||||||
<li><a href="#projects">Project workflows</a></li>
|
<li><a href="#projects">Project workflows</a></li>
|
||||||
<li><a href="#plugins">Recommended plugins</a></li>
|
<li><a href="#plugins">Recommended plugins</a></li>
|
||||||
|
|
@ -323,6 +324,21 @@
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="ad-section" id="claude-app">
|
||||||
|
<h2>1b. Claude Desktop & claude.ai</h2>
|
||||||
|
<p class="ad-lead">{{ instance_brand }}'s plugin marketplace also works from Claude Desktop and claude.ai — same plugins, same RBAC, no terminal required. Useful when you're already inside Claude for chat and want one-click access to your team's curated tools.</p>
|
||||||
|
|
||||||
|
<p>Two channels share the same RBAC-filtered marketplace feed:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Git smart-HTTP</strong> — preferred. Register once with <code>/plugin marketplace add</code>; Claude owns the clone/fetch cycle from then on.</li>
|
||||||
|
<li><strong>ZIP download</strong> — fallback for environments where the git path can't reach the server (private-CA TLS, restrictive proxies). A SessionStart hook unpacks the served bundle into <code>./marketplace/</code> on each session start.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>The exact registration commands (URL, PAT, hook setup) are baked into the clipboard payload at <a href="/setup">/setup</a> — that page detects your platform and chooses the right channel automatically. The setup script also installs the bundled hooks that keep the marketplace + session telemetry in sync between Claude Desktop and the central catalog.</p>
|
||||||
|
|
||||||
|
<p class="ad-lead" style="margin-top: 14px;">When to prefer the terminal anyway: large refactors, anything that touches files outside of <code>~/{{ workspace_dir }}</code>, or workflows that need Claude Code's CLI-only features (auto-mode, YOLO, custom hooks). Claude Desktop and claude.ai are best for short-form chat with read-only / well-scoped tools.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="ad-section" id="workspace">
|
<section class="ad-section" id="workspace">
|
||||||
<h2>2. <code>~/{{ workspace_dir }}</code> workspace anatomy</h2>
|
<h2>2. <code>~/{{ workspace_dir }}</code> workspace anatomy</h2>
|
||||||
<p class="ad-lead">Your workspace was created by <code>agnes init</code> and the bundled {{ instance_brand }} plugin. Here's what each folder is for.</p>
|
<p class="ad-lead">Your workspace was created by <code>agnes init</code> and the bundled {{ instance_brand }} plugin. Here's what each folder is for.</p>
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,14 @@ instance:
|
||||||
# # `name` above still drives browser <title> text and page
|
# # `name` above still drives browser <title> text and page
|
||||||
# # headings — keep it populated. Env override:
|
# # headings — keep it populated. Env override:
|
||||||
# # AGNES_INSTANCE_LOGO_SVG.
|
# # AGNES_INSTANCE_LOGO_SVG.
|
||||||
|
# overview: | # Operator-authored Overview body rendered in the new
|
||||||
|
# <p>Free-form HTML — paragraphs, links, lists.</p>
|
||||||
|
# # Overview section on /home (between Getting Started and
|
||||||
|
# # Usage modes). Use for product framing, privacy posture,
|
||||||
|
# # what-data-flows summary — operator-specific copy stays
|
||||||
|
# # out of the OSS this way. HTML in, HTML out (same `| safe`
|
||||||
|
# # filter as news_intro). Empty/unset = section hidden.
|
||||||
|
# # Env override: AGNES_INSTANCE_OVERVIEW.
|
||||||
# sync_interval: "1 hour" # Cadence shown in analyst CLAUDE.md (e.g., "1 hour", "30 minutes", "daily")
|
# sync_interval: "1 hour" # Cadence shown in analyst CLAUDE.md (e.g., "1 hour", "30 minutes", "daily")
|
||||||
# admin_email: "ops@acme.com" # Operator contact shown on /home GWS connector tile as
|
# admin_email: "ops@acme.com" # Operator contact shown on /home GWS connector tile as
|
||||||
# an "Email admin" mailto button (analysts whose operator
|
# an "Email admin" mailto button (analysts whose operator
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "agnes-the-ai-analyst"
|
name = "agnes-the-ai-analyst"
|
||||||
version = "0.54.6"
|
version = "0.54.7"
|
||||||
description = "Agnes — AI Data Analyst platform for AI analytical systems"
|
description = "Agnes — AI Data Analyst platform for AI analytical systems"
|
||||||
requires-python = ">=3.11,<3.14"
|
requires-python = ">=3.11,<3.14"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
|
|
@ -329,3 +329,73 @@ def test_home_renders_connector_prompts_from_shared_module(fresh_db):
|
||||||
f"the home tile and setup script will paste different text. "
|
f"the home tile and setup script will paste different text. "
|
||||||
f"len(home)={len(actual)} len(module)={len(expected)}"
|
f"len(home)={len(actual)} len(module)={len(expected)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Getting Started + Overview + Usage modes (PR #289 home additions) ────
|
||||||
|
|
||||||
|
|
||||||
|
def test_getting_started_card_renders_on_home(fresh_db):
|
||||||
|
"""The dismissible Getting Started card sits between the install
|
||||||
|
hero and the connector tiles. Both rows must be present and point
|
||||||
|
at /setup and /setup-advanced respectively. State-independent:
|
||||||
|
renders for both onboarded and not-onboarded users (per-device
|
||||||
|
localStorage dismiss is the only off switch)."""
|
||||||
|
from src.db import get_system_db, close_system_db
|
||||||
|
|
||||||
|
for onboarded in (False, True):
|
||||||
|
conn = get_system_db()
|
||||||
|
try:
|
||||||
|
_, sess = _make_user_and_session(
|
||||||
|
conn, email=f"gs-{onboarded}@example.com", onboarded=onboarded
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
close_system_db()
|
||||||
|
body = _client().get("/home", cookies={"access_token": sess}).text
|
||||||
|
assert '<section class="home-getting-started"' in body
|
||||||
|
assert 'data-dismiss-key="agnes_home_gs_dismissed"' in body
|
||||||
|
assert 'class="home-gs-item" href="/setup"' in body
|
||||||
|
assert 'class="home-gs-item" href="/setup-advanced"' in body
|
||||||
|
|
||||||
|
|
||||||
|
def test_overview_section_renders_when_yaml_set(fresh_db, monkeypatch):
|
||||||
|
"""Setting `AGNES_INSTANCE_OVERVIEW` env (mirrors
|
||||||
|
instance.overview yaml) injects raw HTML into the Overview section
|
||||||
|
via the same `| safe` filter as news_intro. The marker text must
|
||||||
|
appear inside the rendered section wrapper. Overview deliberately
|
||||||
|
has NO dismiss button — it's operator-owned reference content
|
||||||
|
(privacy posture, telemetry policy, product framing), and a
|
||||||
|
per-device hide would leave returning users unable to re-read
|
||||||
|
it without clearing localStorage."""
|
||||||
|
monkeypatch.setenv("AGNES_INSTANCE_OVERVIEW", "<p>OVERVIEW_TEST_MARKER</p>")
|
||||||
|
from src.db import get_system_db, close_system_db
|
||||||
|
|
||||||
|
conn = get_system_db()
|
||||||
|
try:
|
||||||
|
_, sess = _make_user_and_session(conn)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
close_system_db()
|
||||||
|
body = _client().get("/home", cookies={"access_token": sess}).text
|
||||||
|
assert '<section class="home-overview">' in body
|
||||||
|
assert "OVERVIEW_TEST_MARKER" in body
|
||||||
|
# Overview must NOT carry a dismiss key — content stays
|
||||||
|
# reachable on every visit so users can re-read it.
|
||||||
|
assert 'data-dismiss-key="agnes_home_overview_dismissed"' not in body
|
||||||
|
|
||||||
|
|
||||||
|
def test_overview_section_hidden_when_yaml_empty(fresh_db, monkeypatch):
|
||||||
|
"""Default empty `instance.overview` (no env override) hides the
|
||||||
|
section entirely so the OSS ships without a stray empty
|
||||||
|
Overview placeholder."""
|
||||||
|
monkeypatch.delenv("AGNES_INSTANCE_OVERVIEW", raising=False)
|
||||||
|
from src.db import get_system_db, close_system_db
|
||||||
|
|
||||||
|
conn = get_system_db()
|
||||||
|
try:
|
||||||
|
_, sess = _make_user_and_session(conn)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
close_system_db()
|
||||||
|
body = _client().get("/home", cookies={"access_token": sess}).text
|
||||||
|
assert '<section class="home-overview">' not in body
|
||||||
|
|
|
||||||
2
uv.lock
2
uv.lock
|
|
@ -24,7 +24,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "agnes-the-ai-analyst"
|
name = "agnes-the-ai-analyst"
|
||||||
version = "0.54.6"
|
version = "0.54.7"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "a2wsgi" },
|
{ name = "a2wsgi" },
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue