121 lines
6.7 KiB
Markdown
121 lines
6.7 KiB
Markdown
# kjol-web — the kjol website
|
|
|
|
The landing page and documentation for the whole codebase. It is also the thing it
|
|
documents: every page runs the code it describes, and there is not a screenshot of a
|
|
component anywhere on the site.
|
|
|
|
It is **one server running two entirely different front-ends**, and that is the point of
|
|
it. kjol has two web layers — one written in Go and compiled to WebAssembly, one written
|
|
in Solid and bundled by a Go toolchain — and the only honest way to document both is to
|
|
build the site out of both.
|
|
|
|
## Run it
|
|
|
|
```sh
|
|
cd go/cmd/kjol-web
|
|
go run ./build # cold build: both halves
|
|
go run ./server # SSR + /rsc + hot reload at http://localhost:8085
|
|
```
|
|
|
|
`go run ./server` performs that same build on every save and hot-swaps the result into
|
|
the browser, so day to day it is the only command you need. A `.go` save rebuilds the
|
|
wasm and swaps it in without a reload or a flash, preserving page state; a `.css` save
|
|
recompiles only Tailwind; a compile error lands in a browser overlay rather than in a
|
|
terminal you were not looking at.
|
|
|
|
## The shape of the site
|
|
|
|
| Path | Rendered by | What it is |
|
|
|---|---|---|
|
|
| `/`, `/about` | Go → WebAssembly, SSR'd | The landing page. The **Layers** menu is the site's primary navigation. |
|
|
| `/wasm/*` | Go → WebAssembly | **Kjol Wasm Web** — the gowasm engine: SSR + hydration, server components, the `webui` kit, overlays, AutoTable, charts, client fetching. |
|
|
| `/js/*` | Solid → esbuild, client-rendered | **Kjol JS Web** — the Solid kit: components, forms, AutoTable, theming. |
|
|
| `/js/ssr` | Solid → **goja, at request time** | A public page server-rendered with live data injected — the ISR path. |
|
|
|
|
Crossing between `/wasm` and `/js` is a real page load. They are different binaries, and
|
|
pretending otherwise would mean shipping both to every visitor.
|
|
|
|
## Layout
|
|
|
|
```
|
|
app/ the Go/WASM half — pages as plain Go functions returning a *VNode
|
|
pages.go Deps + Shell + the public/app layouts + the landing page
|
|
layers.go the Layers, as data. MIRRORED in frontend/src/layers.ts.
|
|
docs.go the docs chrome; docsNav() is the sidebar AND the index
|
|
kit.go table.go overlays.go chart.go data.go server_counter.go
|
|
*.gen.go GENERATED by kjol/cmd/wasmgen (routes, layout dispatch, RSC stubs)
|
|
wasm/ the js/wasm client entry (main_native.go is a host stub)
|
|
css/app.css its Tailwind entry — kjol/tw scans the .go files for class names
|
|
|
|
frontend/ the Solid half — .tsx pages written against @ui/*
|
|
css/style.css brand ONLY. kjol's theme.css is prepended by the bundler, and it
|
|
is the one that does `@import "tailwindcss"`.
|
|
vendor/ pdf-lib + pdfjs-dist. @ui/AutoTable imports them at the TOP LEVEL,
|
|
so a bundle without them does not degrade — it fails to evaluate.
|
|
src/app.ts the SPA entry. It is .ts, not .tsx, because the bundler resolves
|
|
the entry as src/app.ts and nothing else — so it can hold no JSX.
|
|
src/layers.ts the Layers again. Keep in step with app/layers.go.
|
|
|
|
server/ ONE Go server: serves wwwroot, SSRs the wasm routes, hosts /rsc,
|
|
mounts the Solid SPA at /js/*, serves the SSR'd public pages.
|
|
internal/handlers/ the app side of the public-page inversion: kjol generates the
|
|
registry; this owns the type and the document shell.
|
|
buildsteps/ the build, in Go rather than a shell script, so the one-shot build and
|
|
the dev server's watch loop call the SAME functions and cannot drift.
|
|
wwwroot/ both halves write here. They never collide: app.css / app.wasm for one,
|
|
bundle.min.* for the other. One static dir, one server.
|
|
```
|
|
|
|
## Theming
|
|
|
|
One theme, both halves. The kits are themed by **semantic tokens** — components say
|
|
`bg-surface`, `text-ink`, `border-line` and never name a colour — so dark mode
|
|
re-points about a dozen CSS variables and not one component knows it happened.
|
|
|
|
The choice is stored under a single `kjol-theme` key that **both** front-ends read, so
|
|
switching to dark in `/wasm` and walking over to `/js` keeps it dark. A ten-line boot
|
|
script in the document head applies the class before first paint; without it every
|
|
dark-mode reader would get a white page until the bundle landed, and then have it
|
|
snatched away.
|
|
|
|
The only places a `dark:` variant survives are the two a re-pointed token cannot fix: a
|
|
coloured tint (a `red-50` wash is invisible on a near-black surface) and a fill that has
|
|
to invert (the neutral button, whose label must go dark when the fill goes pale).
|
|
|
|
## Adding things
|
|
|
|
**A Go/WASM page:** write the function, mark it `//gowasm:page /wasm/thing layout=app`,
|
|
build. `wasmgen` regenerates the routing. Add `static` to have it server-rendered.
|
|
|
|
**A Solid page:** write the `.tsx`, add it to `routes` in `frontend/src/app.ts` and to
|
|
`NAV` in `frontend/src/layout/Shell.tsx`.
|
|
|
|
**A layer:** one entry in `app/layers.go` *and* one in `frontend/src/layers.ts`. They are
|
|
two files because nothing is upstream of both a WebAssembly binary and an esbuild bundle;
|
|
keeping each to a flat list of plain data is what makes that duplication survivable.
|
|
|
|
## How it maps onto the engine
|
|
|
|
| Package | Role | This app's use |
|
|
|---|---|---|
|
|
| `kjol/vdom` | neutral virtual DOM (native + wasm): `VNode`, `Signal`, `RenderHTML` | pages build `*VNode`; the server SSRs with `vdom.RenderHTML` |
|
|
| `kjol/wasmruntime` | wasm client runtime: reconcile, `Run`/`Hydrate`, router, fetch | `wasm/main.go` calls `Hydrate`/`Run` |
|
|
| `kjol/rsc` | stateless server components over HTTP | `//gowasm:server` + its generated client stub |
|
|
| `kjol/wasmdevserver` | reusable dev server: SSR, `/rsc`, hot reload, error overlay | `server/main.go` fills a `wasmdevserver.Config` |
|
|
| `kjol/webui` | the Go component kit | every `/wasm/*` page |
|
|
| `kjol/jsbundler` | TSX → Solid → esbuild, the Tailwind driver, the SSR bake | `buildsteps.JS`, and the ISR render at request time |
|
|
| `kjol/jsruntime` | the Solid kit, the vendored runtime, the icons, `theme.css` | everything under `/js/*` |
|
|
| `kjol/tw` | the Tailwind v4 engine, in Go | both stylesheets — it scans `.go` for one and `.tsx` for the other |
|
|
|
|
The **golden rule** holds throughout: no kjol package imports application code. The app
|
|
injects `Build`, `Render` and `Document` into `wasmdevserver`; it owns the `publicPage`
|
|
type that kjol's generated registry is written against. The dependency only ever points
|
|
one way.
|
|
|
|
## Its own module
|
|
|
|
`go.mod` declares module `kjolweb` with `replace kjol => ../..`, so its dependencies —
|
|
go-chart for the server-drawn charts, plus esbuild and goja by way of the bundler — stay
|
|
out of kjol, whose engine packages are stdlib-only. `go build ./...` at the kjol root
|
|
does not descend into this nested module; build it from here.
|