Files
ClaudeDo/docs/superpowers/specs/2026-06-04-marketing-website-design.md
mika kuns a2c339cd87 docs(web): add ClaudeDo distribution website design spec
Approved design for a Nuxt 3 site at claudedo.kuns.dev: "the page is the
app" concept (3-island layout), build-time release fetch, and a Nitro
release proxy that fronts the self-updater to hide the Gitea URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 20:07:58 +02:00

201 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ClaudeDo distribution website — design
**Date:** 2026-06-04
**Status:** Approved (design), ready for implementation planning
**Repo:** new standalone repo `claudedo-web` (not part of the ClaudeDo app solution)
**Domain:** `claudedo.kuns.dev` (Coolify on the user's VPS)
## Purpose
Give friends a public place to download ClaudeDo and learn what it does, without
sending them to the Gitea repo — so the source repo can be made more private. The
site also fronts the app's self-updater so the Gitea URL is never exposed in the
app or on the page.
## Goals / non-goals
**Goals**
- Public, no-auth landing page at `claudedo.kuns.dev` that matches the app's visual identity.
- A primary download (installer `.exe`) plus the portable `.zip` and checksums.
- A release proxy that (a) feeds the page the current version and (b) serves the
app's self-updater the same JSON shape Gitea returns, with download URLs rewritten
to route through `claudedo.kuns.dev` — hiding Gitea entirely.
**Non-goals**
- No docs site / getting-started page (the app ships an installer that handles setup).
- No changelog page (release notes already live on Gitea releases).
- No auth, accounts, analytics, or CMS.
- No CI/PR tooling for this repo beyond what Coolify needs to deploy.
## Access & distribution decisions
- **Access:** fully public. No password/login. Relies on the unadvertised URL.
- **Download source:** build-time fetch of the latest Gitea release for the displayed
version; actual download links route through the proxy and resolve the latest asset
at request time (so a stale page still downloads the current build).
- **Self-updater proxy:** in scope for v1 (not deferred).
## Tech stack
- **Nuxt 3** (Vue 3) — single framework, single repo, single Coolify deploy.
- **Nitro** server routes for the release proxy + asset streaming.
- **No DB, no auth, no secrets** (the `releases/ClaudeDo` repo is public).
- Fonts: **Inter Tight** (display/body) + **JetBrains Mono** (mono), self-hosted or via
Google Fonts. Design tokens ported from `docs/UI Rewrite/design_handoff_claudedo/Tokens.axaml`
and `styles.css` (moss/sage/peat palette, dark-first, 14px island radius, grain texture).
## Concept: "the page IS the app"
The landing page is a faithful, in-browser rendering of the ClaudeDo desktop — the
window chrome + the three islands — rather than a conventional marketing page. This
is the chosen direction (over a cinematic single-window scroll and a worklog feed).
### Layout — three islands on the app "desktop"
Desktop background = the app's layered moss gradients + 3px grain overlay. A centered
app `window` (titlebar + body) holds a 3-column island grid:
1. **Lists island (left)** — repurposed as page nav.
- Header "Lists" + a decorative search box (`Ctrl K` kbd chip).
- "Pages" group: Overview · Features (6) · How it works · Screenshots (3) · Download,
each with a colored swatch dot; active item gets the accent left-bar.
- Footer styled like the app's user footer: avatar, "For friends", `claudedo.kuns.dev`.
2. **Tasks island (middle)** — features rendered as **task cards**.
- Header: date eyebrow, big title (the hero line "Queue the work. Claude does it."),
a `running · review` badge, eye/gear icon buttons, and a subtitle.
- A decorative "Add a task…" row.
- Six **feature cards** (circle check — done cards filled; title; a status chip
`done`/`running`/`waiting for review`; a star). The features:
1. Isolated worktrees
2. The task queue
3. Review & merge
4. Live session log
5. Per-list & per-task config
6. Self-updating
- A "Ready" group with the final **"↓ Download ClaudeDo"** card.
3. **Detail island (right)** — faithful to the reworked Task-Detail island.
- **Source of truth for the detail visuals:** `docs/superpowers/specs/2026-06-04-task-detail-redesign-design.md`
(the app's in-progress rework). The website's detail pane must track that design.
- Three-zone stack:
- **Task header** — mono id (`#F01…`), title, trash/gear action icons.
- **DETAILS bar** — `DETAILS` eyebrow + `Edit` / copy / `⋯`, then a **markdown body**
(headings, paragraphs, inline `code`, ordered/unordered lists) describing the feature.
- **WorkConsole** docked at the bottom — traffic-light dots, `· N turns · +x y`,
tabs **Output / Actions / Session**, and a `Created …` footer.
- Per-feature mapping:
- Feature panels: markdown writeup in DETAILS + a short relevant **Output** log.
- "Review & merge": opens on the **Actions** tab with `Merge target` + `Open Diff` /
`Approve & merge`.
- **Download**: DETAILS shows requirements (`.NET 8 Desktop Runtime`, `Claude CLI`,
`Git`); the **Actions** tab holds the install controls, with `Merge target`
repurposed as a **Build** selector and buttons `↓ Download installer` /
`Portable .zip` / `checksums.txt`.
4. **Statusbar (bottom)**`● Online · claudedo.kuns.dev · private build`.
### Interaction
- Clicking a task card selects it (accent bar + card highlight) and swaps the active
detail panel; the WorkConsole tabs are clickable within a panel.
- **All panels are server-rendered and present in the DOM**, toggled by class — the
page is fully readable and downloadable **without JavaScript** (progressive
enhancement). Vue handles the selection state on the client.
### Responsive
- ≤ ~1100px: drop the Detail island; show Lists + Tasks.
- ≤ ~780px: single column — the Tasks list; tapping a feature pushes to a full-screen
Detail view (mirrors the app's narrow-window behavior) with a back affordance.
## Server: release proxy (Nitro)
The app's `ReleaseClient` (`src/ClaudeDo.Releases/ReleaseClient.cs`) calls
`{apiBase}/releases/latest` and reads `tag_name`, `name`, and
`assets[].browser_download_url`; `DownloadAsync` GETs an asset URL directly.
- **`GET /api/releases/latest`** — fetches `https://git.kuns.dev/api/v1/repos/releases/ClaudeDo/releases/latest`,
returns the **same JSON shape**, but every `assets[].browser_download_url` is rewritten
from the Gitea URL to `https://claudedo.kuns.dev/api/download/<encoded-asset-path>`.
Cached briefly (e.g. 5 min) server-side.
- **`GET /api/download/[...path]`** — reconstructs the Gitea asset URL from the path and
**streams** the binary back (no redirect to Gitea, so the URL stays hidden). Sets
appropriate `Content-Type`/`Content-Disposition`.
- **`server/utils/gitea.ts`** — shared base URL (`GITEA_API`, `REPO` from env), fetch
helper, and the URL-rewrite/asset-path round-trip.
- The page's download buttons point at the same `/api/download/...` routes (with a
stable "latest installer" path), so links never go stale between deploys.
### App-side coordinating change (separate, in the ClaudeDo repo)
Point `ReleaseClient`'s `apiBase` at `https://claudedo.kuns.dev/api` instead of the
Gitea default (one-line DI change where `ReleaseClient`/`UpdateCheckService` are
constructed). Tracked as a follow-up; not part of the `claudedo-web` repo. The proxy
path (`/api/releases/latest`) is chosen to match the existing
`{apiBase}/releases/latest` call so the parser is untouched.
## Content / assets
- **Screenshots (provided):** main 3-column view (hero), diff review modal, worktrees
panel. Stored in `public/screenshots/`. Placeholders sized for them until dropped in.
- Copy: hero "Queue the work. Claude does it."; six feature writeups as above (final
wording during implementation).
## Error handling
- **Build-time release fetch fails:** render the page with a last-known/placeholder
version label; download buttons still work because they resolve via the runtime
proxy route.
- **Proxy `/api/releases/latest` upstream failure:** return a 502/`null`-equivalent the
way Gitea would on miss; the app's `UpdateCheckService` already treats null/exception
as `CheckFailed` and degrades gracefully.
- **`/api/download` upstream failure:** surface a 502; the button shows an error state.
- No retries beyond a single upstream attempt for v1 (low traffic, friends-only).
## Testing
- **Vitest** unit tests for `server/utils/gitea.ts`: URL rewrite (Gitea → proxy) and the
asset-path round-trip (proxy path → Gitea URL), and release-JSON shape preservation.
- A light component smoke test that the page renders the islands and the download
controls without JS errors.
- No real-network/Gitea calls in tests — mock the upstream fetch.
## Deployment (Coolify)
- **Dockerfile**: `node:20-alpine` build → `nuxt build` → run `.output/server/index.mjs`.
- Coolify app bound to `claudedo.kuns.dev` with TLS via its reverse proxy.
- Env: `GITEA_API` (default `https://git.kuns.dev/api/v1`), `REPO` (`releases/ClaudeDo`),
`PUBLIC_BASE_URL` (`https://claudedo.kuns.dev`) for URL rewriting.
- Deploy on push to `main`; re-deploy (or a periodic rebuild) refreshes the displayed
version. No PR/CI tooling beyond Coolify's build.
## Open risk
- The reworked Detail island in the app is still in flux. The website's detail pane
must be kept in sync with `2026-06-04-task-detail-redesign-design.md`; expect a
visual-polish pass once that rework lands.
## Repo layout
```
claudedo-web/
├── nuxt.config.ts
├── app.vue
├── pages/index.vue # the one landing page
├── components/
│ ├── AppWindow.vue # window chrome + statusbar
│ ├── ListsIsland.vue # page nav
│ ├── TasksIsland.vue # feature cards + download card
│ ├── DetailIsland.vue # three-zone detail (header / DETAILS md / WorkConsole)
│ ├── WorkConsole.vue # tabs: Output / Actions / Session
│ └── content/ # per-feature markdown/blurbs + download panel
├── server/
│ ├── api/releases/latest.get.ts
│ ├── api/download/[...path].get.ts
│ └── utils/gitea.ts
├── assets/css/tokens.css # palette + type ported from Tokens.axaml/styles.css
├── public/screenshots/ # 3 PNGs
└── Dockerfile
```