Skip to content

Commit 3ad70c0

Browse files
feat(0.5.0)!: retire the legacy React UI
The Cowork dashboard at / has been the only thing anyone uses since 0.4.0 shipped. Time to drop the dual-UI maintenance burden. Removed: - ui/ (React + Vite + node_modules sources) - sidecar/attune_gui/static/ (the bundled React build that shipped in the wheel) - build_hooks.py (npm-driven hatchling hook) - scripts/dev.sh (Vite + sidecar dual-runner) - /legacy/ URL route + _mount_legacy_ui in app.py - 'Legacy UI ↗' link in the sidebar + .legacy-link CSS rules - sdist 'ui/*' includes + [tool.hatch.*.hooks.custom] blocks in pyproject.toml Kept: - Every JSON API (/api/*) exactly as before - The Cowork dashboard at / (Health, Templates, Specs, Summaries, Living Docs, Commands, Jobs) - All 124 tests, all passing Build impact: - Wheel size: 122K → 65K (-47%) - Sdist size: 139K → 62K (-55%) - No more 'npm install' required to build the wheel - No Node/Vite anywhere in the toolchain Anyone with a /legacy/ bookmark gets a 404. Anyone who needs the React UI for any reason can install attune-gui==0.4.0. Verified end-to-end: ruff clean, 124 tests pass, wheel installs cleanly in an isolated env, all routes resolve as expected (/ → 307 /dashboard, /legacy → 404). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b570bdf commit 3ad70c0

28 files changed

Lines changed: 76 additions & 4097 deletions

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,40 @@
33
All notable changes to `attune-gui` are documented here.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
55

6+
## [0.5.0] — 2026-05-04
7+
8+
### Removed — legacy React UI
9+
10+
The React/Vite UI has been retired. The Cowork dashboard at `/` is now
11+
the only surface. This was always the plan from the `cowork-dashboard`
12+
spec; 0.4.0 ran the two side-by-side to give you time to vet the new
13+
one. With everything verified in production, the bundled React assets
14+
are dead weight.
15+
16+
What goes away:
17+
- `/legacy/` URL route — anyone with a bookmark gets a 404.
18+
- `ui/` source tree (React + Vite + node_modules) — no more `npm install`
19+
to build the wheel.
20+
- `sidecar/attune_gui/static/` — the built React assets that shipped in
21+
the wheel. Wheel size drops dramatically as a result.
22+
- `build_hooks.py` — the hatchling hook that drove `npm run build`.
23+
- `scripts/dev.sh` — the dual-runner script.
24+
- The "Legacy UI ↗" link in the dashboard sidebar.
25+
26+
What stays the same:
27+
- All JSON APIs (`/api/*`) — including the old ones that powered the
28+
React UI. Any external scripts hitting those continue to work.
29+
- The Cowork dashboard at `/` — same routes, same look.
30+
- Tests (124, all passing) — none of them hit the legacy mount.
31+
32+
If you need the old React UI for any reason, install `attune-gui==0.4.0`.
33+
34+
### Changed
35+
- `pyproject.toml` description updated; sdist no longer includes `ui/*`
36+
or `build_hooks.py`.
37+
38+
---
39+
640
## [0.4.0] — 2026-05-04
741

842
### Added — Spec authoring

README.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ Sidebar nav with seven pages, each consuming the existing JSON API:
2222
Click any spec or template to open the **Preview / Edit** panel — server-side
2323
Markdown rendering plus a raw `<textarea>` for editing.
2424

25-
> Prefer the React UI? It's still bundled and reachable at
26-
> `/legacy/`. Both surfaces talk to the same FastAPI sidecar.
27-
2825
> Looking for AI dev workflows (code review, security audits, refactor
2926
> planning, multi-agent orchestration)? Those live in
3027
> [`attune-ai`](https://pypi.org/project/attune-ai/) — a separate
@@ -81,17 +78,14 @@ uv sync
8178
uv run attune-gui --port 8765 --reload
8279
```
8380

84-
For HMR work on the React UI at `/legacy/`:
85-
86-
```bash
87-
cd ui && npm install && cd ..
88-
./scripts/dev.sh # starts sidecar + Vite dev server
89-
```
81+
Templates auto-reload — edit anything under `sidecar/attune_gui/templates/`
82+
and refresh the browser. Python code changes reload automatically with
83+
`--reload`.
9084

9185
### Tests
9286

9387
```bash
94-
uv run pytest # 105 tests, ~2s
88+
uv run pytest # 124 tests, ~2s
9589
uv run ruff check . # lint
9690
```
9791

@@ -100,7 +94,6 @@ uv run ruff check . # lint
10094
```
10195
┌──────────────────────────────────────┐
10296
│ Cowork dashboard (Jinja2) / │
103-
│ Legacy React UI /legacy/ │
10497
└──────────────────┬───────────────────┘
10598
│ /api/*
10699
┌──────────────────▼───────────────────┐

build_hooks.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

pyproject.toml

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "attune-gui"
7-
version = "0.4.0"
8-
description = "Local dashboard for attune-rag / attune-help / attune-author. Server-rendered Jinja2 UI (Cowork) with React UI preserved at /legacy/."
7+
version = "0.5.0"
8+
description = "Local dashboard for attune-rag / attune-help / attune-author. Server-rendered Jinja2 UI — ships clean via PyPI with no npm step."
99
readme = "README.md"
1010
requires-python = ">=3.10"
1111
license = {text = "Apache-2.0"}
@@ -61,25 +61,13 @@ packages = ["sidecar/attune_gui"]
6161
[tool.hatch.build.targets.sdist]
6262
include = [
6363
"sidecar/attune_gui",
64-
"ui/src",
65-
"ui/index.html",
66-
"ui/package.json",
67-
"ui/package-lock.json",
68-
"ui/vite.config.js",
69-
"scripts",
70-
"build_hooks.py",
64+
"sidecar/tests",
7165
"README.md",
7266
"CHANGELOG.md",
7367
"LICENSE",
7468
"pyproject.toml",
7569
]
7670

77-
[tool.hatch.build.targets.wheel.hooks.custom]
78-
path = "build_hooks.py"
79-
80-
[tool.hatch.build.targets.sdist.hooks.custom]
81-
path = "build_hooks.py"
82-
8371
[tool.ruff]
8472
line-length = 100
8573
target-version = "py310"

scripts/dev.sh

Lines changed: 0 additions & 35 deletions
This file was deleted.

sidecar/.help/features.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
version: 1
2+
features:
3+
attune-gui:
4+
description: "attune-gui \u2014 local FastAPI sidecar that drives attune-rag,\
5+
\ attune-author, and (later) attune-ai."
6+
files:
7+
- attune_gui/**
8+
attune-gui.egg-info:
9+
description: Attune Gui.Egg Info
10+
files:
11+
- attune_gui.egg-info/**
12+
attune_gui-entry:
13+
description: 'Entry point: attune_gui/app.py'
14+
files:
15+
- attune_gui/app.py
16+
tags:
17+
- entry-point
18+
tests:
19+
description: Tests
20+
files:
21+
- tests/**
22+
tags:
23+
- testing

sidecar/attune_gui/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
from __future__ import annotations
44

5-
__version__ = "0.4.0"
5+
__version__ = "0.5.0"

sidecar/attune_gui/app.py

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""FastAPI app factory — wires routes, CORS, and the origin guard.
22
3-
Mounts the new Cowork dashboard at ``/`` (Jinja2 server-rendered) and keeps
4-
the legacy React UI available at ``/legacy/`` for fallback.
3+
Serves the Cowork dashboard (Jinja2) at ``/`` and the JSON APIs under
4+
``/api/*``. The legacy React UI was retired in 0.5.0.
55
"""
66

77
from __future__ import annotations
@@ -11,7 +11,7 @@
1111

1212
from fastapi import Depends, FastAPI
1313
from fastapi.middleware.cors import CORSMiddleware
14-
from fastapi.responses import HTMLResponse, PlainTextResponse
14+
from fastapi.responses import PlainTextResponse
1515
from fastapi.staticfiles import StaticFiles
1616

1717
from attune_gui import __version__
@@ -34,10 +34,8 @@
3434

3535
logger = logging.getLogger(__name__)
3636

37-
# Package-relative dirs.
38-
_PKG_DIR = Path(__file__).parent
39-
_STATIC_DIR = _PKG_DIR / "static" # legacy React build output
40-
_CW_STATIC_DIR = _PKG_DIR / "static_cw" # Cowork dashboard CSS/JS
37+
# Cowork dashboard CSS/JS lives next to the package.
38+
_CW_STATIC_DIR = Path(__file__).parent / "static_cw"
4139

4240

4341
def create_app() -> FastAPI:
@@ -58,7 +56,7 @@ def create_app() -> FastAPI:
5856
allow_headers=["*"],
5957
)
6058

61-
# ---- JSON APIs (existing) -------------------------------------------------
59+
# ---- JSON APIs (existing) -----------------------------------------------
6260
app.include_router(system.router)
6361
app.include_router(fs.router)
6462
app.include_router(rag.router)
@@ -78,8 +76,10 @@ def create_app() -> FastAPI:
7876
if _CW_STATIC_DIR.is_dir():
7977
app.mount("/cw-static", StaticFiles(directory=_CW_STATIC_DIR), name="cw-static")
8078

81-
# ---- Legacy React UI at /legacy/ ----------------------------------------
82-
_mount_legacy_ui(app)
79+
# ---- robots --------------------------------------------------------------
80+
@app.get("/robots.txt", response_class=PlainTextResponse, include_in_schema=False)
81+
async def _robots() -> str:
82+
return "User-agent: *\nDisallow: /\n"
8383

8484
# ---- Cowork HTML pages (registered LAST so /api/* still resolves first) -
8585
# The pages router defines /, /dashboard, /dashboard/<page>. Specific
@@ -88,37 +88,3 @@ def create_app() -> FastAPI:
8888
app.include_router(cowork_pages.router)
8989

9090
return app
91-
92-
93-
def _mount_legacy_ui(app: FastAPI) -> None:
94-
"""Mount the React UI at ``/legacy/`` so the new dashboard owns ``/``."""
95-
if _STATIC_DIR.is_dir() and (_STATIC_DIR / "index.html").is_file():
96-
app.mount(
97-
"/legacy",
98-
StaticFiles(directory=_STATIC_DIR, html=True),
99-
name="legacy-ui",
100-
)
101-
return
102-
103-
# Development fallback: serve the Vite build output.
104-
repo_root = Path(__file__).resolve().parents[2]
105-
dist_ui = repo_root / "ui" / "dist"
106-
if dist_ui.is_dir() and (dist_ui / "index.html").is_file():
107-
app.mount(
108-
"/legacy",
109-
StaticFiles(directory=dist_ui, html=True),
110-
name="legacy-ui-dist",
111-
)
112-
return
113-
114-
@app.get("/legacy", response_class=HTMLResponse)
115-
async def _legacy_placeholder() -> str:
116-
return (
117-
"<h1>Legacy React UI not built</h1>"
118-
"<p>Run <code>cd ui && npm run build</code> to enable the legacy UI.</p>"
119-
"<p><a href='/dashboard'>← Back to dashboard</a></p>"
120-
)
121-
122-
@app.get("/robots.txt", response_class=PlainTextResponse)
123-
async def _robots() -> str:
124-
return "User-agent: *\nDisallow: /\n"

sidecar/attune_gui/static_cw/style.css

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,6 @@ code, .mono {
101101
color: var(--sidebar-active);
102102
}
103103

104-
.sidebar-foot { margin-top: auto; }
105-
.legacy-link {
106-
font-size: 0.75rem;
107-
color: var(--ink-mute);
108-
padding: 8px 12px;
109-
display: block;
110-
}
111-
.legacy-link:hover { color: var(--sidebar-active); text-decoration: none; }
112-
113104
/* ── Main canvas ───────────────────────────────────────────────────────── */
114105

115106
.main {

sidecar/attune_gui/templates/base.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
data-slug="{{ item.slug }}">{{ item.label }}</a>
1818
{% endfor %}
1919
</nav>
20-
<div class="sidebar-foot">
21-
<a href="/legacy/" class="legacy-link" title="Open the React UI">Legacy UI ↗</a>
22-
</div>
2320
</aside>
2421

2522
<main class="main">

0 commit comments

Comments
 (0)