feat(brand): wire instance.logo_svg into header brand slot (release 0.54.6) (#289)
* feat(brand): inline operator SVG logo + drop header subtitle (release 0.54.6)
Three header tweaks, one PR:
1. _app_header.html drops the small uppercase subtitle line below the
brand. instance.subtitle still flows into the CLAUDE.md preamble +
init welcome template ("Operated by …"); only the web header chrome
loses it.
2. get_instance_logo_svg() in app/instance_config.py reads
instance.logo_svg (yaml) / AGNES_INSTANCE_LOGO_SVG (env). The
yaml field was already documented in instance.yaml.example and the
template already supported inline <svg> via {{ config.LOGO_SVG |
safe }}, but router.py:344 hard-coded LOGO_SVG = "" — the middle
wire was missing. Now operators can paste a lockup directly into
their instance.yaml under instance.logo_svg: | and have it render
in the header. Resolution mirrors get_instance_brand (env > yaml >
""). instance.name remains independent: drives browser <title>
tags + page h1s + CLAUDE.md heading; the SVG is the web-header
visual only.
3. .app-header-logo svg gains max-height: 40px; width: auto; so any
operator's lockup scales via its viewBox to fit the 72px header
without per-asset width/height edits. Pairs with #2 — without the
clamp, raw artwork (e.g. a 1600x430 lockup) overflows the chrome.
Release-cut included per the same-PR rule (Unreleased contained only
these bullets after rebase onto 0.54.5).
* revert: keep app-header-subtitle span — out of scope for this PR
Initial commit dropped the subtitle line on the assumption that
the user wanted both the secondary header line AND the future-SVG
brand cleaned up. The actual ask was narrower: drop the hostname
suffix that renders inside instance.name ("Foundry AI (hostname)"),
which is a startup.sh concern, not a template one. Restore the
subtitle span and the CHANGELOG bullet that announced its removal.
PR scope narrows to LOGO_SVG wiring + CSS clamp only.
* fix(header): hide subtitle span when instance.subtitle is empty
Pre-fix the template fell back to the literal string 'Data Analyst
Portal' when INSTANCE_SUBTITLE was unset, so operators who left the
field empty saw a stray hardcoded label below their brand. Switched
to a Jinja {% if %} guard around the whole <span class="app-header-
subtitle"> so an empty subtitle produces no element at all — clean
header chrome instead of placeholder leak.
* feat(home): hide install-hero once onboarded + X close button
- Wrap the entire install-hero in `{% if not onboarded %}` so once
`users.onboarded=true` (auto-flipped by `agnes init` POSTing
/api/me/onboarded, or by the new X / existing fallback button) the
blue hero disappears entirely. Pre-PR the onboarded branch reused
the same shell with a "Welcome back" header + "Steps 1–4 done" badge
+ minimize toggle, which visually outweighed the actual nav hub.
- Add a circular × close button (top-right of the hero, rendered only
when not-onboarded). Click → window.confirm() asking the user to
acknowledge onboarding → POST /api/me/onboarded → reload. The
confirm string intentionally avoids the literal phrase
"Mark me as offboarded" because cli/commands/onboarded.py::status
scans /home's rendered HTML for that exact marker as a fallback for
the api/me/profile check.
- Lift the offboard escape hatch out of the hero into a discrete
`.offboard-strip` rendered below, gated `{% if onboarded %}`. Lets
the analyst flip back to the install view after wiping their
workspace folder.
- Centralize the /api/me/onboarded POST into a `postOnboarded()` JS
helper reused by the hero X, the existing "Mark me as onboarded"
fallback button, and the new offboard button.
Tests updated to match the new behavior:
- `test_home_onboarded_user_sees_nav_hub` — asserts the hero is gone
and the offboard strip is the only setup-flow remnant.
- `test_minimize_toggle_no_longer_rendered` (renamed) — asserts the
minimize toggle is absent in both states (was previously rendered
inside the now-hidden onboarded branch of the hero).
- `test_home_no_auto_transition_after_post_until_reload` — checks
offboard-strip presence post-flip instead of the removed
"Welcome back" hero copy.
* fix(home): X-close button used invalid source enum, hit 422
The X button's data-target-source was 'self_acknowledged_x' to give
audit_log a separate marker for X-vs-button-driven flips. But
app/api/me.py:38's OnboardedRequest pins source to a Literal of
['agnes_init', 'self_acknowledged', 'self_unmark'] — pydantic
returned 422 on every X click.
Confusing side effect: both buttons share self-mark-status as the
status element, so the failed X click rendered 'Failed (422)' next
to the still-functional 'Mark me as onboarded' button. Looked like
the button itself broke.
Fix: drop the _x suffix. Both surfaces now POST source='self_acknowledged'.
Distinction in audit_log is not load-bearing — the source field
captures user intent ('I'm onboarded'), not the specific UI affordance.
This commit is contained in:
parent
471c63d711
commit
14ddaf1e8e
10 changed files with 239 additions and 109 deletions
36
CHANGELOG.md
36
CHANGELOG.md
|
|
@ -10,6 +10,42 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.54.6] — 2026-05-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Header brand: wired `instance.logo_svg` (yaml) /
|
||||
`AGNES_INSTANCE_LOGO_SVG` (env) into the brand slot via a new
|
||||
`get_instance_logo_svg()` helper in `app/instance_config.py`.
|
||||
Previously the yaml field was documented in
|
||||
`config/instance.yaml.example` and the template already supported
|
||||
inline SVG via `config.LOGO_SVG | safe`, but the router
|
||||
hard-coded `LOGO_SVG = ""` — operators can now drop inline SVG
|
||||
markup into their `instance.yaml` and have it appear in the
|
||||
header. `instance.name` continues to drive browser titles and
|
||||
page headings; the two fields are independent.
|
||||
- Header brand: clamped `.app-header-logo svg` to `max-height: 40px;
|
||||
width: auto;` (was just `display: block;`) so any operator's
|
||||
`logo_svg` scales via its viewBox to fit the 72px-tall header
|
||||
without per-asset width/height edits.
|
||||
- Header subtitle: empty `instance.subtitle` now renders nothing
|
||||
(the whole `<span class="app-header-subtitle">` is skipped)
|
||||
instead of falling back to the literal placeholder string
|
||||
"Data Analyst Portal". Operators who leave the field unset get a
|
||||
clean header instead of a stray hardcoded label.
|
||||
- `/home` install-hero now disappears entirely once the user is
|
||||
onboarded (`users.onboarded=true`, set by `agnes init`'s POST to
|
||||
`/api/me/onboarded` or by an explicit click). Pre-fix the hero
|
||||
kept rendering a "Welcome back — you're set up" variant that
|
||||
visually outweighed the actual nav hub. Adds a close (×) button
|
||||
in the top-right of the hero — confirms with a `window.confirm()`
|
||||
dialog asking the user to acknowledge onboarding before flipping
|
||||
state, so a stray click won't hide the setup steps. The
|
||||
offboarding escape hatch (previously living inside the hero's
|
||||
onboarded branch) moves to a discrete strip below — visible only
|
||||
when onboarded, so analysts who wipe `~/{{ workspace_dir }}` can
|
||||
flip back without digging through settings.
|
||||
|
||||
## [0.54.5] — 2026-05-13
|
||||
|
||||
### Internal
|
||||
|
|
|
|||
|
|
@ -278,6 +278,23 @@ def get_instance_brand() -> str:
|
|||
return value or "Agnes"
|
||||
|
||||
|
||||
def get_instance_logo_svg() -> str:
|
||||
"""Raw inline ``<svg>`` markup rendered into the header brand slot
|
||||
(``_app_header.html``). When non-empty, replaces the text brand in
|
||||
the header — typical use is a lockup that already contains the
|
||||
brand wordmark. When empty, the header falls back to
|
||||
:func:`get_instance_name` as text.
|
||||
|
||||
Resolution: ``AGNES_INSTANCE_LOGO_SVG`` env > ``instance.logo_svg``
|
||||
YAML > ``""``. Mirrors :func:`get_instance_brand` so Terraform env
|
||||
overrides work the same way.
|
||||
"""
|
||||
raw = os.environ.get("AGNES_INSTANCE_LOGO_SVG")
|
||||
if raw is None:
|
||||
raw = get_value("instance", "logo_svg", default="")
|
||||
return (raw or "").strip()
|
||||
|
||||
|
||||
def get_workspace_dir_name() -> str:
|
||||
"""Filesystem-safe folder name for the analyst's local workspace
|
||||
(``~/<workspace_dir_name>``). Defaults to :func:`get_instance_brand`
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ from app.instance_config import (
|
|||
get_gws_oauth_credentials, get_home_automode_visibility,
|
||||
get_instance_admin_email, get_atlassian_base_url,
|
||||
get_instance_brand, get_workspace_dir_name,
|
||||
get_instance_logo_svg,
|
||||
)
|
||||
from app.web.connector_prompts import all_connector_prompts
|
||||
from src.repositories.sync_state import SyncStateRepository
|
||||
|
|
@ -343,7 +344,7 @@ def _build_context(
|
|||
INSTANCE_NAME = get_instance_name()
|
||||
INSTANCE_SUBTITLE = get_instance_subtitle()
|
||||
INSTANCE_COPYRIGHT = ""
|
||||
LOGO_SVG = ""
|
||||
LOGO_SVG = get_instance_logo_svg()
|
||||
TELEGRAM_BOT_USERNAME = os.environ.get("TELEGRAM_BOT_USERNAME", "")
|
||||
SSH_ALIAS = "data-analyst"
|
||||
SERVER_HOST = os.environ.get("SERVER_HOST", "")
|
||||
|
|
|
|||
|
|
@ -2109,7 +2109,11 @@ a.slack-badge:hover {
|
|||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.app-header-logo svg { display: block; }
|
||||
.app-header-logo svg {
|
||||
display: block;
|
||||
max-height: 40px;
|
||||
width: auto;
|
||||
}
|
||||
a.app-header-logo:focus-visible {
|
||||
outline: 2px solid var(--primary, #6366f1);
|
||||
outline-offset: 2px;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<a class="app-header-logo" href="/" aria-label="Home">
|
||||
{% if config.LOGO_SVG %}{{ config.LOGO_SVG | safe }}{% else %}{{ config.INSTANCE_NAME or 'Data Analyst Portal' }}{% endif %}
|
||||
</a>
|
||||
<span class="app-header-subtitle">{{ config.INSTANCE_SUBTITLE or 'Data Analyst Portal' }}</span>
|
||||
{% if config.INSTANCE_SUBTITLE %}<span class="app-header-subtitle">{{ config.INSTANCE_SUBTITLE }}</span>{% endif %}
|
||||
</div>
|
||||
<div class="app-header-right">
|
||||
{% set _path = request.url.path %}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
.home-mock * { box-sizing: border-box; }
|
||||
|
||||
.home-mock .install-hero {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #0073D1 0%, #0056A3 100%);
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
|
|
@ -31,6 +32,51 @@
|
|||
margin-bottom: 22px;
|
||||
box-shadow: 0 8px 24px rgba(0, 86, 163, 0.18);
|
||||
}
|
||||
.home-mock .install-hero-close {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.14);
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.home-mock .install-hero-close:hover,
|
||||
.home-mock .install-hero-close:focus-visible {
|
||||
background: rgba(255, 255, 255, 0.28);
|
||||
outline: none;
|
||||
}
|
||||
.home-mock .offboard-strip {
|
||||
margin: 0 0 22px;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--hp-border);
|
||||
border-radius: 10px;
|
||||
background: var(--hp-border-light);
|
||||
color: var(--hp-text-secondary);
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
.home-mock .offboard-strip button {
|
||||
border: 1px solid var(--hp-border);
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.home-mock .offboard-strip button:hover { background: var(--hp-border-light); }
|
||||
.home-mock .install-hero .eyebrow {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
|
|
@ -1154,29 +1200,22 @@
|
|||
|
||||
{% set display_name = (user.name or (user.email or "").split("@")[0] or "there") %}
|
||||
|
||||
{# Install-hero renders only for not-onboarded users. Once `agnes init`
|
||||
POSTs /api/me/onboarded (or the user clicks the in-hero X) the hero
|
||||
disappears entirely — the rest of /home (connector tiles, news,
|
||||
etc.) stays. Offboarding escape hatch moved to a discrete strip
|
||||
below; see `.offboard-strip`. #}
|
||||
{% if not onboarded %}
|
||||
<div class="install-hero">
|
||||
{% if onboarded %}
|
||||
<div class="eyebrow">Welcome back, {{ display_name }} — your workspace is ready</div>
|
||||
<h1>You're set up — keep this page handy</h1>
|
||||
<p class="lead">
|
||||
Your local {{ instance_brand }} install is confirmed. The steps below stay useful for <strong>adding another machine</strong>, <strong>connecting more services</strong>, or <strong>turning on auto-accept mode</strong>. Skip whatever you don't need; nothing here re-runs unless you click it.
|
||||
</p>
|
||||
{% else %}
|
||||
<button type="button" class="install-hero-close" id="installHeroClose"
|
||||
data-target-source="self_acknowledged"
|
||||
aria-label="I'm already set up — close this setup hero">×</button>
|
||||
<div class="eyebrow">Welcome, {{ display_name }} — let's get you set up</div>
|
||||
<h1>Connect Claude Code on your machine to your team's data</h1>
|
||||
<p class="lead">
|
||||
{{ instance_brand }} gives <strong>Claude Code</strong> on your computer access to your team's <strong>curated data, plugins, and shared knowledge</strong> — so you can ask questions and get answers in plain language, right from your terminal. This page walks you through the <strong>one-time setup (~10 minutes)</strong>. Everything it installs lives in your home folder (<code style="background: rgba(255,255,255,0.12); padding: 1px 6px; border-radius: 4px; font-family: var(--hp-font-mono); font-size: 12.5px;">~/{{ workspace_dir }}</code>) and can be removed in one command.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if onboarded %}
|
||||
<div class="install-done" role="status" aria-live="polite">
|
||||
<span class="check" aria-hidden="true">✅</span>
|
||||
<span><strong>Steps 1–4 done</strong> — Claude Code installed, auto-mode set, workspace folder created, {{ instance_brand }} ready in <code>~/{{ workspace_dir }}</code>. The full install steps stay one click away under the offboard control below.</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not onboarded %}
|
||||
<div class="install-block">
|
||||
<div class="label">Step 1 — install Claude Code</div>
|
||||
<div class="os-tabs" role="tablist" aria-label="Operating system">
|
||||
|
|
@ -1281,53 +1320,45 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
|||
</details>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# P1-6 — auto-detect badge is the PRIMARY affordance after the
|
||||
install-script copy: agnes-init's first POST to
|
||||
/api/me/onboarded flips state automatically and the page
|
||||
reloads. The manual "Mark me as onboarded" button below it
|
||||
stays as a fallback when auto-flip never lands. #}
|
||||
{% if not onboarded %}
|
||||
<div class="auto-detect-badge" role="status" aria-live="polite">
|
||||
<span class="pulse" aria-hidden="true"></span>
|
||||
<span>Waiting for your first <code>agnes pull</code> — auto-detects within ~30 s of the setup script finishing.</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Self-mark control lives inside the blue hero in both states.
|
||||
When onboarded, the install steps above are hidden so this is
|
||||
the only thing rendered below the lead paragraph. #}
|
||||
{# Self-mark fallback for the auto-flip. The hero's X close button
|
||||
does the same thing more visibly; both target the not-onboarded
|
||||
→ onboarded direction. The onboarded → offboarded variant lives
|
||||
below the hero (.offboard-strip) so it stays reachable once the
|
||||
hero is gone. #}
|
||||
<div class="self-mark">
|
||||
{% if onboarded %}
|
||||
Wiped your workspace or want the full setup view back?
|
||||
<button id="self-mark-btn" type="button"
|
||||
data-target-onboarded="false"
|
||||
data-target-source="self_unmark">Mark me as offboarded</button>
|
||||
{% else %}
|
||||
Already set this up?
|
||||
<button id="self-mark-btn" type="button"
|
||||
data-target-onboarded="true"
|
||||
data-target-source="self_acknowledged">Mark me as onboarded</button>
|
||||
{% endif %}
|
||||
<span id="self-mark-status" class="status" role="status" aria-live="polite"></span>
|
||||
</div>
|
||||
|
||||
{% if onboarded %}
|
||||
{# User-controlled minimize toggle for Connect-your-tools.
|
||||
Default OFF (section renders flat). State persists in
|
||||
localStorage so the choice is per-device. The agnes-init
|
||||
auto-flip of users.onboarded never triggers a collapse on
|
||||
its own — only an explicit click here does. The auto-mode
|
||||
block used to be a peer collapsible (`step3`); it now lives
|
||||
inside the install-hero as Step 2 and is not collapsible. #}
|
||||
<div class="setup-minimize">
|
||||
<button id="setupMinimizeToggle" type="button" aria-pressed="false">
|
||||
Minimize setup view
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if onboarded %}
|
||||
{# Offboarding escape hatch shown only after the hero has disappeared.
|
||||
Lets the analyst (e.g. after wiping ~/FoundryAI) flip the
|
||||
users.onboarded boolean back to false so the full install hero
|
||||
renders again on next reload. Discrete by design — onboarded
|
||||
users land on /home expecting the nav hub, not a setup screen. #}
|
||||
<div class="offboard-strip">
|
||||
<span>Workspace ready — wiped it and need the full setup view back?</span>
|
||||
<button id="offboard-btn" type="button"
|
||||
data-target-source="self_unmark">Mark me as offboarded</button>
|
||||
<span id="offboard-status" class="status" role="status" aria-live="polite"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Auto-mode card used to live here as a `<details>` reference block;
|
||||
moved into the install-hero as the new Step 2 so users enable it
|
||||
|
|
@ -1638,16 +1669,13 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
|||
});
|
||||
}
|
||||
|
||||
var btn = document.getElementById('self-mark-btn');
|
||||
var status = document.getElementById('self-mark-status');
|
||||
if (!btn) return;
|
||||
btn.addEventListener('click', function () {
|
||||
// Direction comes from data-attrs the template sets per render —
|
||||
// onboarded view → flip to FALSE (offboard), not-onboarded → flip to TRUE.
|
||||
var targetOnboarded = btn.getAttribute('data-target-onboarded') === 'true';
|
||||
var targetSource = btn.getAttribute('data-target-source') || 'self_acknowledged';
|
||||
btn.disabled = true;
|
||||
status.textContent = targetOnboarded ? 'Marking…' : 'Resetting…';
|
||||
// Shared poster for /api/me/onboarded — reused by every UI surface
|
||||
// that flips users.onboarded (in-hero X close, "Mark me as onboarded"
|
||||
// fallback button, the offboard strip). Reloads on success so the
|
||||
// template re-renders with the new state.
|
||||
function postOnboarded(triggerBtn, statusEl, targetOnboarded, targetSource) {
|
||||
if (triggerBtn) triggerBtn.disabled = true;
|
||||
if (statusEl) statusEl.textContent = targetOnboarded ? 'Marking…' : 'Resetting…';
|
||||
fetch('/api/me/onboarded', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
|
|
@ -1655,17 +1683,59 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
|
|||
body: JSON.stringify({ source: targetSource, onboarded: targetOnboarded }),
|
||||
}).then(function (resp) {
|
||||
if (resp.ok) {
|
||||
status.textContent = 'Done. Reloading…';
|
||||
if (statusEl) statusEl.textContent = 'Done. Reloading…';
|
||||
window.location.reload();
|
||||
} else {
|
||||
status.textContent = 'Failed (' + resp.status + '). Try again.';
|
||||
btn.disabled = false;
|
||||
if (statusEl) statusEl.textContent = 'Failed (' + resp.status + '). Try again.';
|
||||
if (triggerBtn) triggerBtn.disabled = false;
|
||||
}
|
||||
}).catch(function () {
|
||||
status.textContent = 'Network error. Try again.';
|
||||
btn.disabled = false;
|
||||
if (statusEl) statusEl.textContent = 'Network error. Try again.';
|
||||
if (triggerBtn) triggerBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// "Mark me as onboarded" fallback button inside the hero (rendered
|
||||
// only when not-onboarded — the X close button is the primary path).
|
||||
var btn = document.getElementById('self-mark-btn');
|
||||
var status = document.getElementById('self-mark-status');
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
postOnboarded(
|
||||
btn, status, true,
|
||||
btn.getAttribute('data-target-source') || 'self_acknowledged'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Hero X close — confirm first so a stray click doesn't flip state.
|
||||
var heroClose = document.getElementById('installHeroClose');
|
||||
if (heroClose) {
|
||||
heroClose.addEventListener('click', function () {
|
||||
var ok = window.confirm(
|
||||
"Are you already onboarded? Closing this will mark your account as onboarded " +
|
||||
"and hide the setup steps. You can revert later from the strip below the hero."
|
||||
);
|
||||
if (!ok) return;
|
||||
postOnboarded(
|
||||
heroClose, status, true,
|
||||
heroClose.getAttribute('data-target-source') || 'self_acknowledged'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Offboarding strip — only rendered when onboarded. Flips back to
|
||||
// the install hero on next reload.
|
||||
var offBtn = document.getElementById('offboard-btn');
|
||||
var offStatus = document.getElementById('offboard-status');
|
||||
if (offBtn) {
|
||||
offBtn.addEventListener('click', function () {
|
||||
postOnboarded(
|
||||
offBtn, offStatus, false,
|
||||
offBtn.getAttribute('data-target-source') || 'self_unmark'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Minimize-setup toggle ────────────────────────────────────────
|
||||
// Default OFF: sections render flat (no <summary> visible).
|
||||
|
|
|
|||
|
|
@ -28,8 +28,14 @@ instance:
|
|||
# "FoundryAI"). Set explicitly only if you want a folder
|
||||
# name that differs from the auto-derivation. Env override:
|
||||
# AGNES_WORKSPACE_DIR_NAME.
|
||||
# logo_svg: Full <svg> element for header logo (optional, default: Keboola logo)
|
||||
# Example: '<svg width="120" height="30" viewBox="0 0 100 30" xmlns="http://www.w3.org/2000/svg"><text y="22" font-size="24" fill="#333">Logo</text></svg>'
|
||||
# logo_svg: | # Inline <svg> element rendered into the header brand slot.
|
||||
# <svg width="120" height="30" viewBox="0 0 100 30" xmlns="http://www.w3.org/2000/svg">
|
||||
# <text y="22" font-size="24" fill="#333">Logo</text>
|
||||
# </svg>
|
||||
# # When set, the SVG replaces the text brand in the header.
|
||||
# # `name` above still drives browser <title> text and page
|
||||
# # headings — keep it populated. Env override:
|
||||
# # AGNES_INSTANCE_LOGO_SVG.
|
||||
# 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
|
||||
# an "Email admin" mailto button (analysts whose operator
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "agnes-the-ai-analyst"
|
||||
version = "0.54.5"
|
||||
version = "0.54.6"
|
||||
description = "Agnes — AI Data Analyst platform for AI analytical systems"
|
||||
requires-python = ">=3.11,<3.14"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -75,11 +75,14 @@ def test_home_not_onboarded_user_sees_setup_view(fresh_db):
|
|||
|
||||
|
||||
def test_home_onboarded_user_sees_nav_hub(fresh_db):
|
||||
"""A TRUE-onboarded user gets the post-onboarding view, identifiable by
|
||||
the 'Welcome back' hero, the 'Step 1 & Step 2 done' completion badge,
|
||||
the offboard control, and the absence of the inline Step 1 / Step 2
|
||||
install commands. Step 3 (auto-mode), connectors, and the rest stay
|
||||
visible — they remain useful after onboarding."""
|
||||
"""A TRUE-onboarded user gets the post-onboarding view: the blue
|
||||
install-hero is gone entirely (no welcome banner, no completion
|
||||
badge, no inline step commands), the offboard escape strip is the
|
||||
only setup-flow remnant rendered, and the rest of /home (connector
|
||||
tiles, news, etc.) stays. PR #289 collapsed the dual-state hero
|
||||
into a single not-onboarded-only render — pre-PR the onboarded
|
||||
branch reused the same `.install-hero` shell with welcome copy
|
||||
and a "Steps 1–4 done" badge."""
|
||||
from src.db import get_system_db, close_system_db
|
||||
|
||||
conn = get_system_db()
|
||||
|
|
@ -93,13 +96,11 @@ def test_home_onboarded_user_sees_nav_hub(fresh_db):
|
|||
resp = c.get("/home", cookies={"access_token": sess})
|
||||
assert resp.status_code == 200
|
||||
body = resp.text
|
||||
assert "Welcome back" in body
|
||||
# Banner copy updated when the explicit "create workspace folder"
|
||||
# step was inserted between auto-mode and install-Agnes — completion
|
||||
# badge now spans Steps 1-4 (install Claude Code, auto-mode, mkdir
|
||||
# workspace, install Agnes from Claude Code).
|
||||
assert "Steps 1–4 done" in body or "Steps 1–4 done" in body
|
||||
assert "Mark me as offboarded" in body # offboard control visible
|
||||
# Install hero entirely absent for onboarded users.
|
||||
assert '<div class="install-hero">' not in body
|
||||
# Offboard escape strip + its button replace the in-hero self-mark control.
|
||||
assert '<div class="offboard-strip">' in body
|
||||
assert "Mark me as offboarded" in body
|
||||
# All four inline install-blocks are hidden post-onboarding — the
|
||||
# labels rendered inside the install-block divs go away.
|
||||
assert "Step 1 — install Claude Code" not in body
|
||||
|
|
@ -152,16 +153,22 @@ def test_connectors_render_flat_when_onboarded_by_default(fresh_db):
|
|||
assert 'class="home-mock"\n' in body or '<div class="home-mock">' in body
|
||||
|
||||
|
||||
def test_minimize_toggle_visible_only_when_onboarded(fresh_db):
|
||||
"""The "Minimize setup view" toggle markup is rendered for onboarded
|
||||
users (so they can opt into the collapsed view) and absent for
|
||||
not-onboarded users (where the install steps already dominate)."""
|
||||
def test_minimize_toggle_no_longer_rendered(fresh_db):
|
||||
"""The "Minimize setup view" toggle used to live inside the
|
||||
onboarded-branch of the install-hero. PR #289 hides the hero
|
||||
entirely once `users.onboarded=true`, so the minimize toggle
|
||||
has no rendering site anymore — verify the markup is absent
|
||||
from both states. (The localStorage `agnes_home_setup_minimized`
|
||||
flag and its applyMinimize() JS handler stay in the page so a
|
||||
stale flag from a pre-PR session no-ops cleanly.)"""
|
||||
from src.db import get_system_db, close_system_db
|
||||
|
||||
# Not-onboarded → no toggle button.
|
||||
for onboarded in (False, True):
|
||||
conn = get_system_db()
|
||||
try:
|
||||
_, sess = _make_user_and_session(conn, onboarded=False)
|
||||
_, sess = _make_user_and_session(
|
||||
conn, email=f"user-{onboarded}@example.com", onboarded=onboarded
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
close_system_db()
|
||||
|
|
@ -171,19 +178,6 @@ def test_minimize_toggle_visible_only_when_onboarded(fresh_db):
|
|||
assert '<button id="setupMinimizeToggle"' not in resp.text
|
||||
assert 'class="setup-minimize"' not in resp.text
|
||||
|
||||
# Onboarded → toggle button rendered inside the install-hero.
|
||||
conn = get_system_db()
|
||||
try:
|
||||
_, sess2 = _make_user_and_session(conn, email="b@example.com", onboarded=True)
|
||||
finally:
|
||||
conn.close()
|
||||
close_system_db()
|
||||
c2 = _client()
|
||||
resp2 = c2.get("/home", cookies={"access_token": sess2})
|
||||
assert resp2.status_code == 200
|
||||
assert '<button id="setupMinimizeToggle"' in resp2.text
|
||||
assert 'class="setup-minimize"' in resp2.text
|
||||
|
||||
|
||||
def test_home_no_auto_transition_after_post_until_reload(fresh_db):
|
||||
"""POST /api/me/onboarded flips the flag in the DB but the in-flight
|
||||
|
|
@ -213,7 +207,9 @@ def test_home_no_auto_transition_after_post_until_reload(fresh_db):
|
|||
assert flip.status_code == 200
|
||||
|
||||
post = c.get("/home", cookies={"access_token": sess})
|
||||
assert "Welcome back" in post.text # nav hub view
|
||||
# PR #289: hero disappears entirely; offboard strip is the
|
||||
# only setup-flow remnant. Use either as the nav-hub view marker.
|
||||
assert '<div class="offboard-strip">' in post.text
|
||||
assert 'class="install-block"' not in post.text
|
||||
|
||||
|
||||
|
|
|
|||
2
uv.lock
2
uv.lock
|
|
@ -24,7 +24,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "agnes-the-ai-analyst"
|
||||
version = "0.54.1"
|
||||
version = "0.54.6"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "a2wsgi" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue