first commit

This commit is contained in:
2026-07-12 15:55:18 +05:30
commit ee98026cc5
404 changed files with 44522 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
// About page. Route: /about
// Pure SSR — ships zero JavaScript (no state, no directives).
page About {
layout = "public"
seo {
title = "About"
}
view {
<h1>About Page</h1>
<p>This page ships zero JavaScript — pure SSR.</p>
<p><a href="/">Home</a></p>
}
}
+58
View File
@@ -0,0 +1,58 @@
// Realtime chat. Route: /chat. There is NO client JS file — the page just
// declares `data-room="chat"` and the framework's realtime runtime handles the
// WebSocket, rendering incoming messages into the `<template>`s below and
// sending the form on submit. Server side: app/realtime/chat.ts.
page Chat {
layout = "public"
seo {
title = "Realtime chat"
description = "A broadcast chat room — declared with data-room, zero client JS."
}
style {
.chat-log {
height: 320px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.35rem;
font-size: 0.95rem;
}
.chat-row--system { color: var(--wire-color-muted); font-size: 0.85rem; }
.chat-form { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.chat-form .chat-name { max-width: 150px; }
.chat-form .chat-msg { flex: 1 1 12rem; }
[data-room-status].is-connected { background: var(--wire-color-success); color: #05210f; }
[data-room-status].is-disconnected,
[data-room-status].is-error { background: var(--wire-color-danger); color: #fff; }
}
view {
<h1>Realtime chat</h1>
<p>
Open this page in two tabs. The page ships <strong>no client JS</strong> —
it just declares <code>data-room="chat"</code>; the framework connects,
renders messages, and sends the form. Server side is a <code>defineRoom</code>
handler in <code>app/realtime/chat.ts</code>.
</p>
<div data-room="chat">
<div data-component="stack" gap="4">
<span data-room-status data-room-status-class="wire-badge" class="wire-badge wire-badge--default">connecting…</span>
<div data-room-log class="chat-log wire-card"></div>
<template data-room-item="message"><div><strong>%user%</strong>: %text%</div></template>
<template data-room-item="system"><div class="chat-row--system"><em>%text%</em></div></template>
<form data-room-send class="chat-form">
<input name="user" class="wire-input chat-name" placeholder="Your name" autocomplete="off">
<input name="text" class="wire-input chat-msg" placeholder="Type a message…" data-room-reset autocomplete="off">
<button type="submit" class="wire-btn wire-btn--primary">Send</button>
</form>
</div>
</div>
<p><a href="/">← Home</a></p>
}
}
@@ -0,0 +1,34 @@
// Dashboard page. Route: /dashboard. Uses the sidebar layout.
page Dashboard {
layout = "dashboard"
seo {
title = "Dashboard"
}
view {
<!-- Fills the layout's named "actions" slot; the rest is the default slot. -->
<div data-slot="actions">
<div data-component="button" variant="primary" label="+ New"></div>
</div>
<h1>Dashboard</h1>
<p>This page selects the <code>dashboard</code> layout (sidebar nav), and fills
its named <code>actions</code> slot with a button.</p>
<div data-component="grid" cols="3" gap="4">
<div data-component="card">
<strong>Users</strong>
<div data-component="badge" variant="success" label="1,204"></div>
</div>
<div data-component="card">
<strong>Revenue</strong>
<div data-component="badge" variant="primary" label="$8.2k"></div>
</div>
<div data-component="card">
<strong>Errors</strong>
<div data-component="badge" variant="danger" label="3"></div>
</div>
</div>
}
}
+130
View File
@@ -0,0 +1,130 @@
// A page written in the .wrn language. Route: /hello
//
// SSR:
// `view` compiles to server-rendered HTML and is returned on the first request.
//
// CSR:
// `state`, `{expr}`, and `@click` compile to hydrated behavior.
// The browser hydrates those directives with /__wrnexus/reactive.js.
page Hello {
layout = "public"
// Client state seed. Without this block the page is pure SSR.
state count = 0
seo {
title = "Hello from .wrn"
description = "A WrNexus .wrn page showing SSR data, CSR hydration, cookies, sessions, and localStorage."
canonical = "/hello"
}
// SSR data bindings run on the server before the HTML is sent.
// The API itself lives in app/api/users/ssr.ts; this block only calls it
// and renders the response into HTML.
ssr {
functions {
function userNames(users) {
return users.map((user) => user.name).join(", ")
}
}
api ssrUsers GET /api/users/ssr {
const visits = Number(cookies.get("hello_visits") ?? "0") + 1
cookies.set("hello_visits", String(visits), { sameSite: "Lax" })
session.set("lastHelloVisit", visits)
return `${userNames(users)} - visit ${visits}`
}
}
// Client data bindings hydrate after the first paint. The browser only sees
// an opaque data-wrnexus-csr id; WrNexus calls app/api/users/csr.ts on the server.
client {
functions {
function userNames(users) {
return users.map((user) => user.name).join(", ")
}
}
api csrUsers GET /api/users/csr {
const label = localStorage.get("wrnexus.label") ?? "browser"
session.set("lastClientLabel", label)
return `${userNames(users)} - ${label}`
}
}
view {
<!-- These elements are rendered on the server first. -->
<h1>Hello from .wrn</h1>
<!-- `{count}` and `{count * 2}` update in the browser after hydration. -->
<p>Hello, WrNexus. Count is {count}, doubled is {count * 2}.</p>
<div class="data-grid">
<div class="data-panel">
<h2>SSR API data</h2>
<p>This is fetched from app/api before HTML is sent.</p>
<div class="user-list" api="ssrUsers">Loading SSR users...</div>
</div>
<div class="data-panel">
<h2>CSR API data</h2>
<p>This is fetched from app/api after hydration.</p>
<div class="user-list" api="csrUsers">Loading CSR users...</div>
</div>
</div>
<!-- `@click` becomes data-on-click and runs against the client scope. -->
<div class="my-actions">
<button @click="count++">Increment</button>
<button @click="count = 0">Reset</button>
</div>
}
// Page-local CSS is inlined with the server-rendered HTML.
style {
h1 {
color: #2563eb;
}
.my-actions {
display: flex;
gap: 0.5rem;
margin-top: 0.75rem;
}
.data-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
margin: 1rem 0;
}
.data-panel {
border: 1px solid #d1d5db;
border-radius: 8px;
padding: 1rem;
}
.data-panel h2 {
font-size: 1rem;
margin: 0 0 0.5rem;
}
.user-list {
font-weight: 700;
margin-top: 0.5rem;
}
.my-actions button {
border: 1px solid #d1d5db;
border-radius: 6px;
padding: 0.45rem 0.7rem;
}
}
realtime hello {
on message(data) {
console.log(data)
}
}
}
+58
View File
@@ -0,0 +1,58 @@
// Home page. Route: / (a trailing `index` segment is dropped from the route).
//
// SSR-first: the body is server-rendered. Interactivity comes from components —
// `.wrn` files under app/components, rendered on the server and hydrated in the
// browser. Mount one with data-component="<name>" and pass props as attributes.
page Home {
layout = "public"
seo {
title = "Home"
description = "Welcome to the WrNexus basic app, an SSR-first framework demo."
}
view {
<h1>{t:home.title}</h1>
<p>{t:home.intro}</p>
<!-- Tailwind utility classes (flex layout + spacing) style the page; the
mount divs are replaced by each component's server-rendered HTML. -->
<div class="flex flex-wrap items-center gap-3 my-4">
<div data-component="counter" start="0" label="Count"></div>
<!-- The label is localized: `{t:key}` in a prop is resolved per-request. -->
<div data-component="counter" start="10" label="{t:nav.dashboard}"></div>
</div>
<!-- This box is styled entirely with theme tokens (var(--wire-*)), so it
restyles instantly when the theme changes. -->
<div class="themed-box">
This box uses theme tokens. Current theme controls its colors.
<button data-wire-theme-toggle class="theme-toggle">Toggle theme</button>
</div>
<p><a class="text-red-600 hover:underline" href="/about">About</a></p>
}
style {
.themed-box {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
padding: 1rem;
margin: 1rem 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.theme-toggle {
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
border: 0;
border-radius: var(--wire-radius-sm);
padding: 0.5rem 0.9rem;
cursor: pointer;
}
}
}
+34
View File
@@ -0,0 +1,34 @@
// Login page. Route: /login. Uses the centered `auth` layout.
// The form declares data-schema="login" — the framework injects the schema
// descriptor + the generic validator, which validates on submit/blur and writes
// messages into the [data-error] spans. The same schema guards POST /api/login.
page Login {
layout = "auth"
seo {
title = "Sign in"
}
view {
<form data-schema="login" method="post" action="/api/login" data-redirect="/dashboard">
<div data-component="stack" gap="4">
<h1>Sign in</h1>
<div class="wire-alert wire-alert--success" data-success="Signed in! The form posted JSON and was validated server-side." hidden></div>
<div>
<div data-component="input" type="email" name="email" placeholder="you@example.com"></div>
<span class="wire-field-error" data-error="email"></span>
</div>
<div>
<div data-component="input" type="password" name="password" placeholder="Password"></div>
<span class="wire-field-error" data-error="password"></span>
</div>
<button type="submit" class="wire-btn wire-btn--primary wire-btn--lg">Sign in</button>
<p><a href="/">Back home</a></p>
</div>
</form>
}
}
+29
View File
@@ -0,0 +1,29 @@
// Reactive directives demo. Route: /reactive
//
// No client island file is needed — the generic reactive runtime
// (/__wrnexus/reactive.js) hydrates the [data-scope] that `state` compiles to.
// `count` becomes a signal; `{expr}` text interpolation and `@click` bind to it.
page Reactive {
layout = "public"
// Seeds the reactive scope. The compiler wraps the view in
// <div data-scope="count: 0">, which the runtime hydrates.
state count = 0
seo {
title = "Reactive"
description = "Directive-driven reactivity bound to signals"
}
view {
<h1>Reactive directives</h1>
<div class="card">
<p>Count is <strong>{count}</strong>, doubled is <strong>{count * 2}</strong>.</p>
<button @click="count++">increment</button>
<button @click="count = 0">reset</button>
</div>
<p><a href="/">Home</a> · <a href="/about">About</a></p>
}
}
+85
View File
@@ -0,0 +1,85 @@
// Wire UI showcase. Route: /ui. Every element below is a server-rendered
// component from @wrnexus/ui (auto-discovered), styled by theme tokens.
page UI {
layout = "public"
seo {
title = "Wire UI"
description = "Built-in Wire UI components and layout primitives."
}
view {
<h1>Wire UI components</h1>
<div data-component="stack" gap="5">
<div data-component="alert" variant="info" title="Server-rendered" message="These components render on the server and ship no JS unless they are interactive."></div>
<div data-component="card">
<div data-component="stack" gap="3">
<strong>Buttons</strong>
<div data-component="hstack" gap="3">
<div data-component="button" variant="primary" label="Primary"></div>
<div data-component="button" variant="default" label="Default"></div>
<div data-component="button" variant="danger" label="Danger"></div>
<div data-component="button" variant="ghost" label="Ghost"></div>
</div>
</div>
</div>
<div data-component="grid" cols="3" gap="4">
<div data-component="card">
<strong>Badges</strong>
<div data-component="hstack" gap="2">
<div data-component="badge" variant="success" label="Live"></div>
<div data-component="badge" variant="warning" label="Beta"></div>
<div data-component="badge" variant="danger" label="Off"></div>
</div>
</div>
<div data-component="card">
<strong>Input</strong>
<div data-component="input" placeholder="Type here..."></div>
</div>
<div data-component="card">
<strong>Spinner</strong>
<div data-component="spinner"></div>
</div>
</div>
<div data-component="divider"></div>
<div data-component="card">
<div data-component="stack" gap="3">
<strong>More components</strong>
<div data-component="hstack" gap="3">
<div data-component="tag" label="default"></div>
<div data-component="tag" variant="primary" label="primary"></div>
<div data-component="tag" variant="success" label="success"></div>
<div data-component="tag" variant="danger" label="danger"></div>
</div>
<div data-component="select" name="fruit">
<option>Apple</option>
<option>Banana</option>
<option>Cherry</option>
</div>
<div data-component="switch" name="notify" label="Email notifications"></div>
<div data-component="radio" name="plan" value="pro" label="Pro plan"></div>
<div data-component="progress" value="65"></div>
<div data-component="skeleton" width="60%" height="1.2rem"></div>
<p>Hover this <div data-component="tooltip" text="I am a CSS tooltip"><strong>tooltip trigger</strong></div>.</p>
<div data-component="table">
<thead><tr><th>Name</th><th>Role</th></tr></thead>
<tbody><tr><td>Ada</td><td>Admin</td></tr><tr><td>Grace</td><td>Editor</td></tr></tbody>
</div>
</div>
</div>
<div data-component="divider"></div>
<div data-component="disclosure" summary="How do I override component styles?">
Four ways, least to most control: change a theme token, redefine a
<code>.wire-*</code> class in your CSS, pass a <code>class</code> prop, or run
<code>wrnexus eject &lt;name&gt;</code> to copy the component into app/components.
</div>
</div>
}
}