Update kjol website with C documentation
This commit is contained in:
@@ -7,22 +7,42 @@ import (
|
||||
ui "kjol/webui"
|
||||
)
|
||||
|
||||
// The layers of kjol, as data.
|
||||
// What kjøl is made of, as data.
|
||||
//
|
||||
// This is the Go mirror of frontend/src/layers.ts. The site has two front-ends built
|
||||
// by two completely different pipelines, and the Layers menu has to be identical in
|
||||
// both — so it is a LIST in each, not markup, and the two lists are the only thing
|
||||
// that has to be kept in step.
|
||||
// There are two kinds of thing here, and conflating them was the mistake this file used
|
||||
// to make — one flat list called "the layers", holding both.
|
||||
//
|
||||
// (A shared source would be better than a mirrored one. There isn't one: this half
|
||||
// compiles to WebAssembly and the other is bundled by esbuild, and nothing is upstream
|
||||
// of both. Keeping it to a flat slice of plain data is what makes the duplication
|
||||
// survivable — you can diff the two by eye.)
|
||||
// LAYERS are LANGUAGES. What kjøl is written in, and what it gives you in each:
|
||||
// the Go base, the TypeScript kit, the C base, the Jai modules. A layer is
|
||||
// a directory of code you can use on its own.
|
||||
//
|
||||
// COMPOSITIONS are FRAMEWORKS. What you get when the layers are assembled into
|
||||
// something that does a job — the two web engines. A composition is not
|
||||
// another language; it is a use of them.
|
||||
//
|
||||
// Kjøl Wasm Web is Go, all the way down. Kjøl JS Web is TypeScript compiled by a Go
|
||||
// toolchain — two layers, one framework. Listing that beside "C" as though they were the
|
||||
// same kind of noun told the reader nothing about either.
|
||||
//
|
||||
// This is the Go mirror of frontend/src/layers.ts. The site has two front-ends built by
|
||||
// two completely different pipelines, and these menus have to be identical in both — so
|
||||
// each is a LIST, not markup, and the two lists are the only thing that has to be kept in
|
||||
// step.
|
||||
//
|
||||
// (A shared source would be better than a mirrored one. There isn't one: this side
|
||||
// compiles to WebAssembly and the other is bundled by esbuild, and nothing is upstream of
|
||||
// both. Keeping each to a flat slice of plain data is what makes the duplication
|
||||
// survivable — you can diff them by eye.)
|
||||
|
||||
type Layer struct {
|
||||
Name string
|
||||
Href string
|
||||
Tagline string
|
||||
// Sub is the half-line beside the wordmark while you are inside this layer. It says
|
||||
// what you are standing in — "Go + WebAssembly", "arenas, strings, a lexer" — and a
|
||||
// wordmark that says the same thing everywhere is one more thing the reader has to
|
||||
// keep track of himself.
|
||||
Sub string
|
||||
// Live means you can click into worked examples. The others are documented but
|
||||
// have no demo — they still appear, because a menu that silently omits half the
|
||||
// library teaches the reader that the library is half the size it is.
|
||||
@@ -30,66 +50,107 @@ type Layer struct {
|
||||
Icon string
|
||||
}
|
||||
|
||||
func Layers() []Layer {
|
||||
// Wordmark is what the CHROME calls this layer — the top bar, and the page's own title.
|
||||
// The menu calls it Name.
|
||||
//
|
||||
// They differ, and only for the languages. In a menu headed "Layers" the row says "C",
|
||||
// because the row is answering "which language"; up in the top bar, alone, "C" is the name
|
||||
// of a language rather than the name of the thing you are reading, and it has to say whose
|
||||
// C this is. The compositions are already named "Kjøl Wasm Web" — the product's name is
|
||||
// part of what they ARE, not a prefix bolted on — so they are returned unchanged.
|
||||
func (l Layer) Wordmark() string {
|
||||
if strings.HasPrefix(l.Name, "Kjøl") {
|
||||
return l.Name
|
||||
}
|
||||
return "Kjøl " + l.Name
|
||||
}
|
||||
|
||||
// Languages: what kjøl is written in.
|
||||
func Languages() []Layer {
|
||||
return []Layer{
|
||||
{
|
||||
Name: "Kjol Go",
|
||||
Name: "Go",
|
||||
Href: "/go",
|
||||
Tagline: "The server base: config, database, logging, HTTP, mail, validation.",
|
||||
Tagline: "The base: config, database, logging, HTTP, mail, validation — and both web engines.",
|
||||
Icon: "server",
|
||||
},
|
||||
{
|
||||
Name: "Kjol Wasm Web",
|
||||
Href: "/wasm",
|
||||
Tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, no JS build.",
|
||||
Live: true,
|
||||
Icon: "code",
|
||||
},
|
||||
{
|
||||
Name: "Kjol JS Web",
|
||||
Href: "/js",
|
||||
Tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.",
|
||||
Live: true,
|
||||
Name: "TypeScript",
|
||||
Href: "/ts",
|
||||
Tagline: "The Solid component kit, the vendored runtime, and the generic scaffolding apps import as @kjol/*.",
|
||||
Icon: "squares",
|
||||
},
|
||||
{
|
||||
Name: "Kjol C",
|
||||
Name: "C",
|
||||
Href: "/c",
|
||||
Tagline: "Arena allocator, strings, math, lexer, platform layer.",
|
||||
Tagline: "Arena allocator, counted strings, math, a lexer, a platform layer — and a build system that is a C file.",
|
||||
Sub: "a base layer in C",
|
||||
Live: true,
|
||||
Icon: "bolt",
|
||||
},
|
||||
{
|
||||
Name: "Kjol Jai",
|
||||
Name: "Jai",
|
||||
Href: "/jai",
|
||||
Tagline: "Console rendering module. Early.",
|
||||
Tagline: "Console rendering. Early.",
|
||||
Icon: "cube",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CurrentLayer is the layer the given path belongs to, or nil on the front page.
|
||||
// Compositions: what the languages are assembled into.
|
||||
func Compositions() []Layer {
|
||||
return []Layer{
|
||||
{
|
||||
Name: "Kjøl Wasm Web",
|
||||
Href: "/wasm",
|
||||
Tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, and no JavaScript build at all.",
|
||||
Sub: "Go + WebAssembly",
|
||||
Live: true,
|
||||
Icon: "code",
|
||||
},
|
||||
{
|
||||
Name: "Kjøl JS Web",
|
||||
Href: "/js",
|
||||
Tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.",
|
||||
Sub: "Solid + Go toolchain",
|
||||
Live: true,
|
||||
Icon: "table",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CurrentLayer is the layer or composition the given path belongs to, or nil on the front
|
||||
// page. The wordmark uses it to name where you are standing.
|
||||
func CurrentLayer(path string) *Layer {
|
||||
for i, l := range Layers() {
|
||||
all := append(Compositions(), Languages()...)
|
||||
for i, l := range all {
|
||||
if path == l.Href || strings.HasPrefix(path, l.Href+"/") {
|
||||
return &Layers()[i]
|
||||
return &all[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LayersMenuCtl is the Layers menu's controller.
|
||||
// The two menus' controllers.
|
||||
//
|
||||
// It is created ONCE, here, at package level — not inside layersMenu, which is called
|
||||
// from a layout on every single render. A floating component is a controller: it owns
|
||||
// an open signal, a positioning engine and document listeners, and building a fresh one
|
||||
// per render would leak all three and give you a menu that never opens. Same rule as
|
||||
// Theme, a few lines up in pages.go.
|
||||
var LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd})
|
||||
// They are created ONCE, here, at package level — not inside the functions below, which a
|
||||
// layout calls on every single render. A floating component is a controller: it owns an
|
||||
// open signal, a positioning engine and document listeners, and building a fresh one per
|
||||
// render would leak all three and give you a menu that never opens. Same rule as Theme, a
|
||||
// few lines up in pages.go.
|
||||
//
|
||||
// TWO menus, not one with two headings inside it. They are different questions — "what is
|
||||
// this written in" and "what can I read" — and a reader who wants the second should not
|
||||
// have to scroll past the first to find it. The kit's single-open manager means opening
|
||||
// one closes the other, so they behave like one control with two halves.
|
||||
var (
|
||||
LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd})
|
||||
CompositionsMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd})
|
||||
)
|
||||
|
||||
// layersMenu is the site's primary navigation: kjol is a stack of layers, and this is
|
||||
// how you get from any one of them to any other.
|
||||
// layersMenu lists the LANGUAGES. compositionsMenu, below, lists the frameworks.
|
||||
//
|
||||
// A layer that is Live is a link. One that is not is inert and dimmed, with the word
|
||||
// An entry that is Live is a link. One that is not is inert and dimmed, with the word
|
||||
// "reference" on it — it exists, it is documented in the repository, there is simply
|
||||
// nothing here to click.
|
||||
//
|
||||
@@ -98,35 +159,41 @@ var LayersMenuCtl = ui.NewMenu(ui.MenuOptions{Placement: ui.PlacementBottomEnd})
|
||||
// interception — an intercepted click would ask this WebAssembly to render a page it
|
||||
// does not have.
|
||||
func layersMenu(d Deps) *VNode {
|
||||
content := []*VNode{
|
||||
P(Attr("class", "px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-widest text-ink-faint"),
|
||||
Text("The layers of kjol")),
|
||||
}
|
||||
for _, l := range Layers() {
|
||||
return dropdown(d, LayersMenuCtl, "Layers", Languages())
|
||||
}
|
||||
|
||||
// compositionsMenu lists the FRAMEWORKS — the two things assembled out of the layers, and
|
||||
// the two a reader can actually click into.
|
||||
func compositionsMenu(d Deps) *VNode {
|
||||
return dropdown(d, CompositionsMenuCtl, "Compositions", Compositions())
|
||||
}
|
||||
|
||||
func dropdown(d Deps, ctl *ui.Menu, label string, rows []Layer) *VNode {
|
||||
content := make([]*VNode, 0, len(rows))
|
||||
for _, l := range rows {
|
||||
content = append(content, layerItem(d, l))
|
||||
}
|
||||
|
||||
return Div(Attr("class", "relative"),
|
||||
LayersMenuCtl.Trigger(ui.MenuTriggerProps{
|
||||
ctl.Trigger(ui.MenuTriggerProps{
|
||||
Class: "inline-flex items-center gap-1.5 rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft hover:bg-surface-raised hover:text-ink",
|
||||
},
|
||||
Text("Layers"),
|
||||
Text(label),
|
||||
ui.IconInline("chevron-down", 11, "text-ink-faint"),
|
||||
),
|
||||
LayersMenuCtl.Content("w-96", content...),
|
||||
ctl.Content("w-96", content...),
|
||||
)
|
||||
}
|
||||
|
||||
// layersGrid is the front page's list of layers — the same data as the menu, laid out
|
||||
// to be read rather than navigated. A layer with no examples still gets a row: the
|
||||
// point of the page is what kjol IS, and half of it having no demo yet does not make
|
||||
// that half not exist.
|
||||
func layersGrid(d Deps) *VNode {
|
||||
rows := []Mod{Attr("class", "mt-5 divide-y divide-line rounded-default border border-line")}
|
||||
for _, l := range Layers() {
|
||||
rows = append(rows, layerRow(l))
|
||||
// layerGrid is the front page's list — the same data as the menu, laid out to be read
|
||||
// rather than navigated. A layer with no examples still gets a row: the point of the page
|
||||
// is what kjøl IS, and half of it having no demo yet does not make that half not exist.
|
||||
func layerGrid(rows []Layer) *VNode {
|
||||
mods := []Mod{Attr("class", "mt-5 divide-y divide-line rounded-default border border-line")}
|
||||
for _, l := range rows {
|
||||
mods = append(mods, layerRow(l))
|
||||
}
|
||||
return Div(rows...)
|
||||
return Div(mods...)
|
||||
}
|
||||
|
||||
func layerRow(l Layer) *VNode {
|
||||
|
||||
Reference in New Issue
Block a user