From b19042bca082162bff24ba4a8afaf2a3ebd755c6 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Sun, 23 Aug 2026 19:11:21 +0200 Subject: [PATCH] feat(site): go.dev-style homepage + shared nav/footer chrome - wo-html grows generic nav_bar/card/btn_link + the utility classes they need (sticky nav, footer, 2-col grid, hero sizes); library stays content-free - home: hero (tagline + Get started/View source CTAs), real actor+ table code showcase, four why-cards, chapters strip (gate's 'Learn writeonce' anchor kept); every page shares nav + footer - site gate 11/0 unchanged; loopback bind untouched Co-Authored-By: Claude Fable 5 --- docs/examples/site/content.wo | 14 ++++++++++ docs/examples/site/main.wo | 50 ++++++++++++++++++++++++++++------- docs/examples/wo-html/html.wo | 36 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 9 deletions(-) diff --git a/docs/examples/site/content.wo b/docs/examples/site/content.wo index 7026ec8..0eb8340 100644 --- a/docs/examples/site/content.wo +++ b/docs/examples/site/content.wo @@ -7,6 +7,20 @@ -- site that teaches it. use html +-- The homepage's code showcase: a real flavor of the language — an +-- actor per chat room, rows in the built-in database, one binary. +fn home_snippet() -> Text { + let s = "@table\nclass Message {\n room: Text\n body: Text\n}\n\n"; + s = s .. "class Room {\n name: Text\n fn receive(msg: Post) {\n"; + s = s .. " insert Message { room: self.name, body: msg.body };\n"; + s = s .. " print(\"[\${self.name}] \${msg.body}\");\n }\n}\n\n"; + s = s .. "fn main() -> Int {\n"; + s = s .. " let general: actor Post = spawn Room { name: \"general\" };\n"; + s = s .. " send(general, Post { body: \"hello, writeonce\" });\n"; + s = s .. " time.sleep(50);\n return 0;\n}"; + return code_block(s); +} + fn ch_hello() -> Text { let b = el("p", "leading-relaxed mb-4", "A writeonce program is one directory of .wo files and one entry: a free " .. "function named main. It returns the process exit code. There is no " .. "runtime to install separately and no build pipeline — woc build produces " .. "ONE self-contained binary with the VM and your bytecode inside."); diff --git a/docs/examples/site/main.wo b/docs/examples/site/main.wo index 52b7b24..7df67e9 100644 --- a/docs/examples/site/main.wo +++ b/docs/examples/site/main.wo @@ -43,14 +43,26 @@ fn ok_html(body: Text) -> Resp { return Resp { status: 200, headers: h, body: body }; } -fn site_header() -> Text { - let brand = link("/", "text-xl font-bold text-gray-900 no-underline", "writeonce.de"); - let tag = el("span", "text-sm text-gray-500", "one language, one runtime, one database, one binary"); - return el("div", "flex items-center justify-between mb-8", brand .. tag); +fn site_nav() -> Text { + let links = link("/ch/hello", "text-gray-700", "Tutorial"); + links = links .. link("/health", "text-gray-700", "Health"); + links = links .. link("https://github.com/shoneyj", "text-gray-700", "GitHub"); + return nav_bar("/", "writeonce.de", links); +} + +fn site_footer() -> Text { + return el("div", "footer", "writeonce.de — served by the language it teaches. " .. "One binary: compiler, runtime, database, this page."); } fn shell(title: Text, inner: Text) -> Text { - let body = el("div", "mx-auto max-w-3xl px-4 py-8", site_header() .. inner); + let body = site_nav() .. el("div", "mx-auto max-w-3xl px-4 py-8", inner) .. site_footer(); + return page(title, body); +} + +-- The homepage uses the wider container (go.dev shape); chapter pages +-- keep the reading width above. +fn shell_wide(title: Text, inner: Text) -> Text { + let body = site_nav() .. el("div", "mx-auto max-w-5xl px-4", inner) .. site_footer(); return page(title, body); } @@ -72,10 +84,30 @@ fn chapter_nav(current: Int) -> Text { class Home { pad: Int fn handle(req: Req) -> Resp { - let hero = el("h1", "text-3xl font-bold mb-4", "Learn writeonce"); - let intro = el("p", "leading-relaxed mb-4", "A language where the runtime, the database and the web server are one thing. " .. "This site is written in it — every page you read here is a " .. "Resp built by .wo code, stored in the language's own " .. "tables, served by its own framework. Work through the chapters in order:"); - let body = el("div", "bg-white rounded-lg border shadow-sm p-6", hero .. intro .. chapter_nav(0)); - return ok_html(shell("writeonce — learn the language", body)); + -- hero: tagline + the two CTAs (the go.dev shape, no JS anywhere) + let h1 = el("h1", "text-4xl font-bold mb-4", "One language. One runtime.
One database. One binary."); + let sub = el("p", "text-lg text-gray-700 leading-relaxed mb-6", "writeonce is a language whose compiler, runtime, web server and " .. "database ship as a single never-stopping Linux binary. Ownership-" .. "checked memory, inferred GC where ownership cannot reach, actors " .. "on every core — and the page you are reading is served by it."); + let ctas = el("div", "flex items-center justify-center gap-4", btn_link("/ch/hello", "Get started", true) .. btn_link("https://github.com/shoneyj", "View source", false)); + let hero = el("div", "text-center py-16", h1 .. sub .. ctas); + + -- code showcase: a real flavor of the language + let show_head = el("h2", "text-2xl font-bold mb-2 text-center", "An actor per chat room, rows in the built-in database"); + let show_cap = el("p", "text-sm text-gray-500 text-center mb-4", "No broker, no ORM, no async keyword — ownership moves the message, the WAL makes the row durable."); + let showcase = el("div", "mx-auto max-w-3xl mb-8", show_head .. show_cap .. home_snippet()); + + -- why-cards (2x2 grid, collapses on small screens) + let cards = card("One binary", "woc build emits a self-contained executable: VM, your bytecode, the database engine. Deploys are a file copy; the runtime swaps code in place."); + cards = cards .. card("Memory safety, no tax", "Rust-shaped ownership checked at compile time; where ownership cannot express the shape, the compiler infers GC — per shard, no global pause."); + cards = cards .. card("The database is built in", "Every class is a table. Inserts are WAL-logged before they acknowledge; restart replays. No server to operate, no connection string."); + cards = cards .. card("Actors on every core", "spawn returns an address, send moves ownership. Fibers park on io_uring instead of blocking threads — no async/await, ever."); + let grid = el("div", "grid grid-cols-2 gap-6 mb-8", cards); + + -- chapters (the gate's anchor string lives here) + let learn = el("h2", "text-2xl font-bold mb-4", "Learn writeonce"); + let learn_p = el("p", "leading-relaxed mb-4", "The tutorial is written in the language and stored in its tables — work through the chapters in order:"); + let chapters = el("div", "bg-white rounded-lg border shadow-sm p-6 mb-8", learn .. learn_p .. chapter_nav(0)); + + return ok_html(shell_wide("writeonce — learn the language", hero .. showcase .. grid .. chapters)); } } diff --git a/docs/examples/wo-html/html.wo b/docs/examples/wo-html/html.wo index e8a5ce8..70adfdf 100644 --- a/docs/examples/wo-html/html.wo +++ b/docs/examples/wo-html/html.wo @@ -71,6 +71,29 @@ pub fn form_post(action: Text, inner: Text) -> Text { return "
${inner}
"; } +-- A sticky top navigation bar: brand on the left, a prebuilt row of +-- links on the right. The bar spans the viewport; the inner row shares +-- the page's container widths. +pub fn nav_bar(brand_href: Text, brand: Text, right: Text) -> Text { + let b = link(brand_href, "text-xl font-bold text-gray-900 no-underline", brand); + let row = el("div", "mx-auto max-w-5xl px-4 flex items-center justify-between", b .. el("div", "flex items-center gap-4", right)); + return el("nav", "nav", row); +} + +-- A titled card for feature grids. +pub fn card(title: Text, body: Text) -> Text { + let t = el("h3", "text-lg font-bold mb-2", title); + return el("div", "bg-white rounded-lg border shadow-sm p-6", t .. el("p", "leading-relaxed text-gray-700", body)); +} + +-- A link styled as a button; primary = filled, otherwise outline. +pub fn btn_link(href: Text, label: Text, primary: Bool) -> Text { + if primary { + return link(href, "btn no-underline", label); + } + return link(href, "btn-outline no-underline", label); +} + -- The utility sheet. Tailwind's names, one hand-written static sheet — -- only the utilities this ecosystem's pages actually use; growing it is -- adding a line, not adopting a toolchain. `.code-block`, `.field` and @@ -107,6 +130,19 @@ pub fn tw_css() -> Text { c = c .. "padding:.5rem;font-family:ui-monospace,monospace;font-size:.875rem}"; c = c .. ".btn{background:#2563eb;color:#fff;border:0;border-radius:.25rem;"; c = c .. "padding:.5rem 1rem;font-weight:700;cursor:pointer}"; + c = c .. "a.btn{color:#fff;display:inline-block}a.btn:hover{text-decoration:none;background:#1d4ed8}"; + c = c .. ".btn-outline{display:inline-block;border:1px solid #2563eb;color:#2563eb;"; + c = c .. "border-radius:.25rem;padding:.5rem 1rem;font-weight:700}"; + c = c .. "a.btn-outline:hover{text-decoration:none;background:#eff6ff}"; + c = c .. ".nav{position:sticky;top:0;background:#fff;border-bottom:1px solid #e5e7eb;"; + c = c .. "padding:.75rem 0;z-index:10}"; + c = c .. ".footer{border-top:1px solid #e5e7eb;color:#6b7280;font-size:.875rem;"; + c = c .. "padding:2rem 1rem;text-align:center;margin-top:4rem}"; + c = c .. ".max-w-5xl{max-width:64rem}.text-4xl{font-size:2.25rem;line-height:1.2}"; + c = c .. ".py-16{padding-top:4rem;padding-bottom:4rem}.text-center{text-align:center}"; + c = c .. ".mb-6{margin-bottom:1.5rem}.gap-6{gap:1.5rem}.justify-center{justify-content:center}"; + c = c .. ".grid{display:grid}.grid-cols-2{grid-template-columns:repeat(2,1fr)}"; + c = c .. "@media(max-width:640px){.grid-cols-2{grid-template-columns:1fr}}"; return c; }