diff --git a/AGENTS.md b/AGENTS.md index 1f1e172..a4df616 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,8 @@ debugging, because most entries describe something that looked like one bug and turned out to be another. Then [`docs/NEXT_STEPS.md`](docs/NEXT_STEPS.md) for current recommendations and known deferred fixes. Mobile interaction work also has its own detailed plan in -[`docs/mobile_enhance.md`](docs/mobile_enhance.md). +[`docs/mobile_enhance.md`](docs/mobile_enhance.md). The CDN-to-Vite move is +[`docs/vite_build.md`](docs/vite_build.md). ## Tests earn their place by catching a real bug @@ -122,6 +123,9 @@ implemented in one go: authenticated by OIDC. Read it before adding state to `Runtime`: new state is either genuinely shared (market data) or belongs to a user, and knowing which now is much cheaper than untangling it later. +- `docs/vite_build.md` — pin and hash the frontend, stay on Coolify, do not + split components on the way. A production Dockerfile first, then Vite; + never a root `package.json` while nixpacks is still the builder. Do not build local user accounts. The destination is OIDC, so password storage would be written and then deleted. diff --git a/docs/NEXT_STEPS.md b/docs/NEXT_STEPS.md index 2c51a10..70fd504 100644 --- a/docs/NEXT_STEPS.md +++ b/docs/NEXT_STEPS.md @@ -16,6 +16,13 @@ and transport failure by carrying exact trade time, the last settled minute, source receipt time and an application heartbeat over the existing WebSocket. Any stale threshold must account for Yahoo's declared delay. +## Frontend build + +The CDN-to-Vite plan is in `docs/vite_build.md`. First tranche is a production +`Dockerfile` that reproduces today's nixpacks image, so a later `package.json` +cannot make Coolify treat this as a Node app. Do not start the file move until +that deploy has been seen live. + ## Mobile authoring The detailed plan is in `docs/mobile_enhance.md`. Recommended first tranche: diff --git a/docs/color_refactor.md b/docs/archived/color_refactor.md similarity index 100% rename from docs/color_refactor.md rename to docs/archived/color_refactor.md diff --git a/docs/esquotes.md b/docs/archived/esquotes.md similarity index 100% rename from docs/esquotes.md rename to docs/archived/esquotes.md diff --git a/docs/implementation.md b/docs/implementation.md index 642c2e8..31aaff7 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -1042,3 +1042,18 @@ at `x=1082`, producing the long segment. The first fallback still failed because interpolate between the surrounding displayed candles' own pixel coordinates, which puts `:30` halfway between the hourly candles and remains correct even when another series has inserted foreign timestamps into the shared scale. + +### 2026-08-14 — a crease at the live edge was two renderers disagreeing + +The canvas `LineSeries` holds the historical portion of a manual trendline; an +SVG segment projects it through future whitespace. `updateBar()` updated the +candle but did not extend the canvas line when a new displayed bar opened. The +SVG correctly started at that new bar, producing a visible crease at the live +edge even after its slope was corrected. + +New displayed bars now append one source-space sample to each visible sloped +manual series. This is deliberately not a full `syncLevels()` call: the source +geometry and all previous line points are unchanged, and a per-line +`series.update()` keeps the canvas and SVG endpoints identical without adding +per-tick redraw work. Browser coverage opens a new 5m candle and asserts that +the canvas line reaches the same time and price as the projection. diff --git a/docs/plan.md b/docs/plan.md index 3544fd9..a0f3f78 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -36,8 +36,8 @@ See [§14](#14-why-execution-is-out-of-scope) for why, and for the seam left beh > **For current work, start with `AGENTS.md`**, which every agent loads > automatically. It points at the live planning documents: > `docs/NEXT_STEPS.md` for the short list, `docs/async_refactor.md`, -> `docs/multi_user.md`, `docs/feature_undo.md` and `docs/mobile_enhance.md` for -> designs not yet built. +> `docs/multi_user.md`, `docs/feature_undo.md`, `docs/mobile_enhance.md` and +> `docs/vite_build.md` for designs not yet built. > > **`main` deploys to production.** A push triggers a Forgejo webhook and > Coolify rebuild of . That is the intended workflow now, @@ -90,7 +90,7 @@ if your parser doesn't filter those, that fixture will catch it. | Decision | Choice | Why | |---|---|---| | Backend | FastAPI (already scaffolded) | Repo already runs it; native WebSocket support | -| Frontend | Vue 3 from CDN, **no build step** | Matches existing `static/` setup; keeps deploy trivial | +| Frontend | Vue 3 from CDN, **no build step** | Matches existing `static/` setup; keeps deploy trivial. Destination is Vite — see `docs/vite_build.md`. Do not treat this row as a reason to reject that move. | | Charting | TradingView Lightweight Charts **v5.2.0**, standalone build | Apache-2.0, canvas, built for incremental realtime updates | | Data source | **Pluggable `MarketDataSource`.** Yahoo first, Schwab later | Yahoo needs no API key *and* has the history Schwab lacks — see §2.1 | | Persistence | **In-memory first**, behind a `BarStore` interface | User confirmed deferring persistence is fine for v1 | diff --git a/docs/vite_build.md b/docs/vite_build.md new file mode 100644 index 0000000..ce7034f --- /dev/null +++ b/docs/vite_build.md @@ -0,0 +1,311 @@ +# Vite build — from CDN script tags to a real frontend + +**Status: tracked, not started.** A direction to refactor toward, not a project +with a date. Each phase below is worth shipping on its own; none of it is +speculative scaffolding for a component rewrite. + +Today `static/index.html` loads Vue 3, Lightweight Charts 5.2.0 and Font Awesome +7.3.1 from unpkg, then two plain scripts. FastAPI serves those files and stamps +`?v=` onto every `/static/` URL. Production is Coolify + nixpacks + a Python +`Procfile`. That is the setup this document replaces. + +## The goal is not "more Vue" + +The target is **a pinned, hashed, same-origin frontend** that we can grow +without unpkg and without a Python hasher. It is not a component split, not +TypeScript, not a router, and not leaving Coolify. + +- `chart.js` stays a plain class. Vue still must not wrap chart or series + objects in `ref()` / `reactive()`. +- `window.__chart` stays. E2E and diagnostic work depend on it. +- One `App.vue` holding today's template and `setup()`. Do not extract the + color picker or the tool panels in the same change. +- Stay on Coolify. The friction is nixpacks autodetection, not the platform. + +## What is already right + +- Vue 3 Composition API in `static/app.js` (`createApp`, `ref`, `computed`, + `watch`, `onMounted`). That maps 1:1 onto `vue` from npm. +- `ConfluenceChart` is already framework-free. It only needs `export` instead + of `window.ConfluenceChart`, and ESM named imports instead of the + `LightweightCharts` global. +- FastAPI already owns `/`, `/api`, `/ws`. The built SPA still comes from + that origin. Do not put a Vite server in production. +- Asset hashing exists because a tab left open kept running yesterday's JS + (`main.asset_version`, `tests/test_asset_versioning.py`). Vite's content + hashes replace that rewriter; the *reason* does not go away. + +## Constraints this repo will punish you for forgetting + +- **The agent is on a different machine from the user's browser.** Local Vite + on `localhost:5173` is invisible to them. Whatever serves the UI in dev must + still be reachable as `hera.local:8010` (or whatever host port compose + publishes). HMR has to work across that hop, or we do not use HMR. +- **`--reload` plus an 82-second seed.** Never put a scratch `.py` in the repo + root. Frontend files are safe; uvicorn watches Python. Do not "help" by + adding a Python build helper at the root. +- **Every push to `main` is a production deploy**, and a deploy restarts the + market stream. The Vite cutover is one of those deploys. Land the production + Dockerfile *before* a root `package.json` exists, or nixpacks may decide + this is a Node app and the site goes dark. +- **E2E hits `http://api:8000`**, waits on `window.__chart.bars`, and uses + `--lang=en-US`. None of that changes. A blank canvas after the move is + still the locale bug until proven otherwise. +- **Pin what unpkg currently pins.** Lightweight Charts **5.2.0** and Font + Awesome **7.3.1**. Vue's CDN tag is `vue@3` (floating). Pin a current Vue + 3.x on the way in; do not upgrade LWC in this work. v5 series creation is + `chart.addSeries(CandlestickSeries, opts)` — the v4 helpers do not exist. +- **One uvicorn worker, forever**, until the streamer is a separate process. + The Dockerfile `CMD` is the `Procfile` line. Do not add `--workers`. + +## Target layout + +``` +frontend/ + package.json + package-lock.json committed + vite.config.js + index.html Vite entry; empty #app + src/ + main.js createApp(App).mount('#app') + App.vue today's markup + today's setup() + chart.js export class ConfluenceChart + style.css moved from static/ +dist/ gitignored; Vite outDir, served by FastAPI +Dockerfile production; Coolify prefers this over nixpacks +``` + +`static/` goes away when FastAPI is serving `dist/` and the e2e suite is green. +Do not keep both as a fallback — a missed build would silently serve the CDN +app. + +Suggested `frontend/src/main.js`: + +```js +import { createApp } from 'vue'; +import '@fortawesome/fontawesome-free/css/all.min.css'; +import './style.css'; +import App from './App.vue'; + +createApp(App).mount('#app'); +``` + +Suggested chart import (names used today): + +```js +import { + createChart, + CandlestickSeries, + HistogramSeries, + LineSeries, + LineStyle, + LineType, + CrosshairMode, + TickMarkType, +} from 'lightweight-charts'; +``` + +Keep `export default { setup() { ... return { ... }; } }` in `App.vue`. +`