From 3d244038b5cc33b679f89aed74d17fdf910091d5 Mon Sep 17 00:00:00 2001 From: Vojtech <119944107+cvrysanek@users.noreply.github.com> Date: Thu, 14 May 2026 11:02:23 +0400 Subject: [PATCH] feat(home): Getting Started moves first, collapsible, in-page anchor (#296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three tweaks to the post-PR-#291 Getting Started card: 1. Chronologically first. Moved from below the install-hero (where it sat as a static white card) to ABOVE it, inside the same `{% if not onboarded %}` guard. The blue hero is now the actual install flow that the card points at, not a peer that competes for attention. 2. Collapsed by default. Switched from
to
with no `open` attribute, so the page lands with just a quiet pill (`Getting Started — Two quick next steps — click to expand ›`). Expand to reveal the two rows. Chevron rotates 90deg when open via the `[open]` selector. Per-device dismiss X stays — generic `.home-card-close[data-dismiss-key]` handler now uses `closest('section, details')` so it works on both container types. 3. First row → #install-hero in-page anchor. Was `/setup` (which would round-trip to the same hero via a redirect through /setup). Anchored directly to the blue hero on the same page; copy reads "One-time install — walkthrough in the section below" so the user knows it's a scroll-to, not a navigation. Install-hero
gained `id="install-hero"`. `.install-hero { scroll-margin-top: 88px }` keeps the hero's eyebrow clear of the 72px sticky header on the jump. Second row link to /setup-advanced and the dismiss key unchanged. GS disappears alongside the install-hero when the user is onboarded, so the in-page anchor never dangles. Tests updated to assert the new markup + onboarded-state hiding. --- app/web/templates/home_not_onboarded.html | 121 ++++++++++++++++------ tests/test_web_home_page.py | 60 +++++++---- 2 files changed, 129 insertions(+), 52 deletions(-) diff --git a/app/web/templates/home_not_onboarded.html b/app/web/templates/home_not_onboarded.html index 654bb07..826a263 100644 --- a/app/web/templates/home_not_onboarded.html +++ b/app/web/templates/home_not_onboarded.html @@ -120,7 +120,53 @@ margin-bottom: 18px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); } -.home-mock .home-getting-started > header h2, +/* Getting Started uses
for native collapsed-by-default + behaviour. The summary owns the padding when collapsed; row layout + reveals on expand. Chevron rotates 90deg when open. */ +.home-mock details.home-getting-started { + padding: 0; +} +.home-mock .home-gs-summary { + list-style: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 12px; + padding: 16px 24px; + user-select: none; +} +.home-mock .home-gs-summary::-webkit-details-marker { display: none; } +.home-mock .home-gs-summary-title { + font-size: 16px; + font-weight: 600; + color: var(--hp-text-primary); +} +.home-mock .home-gs-summary-hint { + flex: 1; + font-size: 13px; + color: var(--hp-text-secondary); +} +.home-mock .home-gs-summary-chev { + font-size: 18px; + color: var(--hp-text-muted); + transition: transform 0.15s; +} +.home-mock details.home-getting-started[open] .home-gs-summary-chev { + transform: rotate(90deg); +} +.home-mock details.home-getting-started[open] { + padding-bottom: 18px; +} +.home-mock details.home-getting-started[open] .home-gs-item { + margin-left: 24px; + margin-right: 24px; +} +/* Install-hero is the scroll target for Getting Started's first row. + Offset the anchor jump by the 72px sticky .app-header height + a + bit of breathing room so the hero's eyebrow lands cleanly under + the header bar. */ +.home-mock .install-hero { scroll-margin-top: 88px; } + .home-mock .home-overview > h2, .home-mock .home-usage > header h2 { font-size: 18px; @@ -128,7 +174,6 @@ 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); @@ -1355,7 +1400,42 @@ etc.) stays. Offboarding escape hatch moved to a discrete strip below; see `.offboard-strip`. #} {% if not onboarded %} -
+ {# Getting Started renders FIRST in the not-onboarded flow as a + collapsed-by-default
. Click the summary to expand the + two-row map. First row anchors back to the install hero just + below (#install-hero); second row leaves the page for + /setup-advanced. Per-device dismiss X (data-dismiss-key) survives + on the generic .home-card-close handler — selector widened in JS + to accept
containers too. Disappears post-onboarding + alongside the hero so the in-page anchor never dangles. #} +
+ + Getting Started + Two quick next steps — click to expand + + + + + +
+ Setup {{ instance_brand }} in your Claude Code + One-time install — walkthrough in the section below. +
+ +
+ + +
+ Go deeper into your AI workspace + VS Code layout, recommended plugins, multi-model second opinions, custom skills + rules + hooks, project workflows. +
+ +
+
+ +
@@ -1514,35 +1594,10 @@ Set-Location "$HOME\{{ workspace_dir }}" BEFORE Step 3's install runs ~20 commands. Gated by the same `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. #} -
- -
-

Getting Started

-

Two quick next steps to get the most out of {{ instance_brand }}.

-
- - -
- Setup {{ instance_brand }} in your Claude Code - One-time install: copies a setup script to your clipboard, paste into Claude Code, done in ~10 minutes. -
- -
- - -
- Go deeper into your AI workspace - VS Code layout, recommended plugins, multi-model second opinions, custom skills + rules + hooks, project workflows. -
- -
-
+ {# Getting Started was previously rendered HERE (between the offboard + strip and Overview) as a full-card
. Moved up to render + BEFORE the install-hero as a collapsed-by-default
— see + the block right after `{% if not onboarded %}` near line ~1357. #} {# Overview section — operator-owned, opt-in. Body comes from the `instance.overview` yaml field via get_instance_overview() @@ -2015,7 +2070,7 @@ Set-Location "$HOME\{{ workspace_dir }}" // 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'); + var section = btn.closest('section, details'); if (!section || !key) return; try { if (localStorage.getItem(key) === '1') { diff --git a/tests/test_web_home_page.py b/tests/test_web_home_page.py index b13c6b5..ec46b05 100644 --- a/tests/test_web_home_page.py +++ b/tests/test_web_home_page.py @@ -335,27 +335,49 @@ def test_home_renders_connector_prompts_from_shared_module(fresh_db): 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).""" + """The dismissible Getting Started card now renders BEFORE the + install-hero (chronologically first in the not-onboarded flow) as + a
element — collapsed by default so the install hero + stays visible on first paint. Disappears when the user is + onboarded (no `
`) so the + in-page #install-hero anchor on the first row never points at + nothing. First row links to #install-hero (same-page jump to the + blue setup hero); second row still leaves the page for + /setup-advanced.""" 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 '
' in body + + # Onboarded: install-hero is gone, GS rides alongside it — neither + # renders. Prevents a dangling #install-hero anchor. + conn = get_system_db() + try: + _, sess2 = _make_user_and_session( + conn, email="gs-onboarded@example.com", onboarded=True + ) + finally: + conn.close() + close_system_db() + body2 = _client().get("/home", cookies={"access_token": sess2}).text + assert '