From 2ca2d02b2264c904af6b9173010fa430e5ef73e8 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 21 Jul 2026 11:15:06 +0530 Subject: [PATCH] feat: support WRN imports and dynamic public shell --- .publish/ai/package.json | 2 +- .publish/authz/package.json | 2 +- .publish/cli/package.json | 22 +-- .publish/compiler/README.md | 39 +++-- .publish/compiler/package.json | 2 +- .publish/core/package.json | 2 +- .publish/csr/package.json | 4 +- .publish/db/package.json | 2 +- .publish/dev-server/package.json | 26 +-- .publish/encryption/package.json | 2 +- .publish/helpers/package.json | 4 +- .publish/i18n/package.json | 4 +- .publish/jwt/package.json | 2 +- .publish/mobile/package.json | 4 +- .publish/native/package.json | 2 +- .publish/oauth/package.json | 2 +- .publish/pubsub/package.json | 2 +- .publish/queue/package.json | 2 +- .publish/reactive/package.json | 2 +- .publish/router/package.json | 6 +- .publish/ssr/package.json | 4 +- .publish/styles/package.json | 4 +- .publish/test/package.json | 2 +- .publish/tracking/package.json | 2 +- .publish/ui/COMPONENTS.md | 4 +- .publish/ui/component-reference.json | 168 ++++++++++++++++++++ .publish/ui/components/PublicFooter.wrn | 86 +++++++--- .publish/ui/components/PublicHeader.wrn | 59 +++++-- .publish/ui/package.json | 4 +- .publish/ui/ui.css | 38 +++++ .publish/uploader/package.json | 4 +- .publish/validation/package.json | 2 +- editors/vscode/CHANGELOG.md | 6 + editors/vscode/README.md | 13 ++ editors/vscode/package-lock.json | 4 +- editors/vscode/package.json | 4 +- editors/vscode/snippets/wrn.json | 5 + editors/vscode/src/compiler.cjs | 27 ++++ editors/vscode/src/completion.js | 7 + editors/vscode/src/diagnostics.js | 12 ++ editors/vscode/src/formatter.js | 14 ++ editors/vscode/syntaxes/wrn.tmLanguage.json | 18 +++ editors/vscode/test/diagnostics.test.js | 13 ++ editors/vscode/test/validate.mjs | 7 + packages/ai/package.json | 2 +- packages/authz/package.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/update.ts | 9 ++ packages/compiler/README.md | 39 +++-- packages/compiler/package.json | 2 +- packages/compiler/src/codegen.ts | 2 + packages/compiler/src/native-codegen.ts | 2 +- packages/compiler/src/parser.ts | 18 +++ packages/compiler/test/compiler.test.ts | 14 ++ packages/core/package.json | 2 +- packages/csr/package.json | 2 +- packages/db/package.json | 2 +- packages/dev-server/package.json | 2 +- packages/encryption/package.json | 2 +- packages/helpers/package.json | 2 +- packages/i18n/package.json | 2 +- packages/jwt/package.json | 2 +- packages/mobile/package.json | 2 +- packages/native/package.json | 2 +- packages/oauth/package.json | 2 +- packages/pubsub/package.json | 2 +- packages/queue/package.json | 2 +- packages/reactive/package.json | 2 +- packages/router/package.json | 2 +- packages/ssr/package.json | 2 +- packages/styles/package.json | 2 +- packages/test/package.json | 2 +- packages/tracking/package.json | 2 +- packages/ui/COMPONENTS.md | 4 +- packages/ui/component-reference.json | 168 ++++++++++++++++++++ packages/ui/components/PublicFooter.wrn | 86 +++++++--- packages/ui/components/PublicHeader.wrn | 59 +++++-- packages/ui/package.json | 2 +- packages/ui/ui.css | 38 +++++ packages/uploader/package.json | 2 +- packages/validation/package.json | 2 +- update-package-versions.mjs | 2 +- 82 files changed, 959 insertions(+), 174 deletions(-) diff --git a/.publish/ai/package.json b/.publish/ai/package.json index cdd3f55c..a5caa239 100644 --- a/.publish/ai/package.json +++ b/.publish/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", "license": "MIT", diff --git a/.publish/authz/package.json b/.publish/authz/package.json index 08d04f17..6542d886 100644 --- a/.publish/authz/package.json +++ b/.publish/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/authz — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/cli/package.json b/.publish/cli/package.json index a081da06..6c7304da 100644 --- a/.publish/cli/package.json +++ b/.publish/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/cli — part of the WrNexus framework.", "license": "MIT", @@ -28,16 +28,16 @@ "wrnexus": "./dist/index.js" }, "dependencies": { - "@wrnexus/core": "^0.2.73", - "@wrnexus/router": "^0.2.73", - "@wrnexus/csr": "^0.2.73", - "@wrnexus/compiler": "^0.2.73", - "@wrnexus/styles": "^0.2.73", - "@wrnexus/dev-server": "^0.2.73", - "@wrnexus/ui": "^0.2.73", - "@wrnexus/validation": "^0.2.73", - "@wrnexus/i18n": "^0.2.73", - "@wrnexus/db": "^0.2.73" + "@wrnexus/core": "^0.2.74", + "@wrnexus/router": "^0.2.74", + "@wrnexus/csr": "^0.2.74", + "@wrnexus/compiler": "^0.2.74", + "@wrnexus/styles": "^0.2.74", + "@wrnexus/dev-server": "^0.2.74", + "@wrnexus/ui": "^0.2.74", + "@wrnexus/validation": "^0.2.74", + "@wrnexus/i18n": "^0.2.74", + "@wrnexus/db": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/compiler/README.md b/.publish/compiler/README.md index d543e5b9..e41d34de 100644 --- a/.publish/compiler/README.md +++ b/.publish/compiler/README.md @@ -8,6 +8,19 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr `@wrnexus/compiler` turns `.wrn` source into TypeScript that targets the framework's runtime primitives. A `.wrn` file declares either a `page` (a route) or a `component` (a reusable, prop-driven fragment) with blocks for `state`, `view` (plain HTML), `seo`, `style`, `functions`, `api`, `ssr`/`client` data bindings, and `realtime` websocket handlers. The pipeline is `source → Lexer → parse() → PageAst → generate() → TypeScript`. It is a build/server-side library — the WrNexus dev loader calls it to compile `.wrn` files on the fly, surfacing `ParseError` as a readable error page. +Static ES module imports may appear before the root declaration. Imported values are +available to server-rendered expressions, including component props: + +```wrn +import { appUrl } from "@wrnexus/helpers"; + +layout PublicLayout { + view { + + } +} +``` + ## Installation ```bash @@ -76,19 +89,19 @@ class Lexer { Exported type-only symbols describing the parsed tree: -| Type | Description | -| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `PageAst` | Root node including `kind`, `name`, `types`, typed `props`, typed `states`, `view`, styles, functions, data APIs, lifecycle, and routes. | -| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. | -| `Attr` | `{ name; value; event; boolean? }` — `event` marks `@event` bindings. | -| `StateDecl` | `{ name; valueType?; expr }` — a typed `state x: Type = ` declaration. | -| `PropDecl` | `{ name; valueType?; required; default }` — a typed prop declaration. | -| `SeoBlock` | `Record` from the `seo { ... }` block. | -| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. | -| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. | -| `DataMode` | `"ssr" \| "client"`. | -| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. | -| `RealtimeBlock` | `{ name; handlers }` — a `realtime { on evt(args) { ... } }` block. | +| Type | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PageAst` | Root node including top-level `imports`, `kind`, `name`, `types`, typed `props`, typed `states`, `view`, styles, functions, data APIs, lifecycle, and routes. | +| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. | +| `Attr` | `{ name; value; event; boolean? }` — `event` marks `@event` bindings. | +| `StateDecl` | `{ name; valueType?; expr }` — a typed `state x: Type = ` declaration. | +| `PropDecl` | `{ name; valueType?; required; default }` — a typed prop declaration. | +| `SeoBlock` | `Record` from the `seo { ... }` block. | +| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. | +| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. | +| `DataMode` | `"ssr" \| "client"`. | +| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. | +| `RealtimeBlock` | `{ name; handlers }` — a `realtime { on evt(args) { ... } }` block. | ## Usage diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json index 72f733c6..d6a0b523 100644 --- a/.publish/compiler/package.json +++ b/.publish/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/compiler — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/core/package.json b/.publish/core/package.json index 3ce94f03..96c07cce 100644 --- a/.publish/core/package.json +++ b/.publish/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/core — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/csr/package.json b/.publish/csr/package.json index 0c14f594..8e2fe2d4 100644 --- a/.publish/csr/package.json +++ b/.publish/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/csr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/db/package.json b/.publish/db/package.json index e04d281a..be37188e 100644 --- a/.publish/db/package.json +++ b/.publish/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/db — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/dev-server/package.json b/.publish/dev-server/package.json index 93e243a2..6622cfd7 100644 --- a/.publish/dev-server/package.json +++ b/.publish/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/dev-server — part of the WrNexus framework.", "license": "MIT", @@ -25,18 +25,18 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73", - "@wrnexus/router": "^0.2.73", - "@wrnexus/ssr": "^0.2.73", - "@wrnexus/csr": "^0.2.73", - "@wrnexus/compiler": "^0.2.73", - "@wrnexus/styles": "^0.2.73", - "@wrnexus/ui": "^0.2.73", - "@wrnexus/validation": "^0.2.73", - "@wrnexus/i18n": "^0.2.73", - "@wrnexus/db": "^0.2.73", - "@wrnexus/pubsub": "^0.2.73", - "@wrnexus/uploader": "^0.2.73" + "@wrnexus/core": "^0.2.74", + "@wrnexus/router": "^0.2.74", + "@wrnexus/ssr": "^0.2.74", + "@wrnexus/csr": "^0.2.74", + "@wrnexus/compiler": "^0.2.74", + "@wrnexus/styles": "^0.2.74", + "@wrnexus/ui": "^0.2.74", + "@wrnexus/validation": "^0.2.74", + "@wrnexus/i18n": "^0.2.74", + "@wrnexus/db": "^0.2.74", + "@wrnexus/pubsub": "^0.2.74", + "@wrnexus/uploader": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json index 5dab604a..88e85830 100644 --- a/.publish/encryption/package.json +++ b/.publish/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/encryption — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/helpers/package.json b/.publish/helpers/package.json index aa8b0098..180aba57 100644 --- a/.publish/helpers/package.json +++ b/.publish/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json index bd132978..9e01ddd3 100644 --- a/.publish/i18n/package.json +++ b/.publish/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/i18n — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json index 48ab8223..780c1b25 100644 --- a/.publish/jwt/package.json +++ b/.publish/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/jwt — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/mobile/package.json b/.publish/mobile/package.json index 33cab969..1bcaba81 100644 --- a/.publish/mobile/package.json +++ b/.publish/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/mobile — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/native": "^0.2.73" + "@wrnexus/native": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/native/package.json b/.publish/native/package.json index 148fcc0c..e94d0710 100644 --- a/.publish/native/package.json +++ b/.publish/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/native — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/oauth/package.json b/.publish/oauth/package.json index 82047f1d..8c586ef3 100644 --- a/.publish/oauth/package.json +++ b/.publish/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/oauth — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json index a16671e2..68400dfa 100644 --- a/.publish/pubsub/package.json +++ b/.publish/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/pubsub — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/queue/package.json b/.publish/queue/package.json index e1ae23ea..8a3baade 100644 --- a/.publish/queue/package.json +++ b/.publish/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/queue — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/reactive/package.json b/.publish/reactive/package.json index 995f4751..e082998f 100644 --- a/.publish/reactive/package.json +++ b/.publish/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/reactive — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/router/package.json b/.publish/router/package.json index 36a01207..460327d1 100644 --- a/.publish/router/package.json +++ b/.publish/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/router — part of the WrNexus framework.", "license": "MIT", @@ -21,8 +21,8 @@ } }, "dependencies": { - "@wrnexus/compiler": "^0.2.73", - "@wrnexus/core": "^0.2.73" + "@wrnexus/compiler": "^0.2.74", + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json index db25d9e8..23bd1d89 100644 --- a/.publish/ssr/package.json +++ b/.publish/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/ssr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/styles/package.json b/.publish/styles/package.json index 1707d783..cc8220d4 100644 --- a/.publish/styles/package.json +++ b/.publish/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/styles — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/uploader": "^0.2.73" + "@wrnexus/uploader": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/test/package.json b/.publish/test/package.json index d2c9a51d..230145e6 100644 --- a/.publish/test/package.json +++ b/.publish/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/test — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/tracking/package.json b/.publish/tracking/package.json index 9e141a81..1c8ee93f 100644 --- a/.publish/tracking/package.json +++ b/.publish/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/tracking — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/ui/COMPONENTS.md b/.publish/ui/COMPONENTS.md index e247c6e1..fe227b7c 100644 --- a/.publish/ui/COMPONENTS.md +++ b/.publish/ui/COMPONENTS.md @@ -3253,7 +3253,7 @@ Reusable progress bar component. Reusable public footer component. - Mount: `data-component="PublicFooter"` -- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `newsletterEyebrow: string = "WrNexus updates"`, `newsletterTitle: string = "Identity insights delivered to your inbox"`, `newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources."`, `newsletterAction: string = "/api/newsletter/subscribe"`, `newsletterButtonLabel: string = "Subscribe"`, `newsletterPlaceholder: string = "Enter your work email"`, `newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription."`, `copyrightText: string = "© 2026 WrNexus. All rights reserved."`, `attributionText: string = "Built by WorkRoot Workspace."`, `showNewsletter: boolean = true`, `showSocialLinks: boolean = true`, `showThemeToggle: boolean = true` +- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `brandDescription: string = "Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications."`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `newsletterEyebrow: string = "WrNexus updates"`, `newsletterTitle: string = "Identity insights delivered to your inbox"`, `newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources."`, `newsletterAction: string = "/api/newsletter/subscribe"`, `newsletterButtonLabel: string = "Subscribe"`, `newsletterPlaceholder: string = "Enter your work email"`, `newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription."`, `newsletterPrivacyLabel: string = "privacy policy"`, `newsletterPrivacyHref: string = "/privacy"`, `newsletterFinePrintPrefix: string = "No spam. Unsubscribe at any time. Read our"`, `newsletterFinePrintSuffix: string = "."`, `newsletterEmailLabel: string = "Work email address"`, `newsletterEmailName: string = "email"`, `copyrightText: string = "© 2026 WrNexus. All rights reserved."`, `attributionText: string = "Built by WorkRoot Workspace."`, `showNewsletter: boolean = true`, `showSocialLinks: boolean = true`, `showThemeToggle: boolean = true`, `showStatus: boolean = true`, `showCookiePreferences: boolean = true`, `cookiePreferencesLabel: string = "Cookie preferences"`, `themeLabel: string = "Theme"`, `themeToggleLabel: string = "Toggle color theme"`, `legalTitle: string = "Legal"`, `socialLinks: PublicFooterLink[] = []`, `navigationColumns: PublicFooterColumn[] = []`, `legalLinks: PublicFooterLink[] = []`, `navigationAriaLabel: string = "Footer navigation"` - Slots: `navigation` - Events: `submit` @@ -7203,7 +7203,7 @@ Reusable product page shell component. Reusable public header component. - Mount: `data-component="PublicHeader"` -- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `pricingLabel: string = "Pricing"`, `pricingHref: string = "/pricing"`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `signInLabel: string = "Sign in"`, `signInHref: string = "/sign-in"`, `primaryLabel: string = "Start free"`, `primaryHref: string = "/sign-up"`, `showStatus: boolean = true`, `showThemeToggle: boolean = true`, `showSignIn: boolean = true`, `showPrimaryAction: boolean = true`, `productLabel: string = "Product"`, `solutionsLabel: string = "Solutions"`, `developersLabel: string = "Developers"`, `resourcesLabel: string = "Resources"` +- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `pricingLabel: string = "Pricing"`, `pricingHref: string = "/pricing"`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `signInLabel: string = "Sign in"`, `signInHref: string = "/sign-in"`, `primaryLabel: string = "Start free"`, `primaryHref: string = "/sign-up"`, `showStatus: boolean = true`, `showThemeToggle: boolean = true`, `showSignIn: boolean = true`, `showPrimaryAction: boolean = true`, `productLabel: string = "Product"`, `solutionsLabel: string = "Solutions"`, `developersLabel: string = "Developers"`, `resourcesLabel: string = "Resources"`, `navigationAriaLabel: string = "Main navigation"`, `themeToggleLabel: string = "Toggle color theme"`, `mobileMenuOpenLabel: string = "Open navigation menu"`, `mobileMenuCloseLabel: string = "Close navigation menu"`, `navigationItems: PublicHeaderNavigationItem[] = []`, `actionItems: PublicHeaderActionItem[] = []`, `showNavigation: boolean = true`, `showMobileThemeToggle: boolean = true` - Slots: `navigation`, `actions` - Events: `click` diff --git a/.publish/ui/component-reference.json b/.publish/ui/component-reference.json index 276298e3..0650460f 100644 --- a/.publish/ui/component-reference.json +++ b/.publish/ui/component-reference.json @@ -33954,6 +33954,30 @@ "required": false, "default": "\"WrNexus home\"" }, + { + "name": "brandIcon", + "type": "string", + "required": false, + "default": "\"icon-[lucide--blocks]\"" + }, + { + "name": "brandDescription", + "type": "string", + "required": false, + "default": "\"Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications.\"" + }, + { + "name": "statusLabel", + "type": "string", + "required": false, + "default": "\"All systems operational\"" + }, + { + "name": "statusHref", + "type": "string", + "required": false, + "default": "\"/status\"" + }, { "name": "newsletterEyebrow", "type": "string", @@ -33996,6 +34020,42 @@ "required": false, "default": "\"Thanks. Please check your inbox to confirm your subscription.\"" }, + { + "name": "newsletterPrivacyLabel", + "type": "string", + "required": false, + "default": "\"privacy policy\"" + }, + { + "name": "newsletterPrivacyHref", + "type": "string", + "required": false, + "default": "\"/privacy\"" + }, + { + "name": "newsletterFinePrintPrefix", + "type": "string", + "required": false, + "default": "\"No spam. Unsubscribe at any time. Read our\"" + }, + { + "name": "newsletterFinePrintSuffix", + "type": "string", + "required": false, + "default": "\".\"" + }, + { + "name": "newsletterEmailLabel", + "type": "string", + "required": false, + "default": "\"Work email address\"" + }, + { + "name": "newsletterEmailName", + "type": "string", + "required": false, + "default": "\"email\"" + }, { "name": "copyrightText", "type": "string", @@ -34025,6 +34085,66 @@ "type": "boolean", "required": false, "default": "true" + }, + { + "name": "showStatus", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "showCookiePreferences", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "cookiePreferencesLabel", + "type": "string", + "required": false, + "default": "\"Cookie preferences\"" + }, + { + "name": "themeLabel", + "type": "string", + "required": false, + "default": "\"Theme\"" + }, + { + "name": "themeToggleLabel", + "type": "string", + "required": false, + "default": "\"Toggle color theme\"" + }, + { + "name": "legalTitle", + "type": "string", + "required": false, + "default": "\"Legal\"" + }, + { + "name": "socialLinks", + "type": "PublicFooterLink[]", + "required": false, + "default": "[]" + }, + { + "name": "navigationColumns", + "type": "PublicFooterColumn[]", + "required": false, + "default": "[]" + }, + { + "name": "legalLinks", + "type": "PublicFooterLink[]", + "required": false, + "default": "[]" + }, + { + "name": "navigationAriaLabel", + "type": "string", + "required": false, + "default": "\"Footer navigation\"" } ], "slots": ["navigation"], @@ -34168,6 +34288,54 @@ "type": "string", "required": false, "default": "\"Resources\"" + }, + { + "name": "navigationAriaLabel", + "type": "string", + "required": false, + "default": "\"Main navigation\"" + }, + { + "name": "themeToggleLabel", + "type": "string", + "required": false, + "default": "\"Toggle color theme\"" + }, + { + "name": "mobileMenuOpenLabel", + "type": "string", + "required": false, + "default": "\"Open navigation menu\"" + }, + { + "name": "mobileMenuCloseLabel", + "type": "string", + "required": false, + "default": "\"Close navigation menu\"" + }, + { + "name": "navigationItems", + "type": "PublicHeaderNavigationItem[]", + "required": false, + "default": "[]" + }, + { + "name": "actionItems", + "type": "PublicHeaderActionItem[]", + "required": false, + "default": "[]" + }, + { + "name": "showNavigation", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "showMobileThemeToggle", + "type": "boolean", + "required": false, + "default": "true" } ], "slots": ["navigation", "actions"], diff --git a/.publish/ui/components/PublicFooter.wrn b/.publish/ui/components/PublicFooter.wrn index 3a203b56..5fc4e871 100644 --- a/.publish/ui/components/PublicFooter.wrn +++ b/.publish/ui/components/PublicFooter.wrn @@ -1,10 +1,27 @@ component PublicFooter { + types { + interface PublicFooterLink { + label: string + href: string + ariaLabel?: string + target?: string + icon?: string + } + interface PublicFooterColumn { + title: string + links: PublicFooterLink[] + } + } props { class: string = "" homeHref: string = "/" brandName: string = "WrNexus" brandTagline: string = "Identity Cloud" brandAriaLabel: string = "WrNexus home" + brandIcon: string = "icon-[lucide--blocks]" + brandDescription: string = "Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications." + statusLabel: string = "All systems operational" + statusHref: string = "/status" newsletterEyebrow: string = "WrNexus updates" newsletterTitle: string = "Identity insights delivered to your inbox" newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources." @@ -12,11 +29,27 @@ component PublicFooter { newsletterButtonLabel: string = "Subscribe" newsletterPlaceholder: string = "Enter your work email" newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription." + newsletterPrivacyLabel: string = "privacy policy" + newsletterPrivacyHref: string = "/privacy" + newsletterFinePrintPrefix: string = "No spam. Unsubscribe at any time. Read our" + newsletterFinePrintSuffix: string = "." + newsletterEmailLabel: string = "Work email address" + newsletterEmailName: string = "email" copyrightText: string = "© 2026 WrNexus. All rights reserved." attributionText: string = "Built by WorkRoot Workspace." showNewsletter: boolean = true showSocialLinks: boolean = true showThemeToggle: boolean = true + showStatus: boolean = true + showCookiePreferences: boolean = true + cookiePreferencesLabel: string = "Cookie preferences" + themeLabel: string = "Theme" + themeToggleLabel: string = "Toggle color theme" + legalTitle: string = "Legal" + socialLinks: PublicFooterLink[] = [] + navigationColumns: PublicFooterColumn[] = [] + legalLinks: PublicFooterLink[] = [] + navigationAriaLabel: string = "Footer navigation" } state email = "" state newsletterSubmitted = false @@ -81,7 +114,7 @@ component PublicFooter { class="sr-only" for="footer-newsletter-email" > - Work email address + {newsletterEmailLabel}
@@ -89,7 +122,7 @@ component PublicFooter { - No spam. Unsubscribe at any time. Read our + {newsletterFinePrintPrefix} - privacy policy - . + {newsletterPrivacyLabel} + {newsletterFinePrintSuffix}

- + @@ -165,13 +198,12 @@ component PublicFooter {

- Secure authentication, user management, organizations, - authorization, and enterprise identity for modern applications. + {brandDescription}

-
+
+ {#if socialLinks.length > 0} + {#each socialLinks as item} + {item.label} + {/each} + {:else} + {/if}
@@ -570,6 +614,9 @@ component PublicFooter {
+ {#if legalLinks.length > 0} + {#each legalLinks as item}{item.label}{/each} + {:else} - Legal + {legalTitle} + {/if} - -
diff --git a/.publish/ui/components/PublicHeader.wrn b/.publish/ui/components/PublicHeader.wrn index 1279c6c8..5b8988b9 100644 --- a/.publish/ui/components/PublicHeader.wrn +++ b/.publish/ui/components/PublicHeader.wrn @@ -1,4 +1,15 @@ component PublicHeader { + types { + interface PublicHeaderNavigationItem { + label: string + href: string + ariaLabel?: string + target?: string + } + interface PublicHeaderActionItem extends PublicHeaderNavigationItem { + variant?: "primary" | "secondary" | "ghost" + } + } props { class: string = "" homeHref: string = "/" @@ -22,6 +33,14 @@ component PublicHeader { solutionsLabel: string = "Solutions" developersLabel: string = "Developers" resourcesLabel: string = "Resources" + navigationAriaLabel: string = "Main navigation" + themeToggleLabel: string = "Toggle color theme" + mobileMenuOpenLabel: string = "Open navigation menu" + mobileMenuCloseLabel: string = "Close navigation menu" + navigationItems: PublicHeaderNavigationItem[] = [] + actionItems: PublicHeaderActionItem[] = [] + showNavigation: boolean = true + showMobileThemeToggle: boolean = true } state activeMenu = "" state mobileMenuOpen = false @@ -71,10 +90,16 @@ component PublicHeader {
- +
+ {#if navigationItems.length > 0 || actionItems.length > 0} + + {:else} + {/if}
} diff --git a/.publish/ui/package.json b/.publish/ui/package.json index 5b17cde8..fce13e49 100644 --- a/.publish/ui/package.json +++ b/.publish/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/ui — part of the WrNexus framework.", "license": "MIT", @@ -25,7 +25,7 @@ "./ui.css": "./ui.css" }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist", diff --git a/.publish/ui/ui.css b/.publish/ui/ui.css index 0d5aa5ba..67bc7356 100644 --- a/.publish/ui/ui.css +++ b/.publish/ui/ui.css @@ -1186,6 +1186,44 @@ display: flex; align-items: center; } + +.wire-public-header-action { + display: inline-flex; + min-height: 2.5rem; + align-items: center; + justify-content: center; + gap: 0.5rem; + border: 1px solid transparent; + border-radius: 0.75rem; + padding: 0.625rem 1rem; + color: var(--wire-color-text); + font-size: 0.875rem; + font-weight: 700; + text-decoration: none; + transition: + background var(--wire-motion-base), + border-color var(--wire-motion-base), + color var(--wire-motion-base), + transform var(--wire-motion-base) var(--wire-ease-emphasized); +} +.wire-public-header-action--primary { + color: var(--wire-color-primary-contrast); + background: var(--wire-color-primary); + box-shadow: 0 0.75rem 1.5rem color-mix(in srgb, var(--wire-color-primary) 20%, transparent); +} +.wire-public-header-action--primary:hover { + background: var(--wire-color-primary-hover); + transform: translateY(-1px); +} +.wire-public-header-action--secondary { + border-color: var(--wire-color-border); + background: var(--wire-color-surface); +} +.wire-public-header-action--secondary:hover, +.wire-public-header-action--ghost:hover { + color: var(--wire-color-primary); + background: var(--wire-color-surface-2); +} .wire-auth-field__input { width: 100%; min-height: 3.5rem; diff --git a/.publish/uploader/package.json b/.publish/uploader/package.json index 52da8f6b..6026faf8 100644 --- a/.publish/uploader/package.json +++ b/.publish/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/uploader — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.73" + "@wrnexus/core": "^0.2.74" }, "files": [ "dist" diff --git a/.publish/validation/package.json b/.publish/validation/package.json index eb978a60..6e31a1a0 100644 --- a/.publish/validation/package.json +++ b/.publish/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "description": "@wrnexus/validation — part of the WrNexus framework.", "license": "MIT", diff --git a/editors/vscode/CHANGELOG.md b/editors/vscode/CHANGELOG.md index bf6ec7af..4e69aaf3 100644 --- a/editors/vscode/CHANGELOG.md +++ b/editors/vscode/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.14 + +- Added top-level ES module import highlighting, completion, snippets, formatting, + and diagnostics support for `.wrn` files. +- Imported helpers can now be used in server-rendered component prop expressions. + ## 0.2.13 - Added explicit prop and state types, local `types` blocks, and typed function diff --git a/editors/vscode/README.md b/editors/vscode/README.md index bf919025..aeddbb50 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -27,6 +27,19 @@ component UserCard { The extension shows declared prop types and reports missing required props and incompatible values. Typed prop and state expressions passed into nested components retain their declared type. +Top-level imports are supported before `page`, `component`, or `layout`. Imported +helpers can be used in server-rendered prop expressions: + +```wrn +import { appUrl } from "@wrnexus/helpers"; + +layout PublicLayout { + view { + + } +} +``` + ## Installation Search for **WRNexus Language Support** in the VS Code Extensions view or run: diff --git a/editors/vscode/package-lock.json b/editors/vscode/package-lock.json index 95e3cd11..cbf52919 100644 --- a/editors/vscode/package-lock.json +++ b/editors/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "wrnexus", - "version": "0.2.13", + "version": "0.2.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wrnexus", - "version": "0.2.13", + "version": "0.2.14", "license": "SEE LICENSE IN LICENSE", "devDependencies": { "@vscode/vsce": "^3.9.2" diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 54f146e5..ccb85c1d 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -2,7 +2,7 @@ "name": "wrnexus", "displayName": "WRNexus Language Support", "description": "Complete language support for WRNexus .wrn files, including highlighting, formatting, diagnostics, snippets, lifecycle hooks, state watchers, component functions, completions, and definition navigation.", - "version": "0.2.13", + "version": "0.2.14", "publisher": "wrnexus", "private": true, "license": "SEE LICENSE IN LICENSE", @@ -68,7 +68,7 @@ "extensions": [ ".wrn" ], - "firstLine": "^\\s*(?:page|component|layout)\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s*\\{", + "firstLine": "^\\s*(?:(?:import\\b[^;\\n]*(?:;|\\n)\\s*)*)(?:page|component|layout)\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s*\\{", "configuration": "./language-configuration.json", "icon": { "light": "./icons/wrn.png", diff --git a/editors/vscode/snippets/wrn.json b/editors/vscode/snippets/wrn.json index c3720a0d..db75eba0 100644 --- a/editors/vscode/snippets/wrn.json +++ b/editors/vscode/snippets/wrn.json @@ -1,4 +1,9 @@ { + "WRN import": { + "prefix": ["wrn-import", "import"], + "description": "Import a server-side helper into a WRN file", + "body": ["import { ${1:appUrl} } from \"${2:@wrnexus/helpers}\";", "", "$0"] + }, "WRN page": { "prefix": ["wrn-page", "page"], "description": "Create a WRN page", diff --git a/editors/vscode/src/compiler.cjs b/editors/vscode/src/compiler.cjs index ea2e3309..a8100b4d 100644 --- a/editors/vscode/src/compiler.cjs +++ b/editors/vscode/src/compiler.cjs @@ -382,6 +382,24 @@ function unescapeSeoValue(value) { } function parse(source) { const lx = new Lexer(source); + const imports = []; + const importPattern = /import\s+(?:type\s+)?(?:[\s\S]*?\s+from\s+)?["'][^"'\r\n]+["']\s*;?/y; + while (true) { + while (/\s/u.test(source[lx.pos] ?? "")) + lx.pos++; + if (source.startsWith("//", lx.pos)) { + while (lx.pos < source.length && source[lx.pos] !== ` +`) + lx.pos++; + continue; + } + importPattern.lastIndex = lx.pos; + const statement = importPattern.exec(source); + if (!statement) + break; + imports.push(statement[0].trim()); + lx.pos = importPattern.lastIndex; + } const expect = (type) => { const t = lx.next(); if (t.type !== type) { @@ -627,6 +645,7 @@ function parse(source) { } return { type: "page", + imports, kind, name, layout, @@ -1304,6 +1323,9 @@ function generate(ast) { return generateComponent(ast); } const out = []; + if (ast.imports.length > 0) + out.push(ast.imports.join(` +`)); const ssrBindings = []; const csrBindings = []; const helpers = ast.functions.map((body2) => body2.trim()).filter(Boolean).join(` @@ -1722,6 +1744,9 @@ function renderComponentNode(node, ctx) { } function generateComponent(ast) { const out = []; + if (ast.imports.length > 0) + out.push(ast.imports.join(` +`)); const hasServerEach = viewHasServerEach(ast.view); if (hasServerEach) { out.push(`import { Buffer } from "node:buffer";`); @@ -2192,6 +2217,8 @@ function generateNative(ast) { import React, { useState } from "react"; import { ActivityIndicator, FlatList, Image, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View } from "react-native"; import { useRouter } from "expo-router"; +${ast.imports.join(` +`)} ${typeSource} diff --git a/editors/vscode/src/completion.js b/editors/vscode/src/completion.js index 44617a62..ad81d82c 100644 --- a/editors/vscode/src/completion.js +++ b/editors/vscode/src/completion.js @@ -3,6 +3,13 @@ const vscode = require("vscode"); const BLOCK_COMPLETIONS = [ + { + label: "import", + detail: "WRN server module import", + documentation: + "Import a package or module before the page, component, or layout declaration for server rendering.", + snippet: 'import { ${1:appUrl} } from "${2:@wrnexus/helpers}";\n\n$0', + }, { label: "page", detail: "WRN page", diff --git a/editors/vscode/src/diagnostics.js b/editors/vscode/src/diagnostics.js index 2f05852b..c67f0ef5 100644 --- a/editors/vscode/src/diagnostics.js +++ b/editors/vscode/src/diagnostics.js @@ -89,6 +89,7 @@ function stripComments(source) { function maskLeadingTrivia(source) { const masked = [...source]; let offset = 0; + const importPattern = /import\s+(?:type\s+)?(?:[\s\S]*?\s+from\s+)?["'][^"'\r\n]+["']\s*;?/y; while (offset < source.length) { if (/\s/u.test(source[offset])) { @@ -96,6 +97,17 @@ function maskLeadingTrivia(source) { continue; } + importPattern.lastIndex = offset; + const importStatement = importPattern.exec(source); + if (importStatement) { + const end = importPattern.lastIndex; + while (offset < end) { + if (source[offset] !== "\n" && source[offset] !== "\r") masked[offset] = " "; + offset += 1; + } + continue; + } + if (!source.startsWith("//", offset)) break; while (offset < source.length && source[offset] !== "\n") { diff --git a/editors/vscode/src/formatter.js b/editors/vscode/src/formatter.js index e2890f03..4a29d410 100644 --- a/editors/vscode/src/formatter.js +++ b/editors/vscode/src/formatter.js @@ -326,6 +326,20 @@ function formatWrn(source, options = {}) { previousWasBlank = false; + if (/^import\b/.test(value)) { + const importLines = [value]; + while ( + !/(?:\bfrom\s+)?["'][^"']+["']\s*;?$/.test(importLines[importLines.length - 1]) && + index + 1 < inputLines.length + ) { + index += 1; + importLines.push(inputLines[index].trim()); + } + output.push(importLines[0], ...importLines.slice(1).map((line) => `${unit}${line}`)); + index += 1; + continue; + } + if (isMultilineOpeningTagStart(value)) { const collected = collectOpeningTag(inputLines, index); diff --git a/editors/vscode/syntaxes/wrn.tmLanguage.json b/editors/vscode/syntaxes/wrn.tmLanguage.json index 3cf9452c..6a593e83 100644 --- a/editors/vscode/syntaxes/wrn.tmLanguage.json +++ b/editors/vscode/syntaxes/wrn.tmLanguage.json @@ -6,6 +6,9 @@ { "include": "#comments" }, + { + "include": "#imports" + }, { "include": "#declaration" }, @@ -14,6 +17,21 @@ } ], "repository": { + "imports": { + "begin": "^\\s*(import)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.import.ts" + } + }, + "end": ";|$", + "name": "meta.import.wrn", + "patterns": [ + { + "include": "source.ts" + } + ] + }, "comments": { "patterns": [ { diff --git a/editors/vscode/test/diagnostics.test.js b/editors/vscode/test/diagnostics.test.js index 27725198..184b5db0 100644 --- a/editors/vscode/test/diagnostics.test.js +++ b/editors/vscode/test/diagnostics.test.js @@ -56,6 +56,19 @@ component ThemeToggle { assert.equal(declaration.diagnostic, undefined); }); +test("recognizes a WRN declaration after top-level imports", () => { + const source = `import { appUrl } from "@wrnexus/helpers"; +import type { Context } from "@wrnexus/core"; + +page Home { + view { } +}`; + const declaration = findTopLevelDeclaration({}, source); + assert.equal(declaration.kind, "page"); + assert.equal(declaration.name, "Home"); + assert.equal(declaration.diagnostic, undefined); +}); + test("masks only leading trivia and preserves source offsets", () => { const source = "\uFEFF// Summary\r\npage Home {\n // member comment\n}"; const masked = maskLeadingTrivia(source); diff --git a/editors/vscode/test/validate.mjs b/editors/vscode/test/validate.mjs index 055adfa2..7fc0f3d7 100644 --- a/editors/vscode/test/validate.mjs +++ b/editors/vscode/test/validate.mjs @@ -127,6 +127,13 @@ try { formatWrn(formatted, { insertSpaces: true, tabSize: 2 }) === formatted ? ok("formatter is idempotent") : bad("formatter is idempotent"); + const imported = formatWrn( + `import {\nappUrl,\nother\n} from "@wrnexus/helpers";\n\nlayout Public {\nview {

Hi

}\n}\n`, + { insertSpaces: true, tabSize: 2 }, + ); + imported.includes(`\nlayout Public {`) && formatWrn(imported, { tabSize: 2 }) === imported + ? ok("formatter preserves top-level imports") + : bad("formatter preserves top-level imports"); } catch (e) { bad("formatter loads", e.message); } diff --git a/packages/ai/package.json b/packages/ai/package.json index 6d896bb2..c19fcd98 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", diff --git a/packages/authz/package.json b/packages/authz/package.json index e45fb0c9..85f0a82b 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 0fdd6d7d..9384650a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts index 8f35454d..83055865 100644 --- a/packages/cli/src/update.ts +++ b/packages/cli/src/update.ts @@ -734,6 +734,15 @@ const MIGRATIONS: Migration[] = [ // Shared UI visuals improve automatically after updating and rebuilding. }, }, + { + version: "0.2.74", + id: "wrn-imports-and-dynamic-public-shell", + description: + "Adds top-level WRN imports and data-driven public header and footer navigation, actions, links, and labels.", + apply() { + // Language and shared UI behavior improve automatically after updating. + }, + }, ]; /** Release tooling uses this to require an explicit migration entry per version. */ diff --git a/packages/compiler/README.md b/packages/compiler/README.md index d543e5b9..e41d34de 100644 --- a/packages/compiler/README.md +++ b/packages/compiler/README.md @@ -8,6 +8,19 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr `@wrnexus/compiler` turns `.wrn` source into TypeScript that targets the framework's runtime primitives. A `.wrn` file declares either a `page` (a route) or a `component` (a reusable, prop-driven fragment) with blocks for `state`, `view` (plain HTML), `seo`, `style`, `functions`, `api`, `ssr`/`client` data bindings, and `realtime` websocket handlers. The pipeline is `source → Lexer → parse() → PageAst → generate() → TypeScript`. It is a build/server-side library — the WrNexus dev loader calls it to compile `.wrn` files on the fly, surfacing `ParseError` as a readable error page. +Static ES module imports may appear before the root declaration. Imported values are +available to server-rendered expressions, including component props: + +```wrn +import { appUrl } from "@wrnexus/helpers"; + +layout PublicLayout { + view { + + } +} +``` + ## Installation ```bash @@ -76,19 +89,19 @@ class Lexer { Exported type-only symbols describing the parsed tree: -| Type | Description | -| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `PageAst` | Root node including `kind`, `name`, `types`, typed `props`, typed `states`, `view`, styles, functions, data APIs, lifecycle, and routes. | -| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. | -| `Attr` | `{ name; value; event; boolean? }` — `event` marks `@event` bindings. | -| `StateDecl` | `{ name; valueType?; expr }` — a typed `state x: Type = ` declaration. | -| `PropDecl` | `{ name; valueType?; required; default }` — a typed prop declaration. | -| `SeoBlock` | `Record` from the `seo { ... }` block. | -| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. | -| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. | -| `DataMode` | `"ssr" \| "client"`. | -| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. | -| `RealtimeBlock` | `{ name; handlers }` — a `realtime { on evt(args) { ... } }` block. | +| Type | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PageAst` | Root node including top-level `imports`, `kind`, `name`, `types`, typed `props`, typed `states`, `view`, styles, functions, data APIs, lifecycle, and routes. | +| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. | +| `Attr` | `{ name; value; event; boolean? }` — `event` marks `@event` bindings. | +| `StateDecl` | `{ name; valueType?; expr }` — a typed `state x: Type = ` declaration. | +| `PropDecl` | `{ name; valueType?; required; default }` — a typed prop declaration. | +| `SeoBlock` | `Record` from the `seo { ... }` block. | +| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. | +| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. | +| `DataMode` | `"ssr" \| "client"`. | +| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. | +| `RealtimeBlock` | `{ name; handlers }` — a `realtime { on evt(args) { ... } }` block. | ## Usage diff --git a/packages/compiler/package.json b/packages/compiler/package.json index f3644534..e9876143 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/compiler/src/codegen.ts b/packages/compiler/src/codegen.ts index 6344acbb..dd6d776c 100644 --- a/packages/compiler/src/codegen.ts +++ b/packages/compiler/src/codegen.ts @@ -660,6 +660,7 @@ export function generate(ast: PageAst): string { } const out: string[] = []; + if (ast.imports.length > 0) out.push(ast.imports.join("\n")); const ssrBindings: SsrBinding[] = []; const csrBindings: CsrBinding[] = []; const helpers = ast.functions @@ -1292,6 +1293,7 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string { function generateComponent(ast: PageAst): string { const out: string[] = []; + if (ast.imports.length > 0) out.push(ast.imports.join("\n")); const hasServerEach = viewHasServerEach(ast.view); if (hasServerEach) { diff --git a/packages/compiler/src/native-codegen.ts b/packages/compiler/src/native-codegen.ts index 2ea1ba67..1baf7768 100644 --- a/packages/compiler/src/native-codegen.ts +++ b/packages/compiler/src/native-codegen.ts @@ -233,5 +233,5 @@ export function generateNative(ast: PageAst): string { .map((block) => block.trim()) .filter(Boolean) .join("\n\n"); - return `// generated from .wrn for Expo/React Native\nimport React, { useState } from "react";\nimport { ActivityIndicator, FlatList, Image, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View } from "react-native";\nimport { useRouter } from "expo-router";\n\n${typeSource}\n\nexport default function ${ast.name}() {\n const router = useRouter();\n${hooks}\n return <>${body};\n}\n\n${nativeStyles(ast.styles)}\n`; + return `// generated from .wrn for Expo/React Native\nimport React, { useState } from "react";\nimport { ActivityIndicator, FlatList, Image, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View } from "react-native";\nimport { useRouter } from "expo-router";\n${ast.imports.join("\n")}\n\n${typeSource}\n\nexport default function ${ast.name}() {\n const router = useRouter();\n${hooks}\n return <>${body};\n}\n\n${nativeStyles(ast.styles)}\n`; } diff --git a/packages/compiler/src/parser.ts b/packages/compiler/src/parser.ts index 1bf4cfe2..25fdaaa4 100644 --- a/packages/compiler/src/parser.ts +++ b/packages/compiler/src/parser.ts @@ -146,6 +146,8 @@ export interface PropDecl { export interface PageAst { type: "page"; + /** Static ES module imports declared before the WRN root declaration. */ + imports: string[]; /** * `page` is a route, `component` is a reusable fragment, * and `layout` is a reusable page wrapper. @@ -197,6 +199,21 @@ function unescapeSeoValue(value: string): string { export function parse(source: string): PageAst { const lx = new Lexer(source); + const imports: string[] = []; + const importPattern = /import\s+(?:type\s+)?(?:[\s\S]*?\s+from\s+)?["'][^"'\r\n]+["']\s*;?/y; + while (true) { + while (/\s/u.test(source[lx.pos] ?? "")) lx.pos++; + if (source.startsWith("//", lx.pos)) { + while (lx.pos < source.length && source[lx.pos] !== "\n") lx.pos++; + continue; + } + importPattern.lastIndex = lx.pos; + const statement = importPattern.exec(source); + if (!statement) break; + imports.push(statement[0].trim()); + lx.pos = importPattern.lastIndex; + } + const expect = (type: Token["type"]): Token => { const t = lx.next(); if (t.type !== type) { @@ -474,6 +491,7 @@ export function parse(source: string): PageAst { return { type: "page", + imports, kind, name, layout, diff --git a/packages/compiler/test/compiler.test.ts b/packages/compiler/test/compiler.test.ts index 15a50c1f..8e2fb685 100644 --- a/packages/compiler/test/compiler.test.ts +++ b/packages/compiler/test/compiler.test.ts @@ -57,6 +57,20 @@ test("parses a page with a layout member", () => { expect(ast.layout).toBe("public"); }); +test("top-level imports are preserved and available to SSR view expressions", async () => { + const source = `import { posix } from "node:path"; + +page Home { + view { } +}`; + const ast = parse(source); + expect(ast.imports).toEqual(['import { posix } from "node:path";']); + + const mod = await compileAndImport(source); + const render = mod.default as (ctx: unknown) => string; + expect(String(await render({}))).toContain('signInHref="/sso/sign-in"'); +}); + test("parses a component with props and state", () => { const ast = parse( `component C {\n props {\n n = 0\n }\n state count = n\n view { {count} }\n}`, diff --git a/packages/core/package.json b/packages/core/package.json index 97bd1d86..a1102d2d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/package.json b/packages/csr/package.json index f9131cee..fb93b289 100644 --- a/packages/csr/package.json +++ b/packages/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/db/package.json b/packages/db/package.json index a5beb280..31caaa34 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 7ba49ce2..f546baed 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/encryption/package.json b/packages/encryption/package.json index 79b80bc5..c97420f3 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/helpers/package.json b/packages/helpers/package.json index 00d528fa..aba8d081 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 7f367c73..762061b0 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/jwt/package.json b/packages/jwt/package.json index aaa1c406..401fe5a0 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index e1a3520f..651d6e9a 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/native/package.json b/packages/native/package.json index 183ed123..4b7f0dfc 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/oauth/package.json b/packages/oauth/package.json index a6bac51c..c96f1b0a 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index 24cf9a93..70b4506b 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/queue/package.json b/packages/queue/package.json index 73f7ecdc..d00be1f8 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/reactive/package.json b/packages/reactive/package.json index 5e3d2088..a180691f 100644 --- a/packages/reactive/package.json +++ b/packages/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/router/package.json b/packages/router/package.json index f1e23615..6497c1d4 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/ssr/package.json b/packages/ssr/package.json index c8c71db9..61af0d10 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/styles/package.json b/packages/styles/package.json index da9bd1b7..44520ead 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.73", + "version": "0.2.74", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/test/package.json b/packages/test/package.json index 946bff51..a6a70a53 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/tracking/package.json b/packages/tracking/package.json index 29e77095..c5bbb1e1 100644 --- a/packages/tracking/package.json +++ b/packages/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/COMPONENTS.md b/packages/ui/COMPONENTS.md index e247c6e1..fe227b7c 100644 --- a/packages/ui/COMPONENTS.md +++ b/packages/ui/COMPONENTS.md @@ -3253,7 +3253,7 @@ Reusable progress bar component. Reusable public footer component. - Mount: `data-component="PublicFooter"` -- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `newsletterEyebrow: string = "WrNexus updates"`, `newsletterTitle: string = "Identity insights delivered to your inbox"`, `newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources."`, `newsletterAction: string = "/api/newsletter/subscribe"`, `newsletterButtonLabel: string = "Subscribe"`, `newsletterPlaceholder: string = "Enter your work email"`, `newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription."`, `copyrightText: string = "© 2026 WrNexus. All rights reserved."`, `attributionText: string = "Built by WorkRoot Workspace."`, `showNewsletter: boolean = true`, `showSocialLinks: boolean = true`, `showThemeToggle: boolean = true` +- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `brandDescription: string = "Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications."`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `newsletterEyebrow: string = "WrNexus updates"`, `newsletterTitle: string = "Identity insights delivered to your inbox"`, `newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources."`, `newsletterAction: string = "/api/newsletter/subscribe"`, `newsletterButtonLabel: string = "Subscribe"`, `newsletterPlaceholder: string = "Enter your work email"`, `newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription."`, `newsletterPrivacyLabel: string = "privacy policy"`, `newsletterPrivacyHref: string = "/privacy"`, `newsletterFinePrintPrefix: string = "No spam. Unsubscribe at any time. Read our"`, `newsletterFinePrintSuffix: string = "."`, `newsletterEmailLabel: string = "Work email address"`, `newsletterEmailName: string = "email"`, `copyrightText: string = "© 2026 WrNexus. All rights reserved."`, `attributionText: string = "Built by WorkRoot Workspace."`, `showNewsletter: boolean = true`, `showSocialLinks: boolean = true`, `showThemeToggle: boolean = true`, `showStatus: boolean = true`, `showCookiePreferences: boolean = true`, `cookiePreferencesLabel: string = "Cookie preferences"`, `themeLabel: string = "Theme"`, `themeToggleLabel: string = "Toggle color theme"`, `legalTitle: string = "Legal"`, `socialLinks: PublicFooterLink[] = []`, `navigationColumns: PublicFooterColumn[] = []`, `legalLinks: PublicFooterLink[] = []`, `navigationAriaLabel: string = "Footer navigation"` - Slots: `navigation` - Events: `submit` @@ -7203,7 +7203,7 @@ Reusable product page shell component. Reusable public header component. - Mount: `data-component="PublicHeader"` -- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `pricingLabel: string = "Pricing"`, `pricingHref: string = "/pricing"`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `signInLabel: string = "Sign in"`, `signInHref: string = "/sign-in"`, `primaryLabel: string = "Start free"`, `primaryHref: string = "/sign-up"`, `showStatus: boolean = true`, `showThemeToggle: boolean = true`, `showSignIn: boolean = true`, `showPrimaryAction: boolean = true`, `productLabel: string = "Product"`, `solutionsLabel: string = "Solutions"`, `developersLabel: string = "Developers"`, `resourcesLabel: string = "Resources"` +- Props: `class: string = ""`, `homeHref: string = "/"`, `brandName: string = "WrNexus"`, `brandTagline: string = "Identity Cloud"`, `brandAriaLabel: string = "WrNexus home"`, `brandIcon: string = "icon-[lucide--blocks]"`, `pricingLabel: string = "Pricing"`, `pricingHref: string = "/pricing"`, `statusLabel: string = "All systems operational"`, `statusHref: string = "/status"`, `signInLabel: string = "Sign in"`, `signInHref: string = "/sign-in"`, `primaryLabel: string = "Start free"`, `primaryHref: string = "/sign-up"`, `showStatus: boolean = true`, `showThemeToggle: boolean = true`, `showSignIn: boolean = true`, `showPrimaryAction: boolean = true`, `productLabel: string = "Product"`, `solutionsLabel: string = "Solutions"`, `developersLabel: string = "Developers"`, `resourcesLabel: string = "Resources"`, `navigationAriaLabel: string = "Main navigation"`, `themeToggleLabel: string = "Toggle color theme"`, `mobileMenuOpenLabel: string = "Open navigation menu"`, `mobileMenuCloseLabel: string = "Close navigation menu"`, `navigationItems: PublicHeaderNavigationItem[] = []`, `actionItems: PublicHeaderActionItem[] = []`, `showNavigation: boolean = true`, `showMobileThemeToggle: boolean = true` - Slots: `navigation`, `actions` - Events: `click` diff --git a/packages/ui/component-reference.json b/packages/ui/component-reference.json index 276298e3..0650460f 100644 --- a/packages/ui/component-reference.json +++ b/packages/ui/component-reference.json @@ -33954,6 +33954,30 @@ "required": false, "default": "\"WrNexus home\"" }, + { + "name": "brandIcon", + "type": "string", + "required": false, + "default": "\"icon-[lucide--blocks]\"" + }, + { + "name": "brandDescription", + "type": "string", + "required": false, + "default": "\"Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications.\"" + }, + { + "name": "statusLabel", + "type": "string", + "required": false, + "default": "\"All systems operational\"" + }, + { + "name": "statusHref", + "type": "string", + "required": false, + "default": "\"/status\"" + }, { "name": "newsletterEyebrow", "type": "string", @@ -33996,6 +34020,42 @@ "required": false, "default": "\"Thanks. Please check your inbox to confirm your subscription.\"" }, + { + "name": "newsletterPrivacyLabel", + "type": "string", + "required": false, + "default": "\"privacy policy\"" + }, + { + "name": "newsletterPrivacyHref", + "type": "string", + "required": false, + "default": "\"/privacy\"" + }, + { + "name": "newsletterFinePrintPrefix", + "type": "string", + "required": false, + "default": "\"No spam. Unsubscribe at any time. Read our\"" + }, + { + "name": "newsletterFinePrintSuffix", + "type": "string", + "required": false, + "default": "\".\"" + }, + { + "name": "newsletterEmailLabel", + "type": "string", + "required": false, + "default": "\"Work email address\"" + }, + { + "name": "newsletterEmailName", + "type": "string", + "required": false, + "default": "\"email\"" + }, { "name": "copyrightText", "type": "string", @@ -34025,6 +34085,66 @@ "type": "boolean", "required": false, "default": "true" + }, + { + "name": "showStatus", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "showCookiePreferences", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "cookiePreferencesLabel", + "type": "string", + "required": false, + "default": "\"Cookie preferences\"" + }, + { + "name": "themeLabel", + "type": "string", + "required": false, + "default": "\"Theme\"" + }, + { + "name": "themeToggleLabel", + "type": "string", + "required": false, + "default": "\"Toggle color theme\"" + }, + { + "name": "legalTitle", + "type": "string", + "required": false, + "default": "\"Legal\"" + }, + { + "name": "socialLinks", + "type": "PublicFooterLink[]", + "required": false, + "default": "[]" + }, + { + "name": "navigationColumns", + "type": "PublicFooterColumn[]", + "required": false, + "default": "[]" + }, + { + "name": "legalLinks", + "type": "PublicFooterLink[]", + "required": false, + "default": "[]" + }, + { + "name": "navigationAriaLabel", + "type": "string", + "required": false, + "default": "\"Footer navigation\"" } ], "slots": ["navigation"], @@ -34168,6 +34288,54 @@ "type": "string", "required": false, "default": "\"Resources\"" + }, + { + "name": "navigationAriaLabel", + "type": "string", + "required": false, + "default": "\"Main navigation\"" + }, + { + "name": "themeToggleLabel", + "type": "string", + "required": false, + "default": "\"Toggle color theme\"" + }, + { + "name": "mobileMenuOpenLabel", + "type": "string", + "required": false, + "default": "\"Open navigation menu\"" + }, + { + "name": "mobileMenuCloseLabel", + "type": "string", + "required": false, + "default": "\"Close navigation menu\"" + }, + { + "name": "navigationItems", + "type": "PublicHeaderNavigationItem[]", + "required": false, + "default": "[]" + }, + { + "name": "actionItems", + "type": "PublicHeaderActionItem[]", + "required": false, + "default": "[]" + }, + { + "name": "showNavigation", + "type": "boolean", + "required": false, + "default": "true" + }, + { + "name": "showMobileThemeToggle", + "type": "boolean", + "required": false, + "default": "true" } ], "slots": ["navigation", "actions"], diff --git a/packages/ui/components/PublicFooter.wrn b/packages/ui/components/PublicFooter.wrn index 3a203b56..5fc4e871 100644 --- a/packages/ui/components/PublicFooter.wrn +++ b/packages/ui/components/PublicFooter.wrn @@ -1,10 +1,27 @@ component PublicFooter { + types { + interface PublicFooterLink { + label: string + href: string + ariaLabel?: string + target?: string + icon?: string + } + interface PublicFooterColumn { + title: string + links: PublicFooterLink[] + } + } props { class: string = "" homeHref: string = "/" brandName: string = "WrNexus" brandTagline: string = "Identity Cloud" brandAriaLabel: string = "WrNexus home" + brandIcon: string = "icon-[lucide--blocks]" + brandDescription: string = "Secure authentication, user management, organizations, authorization, and enterprise identity for modern applications." + statusLabel: string = "All systems operational" + statusHref: string = "/status" newsletterEyebrow: string = "WrNexus updates" newsletterTitle: string = "Identity insights delivered to your inbox" newsletterDescription: string = "Get product updates, security guidance, implementation strategies, and practical identity architecture resources." @@ -12,11 +29,27 @@ component PublicFooter { newsletterButtonLabel: string = "Subscribe" newsletterPlaceholder: string = "Enter your work email" newsletterSuccessMessage: string = "Thanks. Please check your inbox to confirm your subscription." + newsletterPrivacyLabel: string = "privacy policy" + newsletterPrivacyHref: string = "/privacy" + newsletterFinePrintPrefix: string = "No spam. Unsubscribe at any time. Read our" + newsletterFinePrintSuffix: string = "." + newsletterEmailLabel: string = "Work email address" + newsletterEmailName: string = "email" copyrightText: string = "© 2026 WrNexus. All rights reserved." attributionText: string = "Built by WorkRoot Workspace." showNewsletter: boolean = true showSocialLinks: boolean = true showThemeToggle: boolean = true + showStatus: boolean = true + showCookiePreferences: boolean = true + cookiePreferencesLabel: string = "Cookie preferences" + themeLabel: string = "Theme" + themeToggleLabel: string = "Toggle color theme" + legalTitle: string = "Legal" + socialLinks: PublicFooterLink[] = [] + navigationColumns: PublicFooterColumn[] = [] + legalLinks: PublicFooterLink[] = [] + navigationAriaLabel: string = "Footer navigation" } state email = "" state newsletterSubmitted = false @@ -81,7 +114,7 @@ component PublicFooter { class="sr-only" for="footer-newsletter-email" > - Work email address + {newsletterEmailLabel}
@@ -89,7 +122,7 @@ component PublicFooter { - No spam. Unsubscribe at any time. Read our + {newsletterFinePrintPrefix} - privacy policy - . + {newsletterPrivacyLabel} + {newsletterFinePrintSuffix}

- + @@ -165,13 +198,12 @@ component PublicFooter {

- Secure authentication, user management, organizations, - authorization, and enterprise identity for modern applications. + {brandDescription}

-
+
+ {#if socialLinks.length > 0} + {#each socialLinks as item} + {item.label} + {/each} + {:else} + {/if}
@@ -570,6 +614,9 @@ component PublicFooter {
+ {#if legalLinks.length > 0} + {#each legalLinks as item}{item.label}{/each} + {:else} - Legal + {legalTitle} + {/if} - -
diff --git a/packages/ui/components/PublicHeader.wrn b/packages/ui/components/PublicHeader.wrn index 1279c6c8..5b8988b9 100644 --- a/packages/ui/components/PublicHeader.wrn +++ b/packages/ui/components/PublicHeader.wrn @@ -1,4 +1,15 @@ component PublicHeader { + types { + interface PublicHeaderNavigationItem { + label: string + href: string + ariaLabel?: string + target?: string + } + interface PublicHeaderActionItem extends PublicHeaderNavigationItem { + variant?: "primary" | "secondary" | "ghost" + } + } props { class: string = "" homeHref: string = "/" @@ -22,6 +33,14 @@ component PublicHeader { solutionsLabel: string = "Solutions" developersLabel: string = "Developers" resourcesLabel: string = "Resources" + navigationAriaLabel: string = "Main navigation" + themeToggleLabel: string = "Toggle color theme" + mobileMenuOpenLabel: string = "Open navigation menu" + mobileMenuCloseLabel: string = "Close navigation menu" + navigationItems: PublicHeaderNavigationItem[] = [] + actionItems: PublicHeaderActionItem[] = [] + showNavigation: boolean = true + showMobileThemeToggle: boolean = true } state activeMenu = "" state mobileMenuOpen = false @@ -71,10 +90,16 @@ component PublicHeader {
- +
+ {#if navigationItems.length > 0 || actionItems.length > 0} + + {:else} + {/if}
} diff --git a/packages/ui/package.json b/packages/ui/package.json index fc5149aa..d0c0291f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/ui.css b/packages/ui/ui.css index 0d5aa5ba..67bc7356 100644 --- a/packages/ui/ui.css +++ b/packages/ui/ui.css @@ -1186,6 +1186,44 @@ display: flex; align-items: center; } + +.wire-public-header-action { + display: inline-flex; + min-height: 2.5rem; + align-items: center; + justify-content: center; + gap: 0.5rem; + border: 1px solid transparent; + border-radius: 0.75rem; + padding: 0.625rem 1rem; + color: var(--wire-color-text); + font-size: 0.875rem; + font-weight: 700; + text-decoration: none; + transition: + background var(--wire-motion-base), + border-color var(--wire-motion-base), + color var(--wire-motion-base), + transform var(--wire-motion-base) var(--wire-ease-emphasized); +} +.wire-public-header-action--primary { + color: var(--wire-color-primary-contrast); + background: var(--wire-color-primary); + box-shadow: 0 0.75rem 1.5rem color-mix(in srgb, var(--wire-color-primary) 20%, transparent); +} +.wire-public-header-action--primary:hover { + background: var(--wire-color-primary-hover); + transform: translateY(-1px); +} +.wire-public-header-action--secondary { + border-color: var(--wire-color-border); + background: var(--wire-color-surface); +} +.wire-public-header-action--secondary:hover, +.wire-public-header-action--ghost:hover { + color: var(--wire-color-primary); + background: var(--wire-color-surface-2); +} .wire-auth-field__input { width: 100%; min-height: 3.5rem; diff --git a/packages/uploader/package.json b/packages/uploader/package.json index 2780a19b..ab5dc3cb 100644 --- a/packages/uploader/package.json +++ b/packages/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/validation/package.json b/packages/validation/package.json index 9329e84e..a6fedc91 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.73", + "version": "0.2.74", "private": true, "type": "module", "main": "src/index.ts", diff --git a/update-package-versions.mjs b/update-package-versions.mjs index 212dec42..13e7126e 100644 --- a/update-package-versions.mjs +++ b/update-package-versions.mjs @@ -1,7 +1,7 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -const version = "0.2.73"; +const version = "0.2.74"; const dependencyGroups = [ "dependencies",