release: WRNexusJS 0.8.0
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
page AsyncData {
|
||||
layout = "public"
|
||||
|
||||
load server summary {
|
||||
return { message: "Rendered by the server loader" }
|
||||
}
|
||||
|
||||
load client profile {
|
||||
return { name: "Ada", role: "Administrator" }
|
||||
}
|
||||
|
||||
view {
|
||||
<section class="stack">
|
||||
<h1>Async data boundaries</h1>
|
||||
|
||||
<Async source="summary">
|
||||
<Loading><p>Preparing the server summary…</p></Loading>
|
||||
<Success data="summary"><p>{summary.message}</p></Success>
|
||||
<Error error="error"><p>{error.message}</p></Error>
|
||||
</Async>
|
||||
|
||||
<Async source="profile" retries="2">
|
||||
<Loading><p>Loading the browser profile…</p></Loading>
|
||||
<Success data="profile"><p>Welcome {profile.name} — {profile.role}</p></Success>
|
||||
<Error error="error"><p>Profile failed: {error.message}</p></Error>
|
||||
</Async>
|
||||
</section>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
page ClientOnly {
|
||||
layout = "public"
|
||||
render = "client"
|
||||
hydrate = "load"
|
||||
|
||||
state count: number = 0
|
||||
|
||||
view {
|
||||
<section class="wire-card stack">
|
||||
<p class="wire-badge">Mounted in the browser</p>
|
||||
<h1>Client-only page</h1>
|
||||
<button type="button" class="wire-btn" @click="count++">Count: {count}</button>
|
||||
</section>
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,11 @@ page Home {
|
||||
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>
|
||||
<button type="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>
|
||||
<p><a class="hover:underline" href="/language-tools">Language server playground</a></p>
|
||||
}
|
||||
|
||||
style {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Real page for checking compiler, formatter, LSP navigation, rename, and reactivity.
|
||||
page LanguageTools {
|
||||
layout = "public"
|
||||
|
||||
state count: number = 0
|
||||
state message: string = "The shared language server is active."
|
||||
|
||||
computed doubled: number = count * 2
|
||||
|
||||
seo {
|
||||
title = "WRN language tools"
|
||||
description = "Interactive WRN page used to verify editor and compiler features."
|
||||
canonical = "/language-tools"
|
||||
}
|
||||
|
||||
functions {
|
||||
function increment() {
|
||||
count++
|
||||
message = `Renaming count updates all ${count} references safely.`
|
||||
}
|
||||
|
||||
function reset() {
|
||||
count = 0
|
||||
message = "State reset. Try hover, definition, references, and rename in your editor."
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<main class="language-tools">
|
||||
<header>
|
||||
<span class="eyebrow">Editor-neutral WRN tooling</span>
|
||||
<h1>Language server playground</h1>
|
||||
<p>{message}</p>
|
||||
</header>
|
||||
|
||||
<section aria-labelledby="counter-heading" class="demo-card">
|
||||
<h2 id="counter-heading">Reactive Data symbol test</h2>
|
||||
<p aria-live="polite">Count: {count}; computed double: {doubled}</p>
|
||||
<div class="actions">
|
||||
<button type="button" @click="increment()">Increment</button>
|
||||
<button type="button" class="secondary" @click="reset()">Reset</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="features-heading" class="demo-card">
|
||||
<h2 id="features-heading">Features exercised by this file</h2>
|
||||
<ul>
|
||||
<li>Compiler and accessibility diagnostics</li>
|
||||
<li>Canonical formatting and completion</li>
|
||||
<li>Hover, symbols, definition, references, and rename</li>
|
||||
<li>Typed state, computed values, and browser events</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
}
|
||||
|
||||
style {
|
||||
.language-tools {
|
||||
display: grid;
|
||||
max-width: 58rem;
|
||||
gap: 1.25rem;
|
||||
margin: 3rem auto;
|
||||
}
|
||||
|
||||
.language-tools header,
|
||||
.demo-card {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: var(--wire-color-primary);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
padding: 1.25rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-lg);
|
||||
background: var(--wire-color-surface);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.actions button {
|
||||
padding: 0.65rem 1rem;
|
||||
border: 1px solid var(--wire-color-primary);
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-primary);
|
||||
color: var(--wire-color-primary-contrast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions .secondary {
|
||||
background: transparent;
|
||||
color: var(--wire-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,24 +11,49 @@ page Login {
|
||||
|
||||
view {
|
||||
<form data-schema="login" method="post" action="/api/login" data-redirect="/dashboard">
|
||||
<div data-component="stack" gap="4">
|
||||
<div class="auth-stack">
|
||||
<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>
|
||||
<label class="auth-field">
|
||||
<span>Email address</span>
|
||||
<input type="email" name="email" autocomplete="email" placeholder="you@example.com" required />
|
||||
<span class="wire-field-error" data-error="email"></span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<div data-component="input" type="password" name="password" placeholder="Password"></div>
|
||||
<label class="auth-field">
|
||||
<span>Password</span>
|
||||
<input type="password" name="password" autocomplete="current-password" placeholder="Password" required />
|
||||
<span class="wire-field-error" data-error="password"></span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<button type="submit" class="wire-btn wire-btn--primary wire-btn--lg">Sign in</button>
|
||||
<p><a href="/">Back home</a></p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
style {
|
||||
form,
|
||||
.auth-stack,
|
||||
.auth-field {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
form,
|
||||
.auth-stack {
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.auth-field input {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.7rem 0.8rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-surface);
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
page PartialStatic {
|
||||
layout = "public"
|
||||
render = "partial-static"
|
||||
|
||||
seo {
|
||||
title = "Partial-static streaming"
|
||||
description = "A build-time WRNexus shell with request-streamed dynamic content."
|
||||
}
|
||||
|
||||
view {
|
||||
<Static>
|
||||
<section class="wire-card stack">
|
||||
<p class="wire-badge">Built once</p>
|
||||
<h1>Build-time static shell</h1>
|
||||
<p>This heading and page structure are emitted into dist/partial-shells.json.</p>
|
||||
</section>
|
||||
</Static>
|
||||
|
||||
<Dynamic>
|
||||
<section class="wire-card stack" aria-live="polite">
|
||||
<p class="wire-badge">Streamed per request</p>
|
||||
<h2>Dynamic account region</h2>
|
||||
<p>Personalized data belongs inside this boundary.</p>
|
||||
</section>
|
||||
</Dynamic>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
page PlatformShowcase {
|
||||
layout = "public"
|
||||
render = "hybrid"
|
||||
hydrate = "load"
|
||||
|
||||
state active = "Admin"
|
||||
|
||||
view {
|
||||
<main class="platform-showcase">
|
||||
<header>
|
||||
<span class="eyebrow">Reactive primitives</span>
|
||||
<h1>Reactive platform showcase</h1>
|
||||
<p>Switch a dynamic component and verify transition and portal rendering.</p>
|
||||
</header>
|
||||
<section class="showcase-card">
|
||||
<button type="button" @click='active = active === "Admin" ? "Guest" : "Admin"'>Switch component</button>
|
||||
<Transition name="fade">
|
||||
<Component is={active}>
|
||||
<section class="component-case" data-component-case="Admin"><strong>Administrator tools</strong><p>Manage users, permissions, and platform settings.</p></section>
|
||||
<section class="component-case" data-component-case="Guest"><strong>Guest dashboard</strong><p>Explore the public workspace and available resources.</p></section>
|
||||
</Component>
|
||||
</Transition>
|
||||
</section>
|
||||
<div id="showcase-modal" class="portal-target" aria-live="polite"></div>
|
||||
<Portal to="#showcase-modal"><p>Portaled content rendered in its target.</p></Portal>
|
||||
</main>
|
||||
}
|
||||
|
||||
style {
|
||||
.platform-showcase {
|
||||
display: grid;
|
||||
max-width: 58rem;
|
||||
gap: 1.25rem;
|
||||
margin: 3rem auto;
|
||||
}
|
||||
|
||||
.platform-showcase header,
|
||||
.showcase-card,
|
||||
.component-case {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: var(--wire-color-primary);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.showcase-card,
|
||||
.portal-target {
|
||||
padding: 1.25rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-lg);
|
||||
background: var(--wire-color-surface);
|
||||
}
|
||||
|
||||
.showcase-card button {
|
||||
width: fit-content;
|
||||
padding: 0.7rem 1rem;
|
||||
border: 0;
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-primary);
|
||||
color: var(--wire-color-primary-contrast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fade-enter-active {
|
||||
animation: platform-fade 180ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes platform-fade {
|
||||
from { opacity: 0; transform: translateY(0.25rem); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CreateUserSchema } from "../schemas/create-user";
|
||||
|
||||
page ServerActions {
|
||||
seo {
|
||||
title = "Typed server actions"
|
||||
description = "Schema validation, CSRF, invalidation and progressive enhancement"
|
||||
}
|
||||
|
||||
action createUser using CreateUserSchema {
|
||||
invalidate("users")
|
||||
return { id: crypto.randomUUID(), name: input.name, email: input.email }
|
||||
}
|
||||
|
||||
view {
|
||||
<main>
|
||||
<h1>Typed server actions</h1>
|
||||
<p>The form works with or without JavaScript.</p>
|
||||
<form @submit="createUser">
|
||||
<label for="action-name">Name</label>
|
||||
<input id="action-name" name="name" autocomplete="name" required />
|
||||
<label for="action-email">Email</label>
|
||||
<input id="action-email" name="email" type="email" autocomplete="email" required />
|
||||
<button type="submit">Create user</button>
|
||||
</form>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user