Compare commits

...
56 Commits
Author SHA1 Message Date
Clintchiz 84e16ebc41 chore(release): publish ui 0.8.56
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:36:04 +05:30
Clintchiz 7993a303ac fix(ui): preserve viewport anchoring for centered navbar
Quality / quality (ubuntu-latest) (push) Failing after 9m57s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:35:43 +05:30
Clintchiz a99290a216 chore(release): publish ui 0.8.55
Quality / quality (ubuntu-latest) (push) Failing after 10m16s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:34:17 +05:30
Clintchiz c648efc240 fix(ui): contain long mega menu labels
Quality / quality (ubuntu-latest) (push) Failing after 10m41s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:33:59 +05:30
Clintchiz decbee2fd1 chore(release): publish ui 0.8.54
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:31:38 +05:30
Clintchiz e62e7f5d27 fix(ui): center desktop navbar menus
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 19:31:17 +05:30
Clintchiz b2c74a3a29 chore(release): publish ui 0.8.53
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:59:43 +05:30
Clintchiz fe86570f45 fix(ui): render dynamic tab panels and ids
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:59:10 +05:30
Clintchiz bc1211ea8b chore(release): publish ui 0.8.52
Quality / quality (ubuntu-latest) (push) Failing after 10m0s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:37:55 +05:30
Clintchiz d2e24eb089 feat(ui): refine timeline and blockquote layouts
Quality / quality (ubuntu-latest) (push) Failing after 10m2s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:37:29 +05:30
Clintchiz e71885d51c chore(release): publish ui 0.8.51
Quality / quality (ubuntu-latest) (push) Failing after 9m56s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:31:26 +05:30
Clintchiz 138b5d0950 fix(ui): prevent full bleed hero overflow
Quality / quality (ubuntu-latest) (push) Failing after 10m42s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 17:30:34 +05:30
Clintchiz 7e1eb899b3 fix(ui): align stats surface within max width
Quality / quality (ubuntu-latest) (push) Failing after 9m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 16:54:23 +05:30
Clintchiz 3f6612cfe7 fix(ui): honor stats bar desktop gutters
Quality / quality (ubuntu-latest) (push) Failing after 9m51s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 16:50:10 +05:30
Clintchiz 510b3ba777 fix(ui): keep breadcrumb separators visible
Quality / quality (ubuntu-latest) (push) Failing after 10m21s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 16:34:44 +05:30
Clintchiz 1ff2c3339c fix(ui): render active breadcrumb pages
Quality / quality (ubuntu-latest) (push) Failing after 10m0s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 16:21:53 +05:30
Clintchiz 027873459d fix(ui): collapse empty page header slots
Quality / quality (ubuntu-latest) (push) Failing after 9m57s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 15:56:57 +05:30
Clintchiz d0bccfe37e fix(csr): suppress inactive theme token warnings
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 15:48:48 +05:30
Clintchiz 120889060a fix(ui): normalize form field label sizing
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 15:25:24 +05:30
Clintchiz 26ec18c24b chore(editor): release vscode extension 0.8.11
Quality / quality (ubuntu-latest) (push) Failing after 9m51s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 15:22:15 +05:30
Clintchiz 7658c5d499 fix(editor): type nested attribute expressions
Quality / quality (ubuntu-latest) (push) Failing after 9m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 15:16:38 +05:30
Clintchiz 3cb6ba1233 fix(ui): render navbar brand prop outside slot
Quality / quality (ubuntu-latest) (push) Failing after 10m32s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 14:00:46 +05:30
Clintchiz 64db7da08d fix(ui): refine responsive catalog navigation
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-25 13:46:19 +05:30
Clintchiz 9f6552ea62 fix(ui): separate catalog icon masks from tiles
Quality / quality (ubuntu-latest) (push) Failing after 9m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 23:30:33 +05:30
Clintchiz 5bc391e74f fix(ui): inherit catalog icon contrast
Quality / quality (ubuntu-latest) (push) Failing after 22s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 23:23:43 +05:30
Clintchiz 3327ec0d37 refactor(ui): clarify catalog navigation hierarchy
Quality / quality (ubuntu-latest) (push) Failing after 10m18s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 23:16:22 +05:30
Clintchiz ceb865f4f1 fix(ui): prevent navigation surface overflow
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 23:08:00 +05:30
Clintchiz 45309b3f72 refactor(ui): compact mega menu layouts
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 23:06:01 +05:30
Clintchiz 8f3a13c854 fix(ui): render only supplied mega menu metadata
Quality / quality (ubuntu-latest) (push) Failing after 9m55s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:57:13 +05:30
Clintchiz eca4447dcf fix(ui): stabilize catalog mega menu interaction
Quality / quality (ubuntu-latest) (push) Failing after 9m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:50:41 +05:30
Clintchiz 904bca21b2 refactor(ui): refine catalog mega menu navigation
Quality / quality (ubuntu-latest) (push) Failing after 10m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:40:58 +05:30
Clintchiz b98d417185 fix(ui): eliminate mega menu flash and lag
Quality / quality (ubuntu-latest) (push) Failing after 9m55s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:33:02 +05:30
Clintchiz 7d737ec0a0 fix(ui): make catalog mega menus interactive
Quality / quality (ubuntu-latest) (push) Failing after 6m15s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:18:30 +05:30
Clintchiz f5538ed621 fix(ui): keep mega menu open across hover gap
Quality / quality (ubuntu-latest) (push) Failing after 9m51s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 22:08:46 +05:30
Clintchiz d2d830e7e2 fix(ui): pin navbar mega panels to viewport
Quality / quality (ubuntu-latest) (push) Failing after 12m5s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:56:27 +05:30
Clintchiz 0618782355 fix(ui): make mega menus work under strict CSP
Quality / quality (ubuntu-latest) (push) Failing after 9m55s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:52:25 +05:30
Clintchiz 9067e68f69 fix(ui): import navbar mega menu dependency
Quality / quality (ubuntu-latest) (push) Failing after 10m39s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:37:26 +05:30
Clintchiz 9d64ec60c4 feat(ui): add typed navbar menu items
Quality / quality (ubuntu-latest) (push) Failing after 11m12s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:36:46 +05:30
Clintchiz fc1eea3939 chore(editor): refresh language server bundle hash
Quality / quality (ubuntu-latest) (push) Failing after 10m8s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:25:53 +05:30
Clintchiz 4ec5dad60a fix(formatter): expand large structured prop defaults
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:25:07 +05:30
Clintchiz a1a2947910 feat(ui): expand mega menu layout system
Quality / quality (ubuntu-latest) (push) Failing after 11m2s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 21:15:50 +05:30
Clintchiz 270040fb5f feat(ui): add in-flow floating navbar
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 19:47:47 +05:30
Clintchiz f9e285b182 feat(ui): own responsive marketing layout APIs
Quality / quality (ubuntu-latest) (push) Failing after 10m42s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 19:44:26 +05:30
Clintchiz 3b402b4e36 fix(ui): collapse empty navbar topbar and add CTA spacing
Quality / quality (ubuntu-latest) (push) Failing after 11m48s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 18:28:39 +05:30
Clintchiz fa535575c5 fix(cli): resolve package updates on Windows
Quality / quality (ubuntu-latest) (push) Failing after 9m56s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 17:23:35 +05:30
Clintchiz 46ef4b775b fix(ui): harden public page components
Quality / quality (ubuntu-latest) (push) Failing after 10m19s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 17:13:14 +05:30
Clintchiz 7a36c1905f fix(cli): handle version flags before workspace discovery
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 15:03:38 +05:30
Clintchiz 1a8c75a958 chore: refresh editor language server bundle 2026-08-24 14:58:03 +05:30
Clintchiz b3c93e9b18 test: harden package boundaries and audit budgets
Quality / quality (ubuntu-latest) (push) Failing after 9m52s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 12:05:20 +05:30
Clintchiz 613ff7ae5b fix: invoke npm reliably from Bun on Windows
Quality / quality (ubuntu-latest) (push) Failing after 9m51s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 11:37:06 +05:30
Clintchiz e372ae571a chore: harden release checks and package coverage
Quality / quality (ubuntu-latest) (push) Failing after 9m57s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-24 11:36:13 +05:30
Clintchiz 354082ebc3 fix: recognize globally registered components
Quality / quality (ubuntu-latest) (push) Failing after 10m22s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 23:03:45 +05:30
Clintchiz aa21bc4a18 chore: release cli with fixed dev runtime
Quality / quality (ubuntu-latest) (push) Failing after 11m3s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 22:58:09 +05:30
Clintchiz 1be482caab fix: defer reactive props in branch pre-rendering
Quality / quality (ubuntu-latest) (push) Failing after 10m4s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 22:51:25 +05:30
Clintchiz 2d1cda0eab chore: restore production release gate 2026-08-23 22:35:11 +05:30
Clintchiz d26403fce2 chore: update public API baseline 2026-08-23 22:24:51 +05:30
103 changed files with 3657 additions and 543 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2 - uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.14 bun-version: 1.4.0
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 24 node-version: 24
+98 -95
View File
@@ -9,7 +9,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@types/bun": "^1.3.14", "@types/bun": "^1.4.0",
"@types/react": "^19.2.18", "@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4", "@types/react-dom": "^19.2.4",
"eslint": "^10.8.1", "eslint": "^10.8.1",
@@ -133,7 +133,7 @@
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@types/bun": "^1.3.14", "@types/bun": "^1.3.14",
"@wrnexus/cli": "^0.8.35", "@wrnexus/cli": "^0.8.36",
"eslint": "^10.8.1", "eslint": "^10.8.1",
"prettier": "^3.9.6", "prettier": "^3.9.6",
"typescript": "^6.0.3", "typescript": "^6.0.3",
@@ -145,27 +145,27 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@wrnexus/ai": "^0.8.9", "@wrnexus/ai": "^0.8.9",
"@wrnexus/auth": "^0.8.11", "@wrnexus/auth": "^0.8.12",
"@wrnexus/authz": "^0.8.9", "@wrnexus/authz": "^0.8.9",
"@wrnexus/captcha": "^0.8.10", "@wrnexus/captcha": "^0.8.11",
"@wrnexus/core": "^0.8.9", "@wrnexus/core": "^0.8.9",
"@wrnexus/csr": "^0.8.21", "@wrnexus/csr": "^0.8.21",
"@wrnexus/db": "^0.8.14", "@wrnexus/db": "^0.8.15",
"@wrnexus/dev-server": "^0.8.32", "@wrnexus/dev-server": "^0.8.33",
"@wrnexus/encryption": "^0.8.8", "@wrnexus/encryption": "^0.8.9",
"@wrnexus/helpers": "^0.8.8", "@wrnexus/helpers": "^0.8.8",
"@wrnexus/i18n": "^0.8.10", "@wrnexus/i18n": "^0.8.11",
"@wrnexus/image": "^0.8.9", "@wrnexus/image": "^0.8.10",
"@wrnexus/jwt": "^0.8.8", "@wrnexus/jwt": "^0.8.9",
"@wrnexus/observability": "^0.8.8", "@wrnexus/observability": "^0.8.8",
"@wrnexus/realtime": "^0.8.9", "@wrnexus/realtime": "^0.8.10",
"@wrnexus/security": "^0.8.8", "@wrnexus/security": "^0.8.8",
"@wrnexus/store": "^0.8.8", "@wrnexus/store": "^0.8.8",
"@wrnexus/styles": "^0.8.15", "@wrnexus/styles": "^0.8.15",
"@wrnexus/tracking": "^0.8.8", "@wrnexus/tracking": "^0.8.8",
"@wrnexus/ui": "^0.8.19", "@wrnexus/ui": "^0.8.19",
"@wrnexus/uploader": "^0.8.9", "@wrnexus/uploader": "^0.8.10",
"@wrnexus/validation": "^0.8.9", "@wrnexus/validation": "^0.8.10",
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
@@ -173,7 +173,7 @@
"@iconify/tailwind4": "^1.2.3", "@iconify/tailwind4": "^1.2.3",
"@tailwindcss/cli": "^4.3.3", "@tailwindcss/cli": "^4.3.3",
"@types/bun": "^1.3.14", "@types/bun": "^1.3.14",
"@wrnexus/cli": "^0.8.35", "@wrnexus/cli": "^0.8.36",
"eslint": "^10.8.1", "eslint": "^10.8.1",
"prettier": "^3.9.6", "prettier": "^3.9.6",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
@@ -186,27 +186,27 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@wrnexus/ai": "^0.8.9", "@wrnexus/ai": "^0.8.9",
"@wrnexus/auth": "^0.8.11", "@wrnexus/auth": "^0.8.12",
"@wrnexus/authz": "^0.8.9", "@wrnexus/authz": "^0.8.9",
"@wrnexus/captcha": "^0.8.10", "@wrnexus/captcha": "^0.8.11",
"@wrnexus/core": "^0.8.9", "@wrnexus/core": "^0.8.9",
"@wrnexus/csr": "^0.8.21", "@wrnexus/csr": "^0.8.21",
"@wrnexus/db": "^0.8.14", "@wrnexus/db": "^0.8.15",
"@wrnexus/dev-server": "^0.8.32", "@wrnexus/dev-server": "^0.8.33",
"@wrnexus/encryption": "^0.8.8", "@wrnexus/encryption": "^0.8.9",
"@wrnexus/helpers": "^0.8.8", "@wrnexus/helpers": "^0.8.8",
"@wrnexus/i18n": "^0.8.10", "@wrnexus/i18n": "^0.8.11",
"@wrnexus/image": "^0.8.9", "@wrnexus/image": "^0.8.10",
"@wrnexus/jwt": "^0.8.8", "@wrnexus/jwt": "^0.8.9",
"@wrnexus/observability": "^0.8.8", "@wrnexus/observability": "^0.8.8",
"@wrnexus/realtime": "^0.8.9", "@wrnexus/realtime": "^0.8.10",
"@wrnexus/security": "^0.8.8", "@wrnexus/security": "^0.8.8",
"@wrnexus/store": "^0.8.8", "@wrnexus/store": "^0.8.8",
"@wrnexus/styles": "^0.8.15", "@wrnexus/styles": "^0.8.15",
"@wrnexus/tracking": "^0.8.8", "@wrnexus/tracking": "^0.8.8",
"@wrnexus/ui": "^0.8.19", "@wrnexus/ui": "^0.8.19",
"@wrnexus/uploader": "^0.8.9", "@wrnexus/uploader": "^0.8.10",
"@wrnexus/validation": "^0.8.9", "@wrnexus/validation": "^0.8.10",
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
@@ -214,7 +214,7 @@
"@iconify/tailwind4": "^1.2.3", "@iconify/tailwind4": "^1.2.3",
"@tailwindcss/cli": "^4.3.3", "@tailwindcss/cli": "^4.3.3",
"@types/bun": "^1.3.14", "@types/bun": "^1.3.14",
"@wrnexus/cli": "^0.8.35", "@wrnexus/cli": "^0.8.36",
"eslint": "^10.8.1", "eslint": "^10.8.1",
"prettier": "^3.9.6", "prettier": "^3.9.6",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
@@ -277,7 +277,7 @@
}, },
"packages/captcha": { "packages/captcha": {
"name": "@wrnexus/captcha", "name": "@wrnexus/captcha",
"version": "0.8.11", "version": "0.8.14",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
"@wrnexus/plugin": "workspace:*", "@wrnexus/plugin": "workspace:*",
@@ -292,7 +292,7 @@
}, },
"packages/cli": { "packages/cli": {
"name": "@wrnexus/cli", "name": "@wrnexus/cli",
"version": "0.8.60", "version": "0.8.64",
"bin": { "bin": {
"wrnexus": "src/index.ts", "wrnexus": "src/index.ts",
}, },
@@ -320,7 +320,7 @@
}, },
"packages/compiler": { "packages/compiler": {
"name": "@wrnexus/compiler", "name": "@wrnexus/compiler",
"version": "0.8.21", "version": "0.8.22",
"dependencies": { "dependencies": {
"@wrnexus/csr": "workspace:*", "@wrnexus/csr": "workspace:*",
"@wrnexus/store": "workspace:*", "@wrnexus/store": "workspace:*",
@@ -342,7 +342,7 @@
}, },
"packages/csr": { "packages/csr": {
"name": "@wrnexus/csr", "name": "@wrnexus/csr",
"version": "0.8.33", "version": "0.8.35",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
}, },
@@ -357,7 +357,7 @@
}, },
"packages/dev-server": { "packages/dev-server": {
"name": "@wrnexus/dev-server", "name": "@wrnexus/dev-server",
"version": "0.8.52", "version": "0.8.56",
"dependencies": { "dependencies": {
"@wrnexus/authz": "workspace:*", "@wrnexus/authz": "workspace:*",
"@wrnexus/cache": "workspace:*", "@wrnexus/cache": "workspace:*",
@@ -372,6 +372,7 @@
"@wrnexus/pubsub": "workspace:*", "@wrnexus/pubsub": "workspace:*",
"@wrnexus/pwa": "workspace:*", "@wrnexus/pwa": "workspace:*",
"@wrnexus/queue": "workspace:*", "@wrnexus/queue": "workspace:*",
"@wrnexus/react": "workspace:*",
"@wrnexus/router": "workspace:*", "@wrnexus/router": "workspace:*",
"@wrnexus/rpc": "workspace:*", "@wrnexus/rpc": "workspace:*",
"@wrnexus/security": "workspace:*", "@wrnexus/security": "workspace:*",
@@ -385,7 +386,7 @@
}, },
"packages/dev-toolbar": { "packages/dev-toolbar": {
"name": "@wrnexus/dev-toolbar", "name": "@wrnexus/dev-toolbar",
"version": "0.8.13", "version": "0.8.16",
"devDependencies": { "devDependencies": {
"@types/bun": "^1.3.14", "@types/bun": "^1.3.14",
"typescript": "^6.0.3", "typescript": "^6.0.3",
@@ -654,7 +655,7 @@
}, },
"packages/store": { "packages/store": {
"name": "@wrnexus/store", "name": "@wrnexus/store",
"version": "0.8.9", "version": "0.8.12",
}, },
"packages/styles": { "packages/styles": {
"name": "@wrnexus/styles", "name": "@wrnexus/styles",
@@ -667,7 +668,7 @@
}, },
"packages/syntax": { "packages/syntax": {
"name": "@wrnexus/syntax", "name": "@wrnexus/syntax",
"version": "0.8.13", "version": "0.8.18",
}, },
"packages/test": { "packages/test": {
"name": "@wrnexus/test", "name": "@wrnexus/test",
@@ -679,7 +680,7 @@
}, },
"packages/typecheck": { "packages/typecheck": {
"name": "@wrnexus/typecheck", "name": "@wrnexus/typecheck",
"version": "0.8.13", "version": "0.8.16",
"dependencies": { "dependencies": {
"@wrnexus/syntax": "workspace:*", "@wrnexus/syntax": "workspace:*",
"typescript": "^6.0.3", "typescript": "^6.0.3",
@@ -687,14 +688,14 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@wrnexus/ui", "name": "@wrnexus/ui",
"version": "0.8.23", "version": "0.8.56",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
}, },
}, },
"packages/uploader": { "packages/uploader": {
"name": "@wrnexus/uploader", "name": "@wrnexus/uploader",
"version": "0.8.10", "version": "0.8.13",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
"@wrnexus/plugin": "workspace:*", "@wrnexus/plugin": "workspace:*",
@@ -795,7 +796,7 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
@@ -821,7 +822,7 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@iconify-json/lucide": ["@iconify-json/lucide@1.2.123", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-0CozmpKXEOEEhltrfT2zt+/t1hez67Y+hM2VJWoIcBKdBrb83VYuKf+b5A0QU9HfQm+RNn2GjFWlTOwi+Ss6IA=="], "@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="],
"@iconify/tailwind4": ["@iconify/tailwind4@1.2.3", "", { "dependencies": { "@iconify/tools": "^5.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA=="], "@iconify/tailwind4": ["@iconify/tailwind4@1.2.3", "", { "dependencies": { "@iconify/tools": "^5.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA=="],
@@ -841,6 +842,8 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="],
"@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="], "@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
@@ -871,79 +874,79 @@
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA=="], "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "@peculiar/asn1-x509-attr": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-cben7oxmQsUGZqotus7yt0srYdncOT6RNWcTQ77T2RFOXejYVYkXadrfePdRcrVpO9K95IRLKKglG2k38jKXuw=="],
"@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg=="], "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-xd4YN4vpRjkDAQWVfZZkeu12IEND7DOpkqaHSIHxZl1uggUNa9Ju0QxY2jHvDAS9pP0zhRBytg8ifsnGo3V0jw=="],
"@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ=="], "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-JJXefFshRAuVAjWQo/39bkg1ywc1VaiO44S8RRC+Ykvf/u2KDmYffoDb0ZBPCR5uJy4AGKQhl8mX+Q8ShcWaXQ=="],
"@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-rsa": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg=="], "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.9.4", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.4", "@peculiar/asn1-pkcs8": "^2.9.4", "@peculiar/asn1-rsa": "^2.9.4", "@peculiar/asn1-schema": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-khuGzHTzNzk4GDlIBEILyIs6Lce0yn0ZBdoI9v93kmNncfZRhD+AQ5ODFqdhvoE8cMJF/JMTQ8yA+t1D14kqCw=="],
"@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA=="], "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-duRdotlUx9eDZe6QrQpQKl61RbWykCHBCkKayP8V8XdEFwlKHZ8qGGDMyS6Pye7OX7nLFttTTpRkJeet78ckwQ=="],
"@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pfx": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ=="], "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.9.4", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.4", "@peculiar/asn1-pfx": "^2.9.4", "@peculiar/asn1-pkcs8": "^2.9.4", "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "@peculiar/asn1-x509-attr": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-kaL4cNxBpdQE2dKlyZBqz4ygCrwffO+8wfoxTEqM1Z8RadvCeELBRzcv0dzM8aY9azHMwODO5nxU65zXmhToOQ=="],
"@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg=="], "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-pZ96eD1PptovcWQ/GSmuNFXd/7EQJNlKfDaNCyE2rx3W0v6QFelkzquVqRSRyyDXXCYD69ZXJDzZ8GhIiQzKoA=="],
"@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.8.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q=="], "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.9.4", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-GjzePcT9Iw8NzeOPf73iNS9xM+TBhd/FilAfP+RQGkTMQJTVWtytN3JHJACCjf/ABNau5S7mS3g+DcuxmRgYEg=="],
"@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg=="], "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-CxhBo/RdEbMMob7T31ZdQjGuoyRFLVwrDzTn25bihzBasRg9kRm/0IxIPvhgQtcK/9dNcO1XQL2fuPugwELL0Q=="],
"@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA=="], "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ehQXbpQaQYycgu8OrvigwSPTFfVRcu0ECNYCWw+yzBp02Lw5paRqzzhUpfOgO2K38+WfFZuEz/0RPtam5g0OMg=="],
"@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="],
"@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="], "@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.5", "", { "os": "android", "cpu": "arm" }, "sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.5", "", { "os": "android", "cpu": "arm64" }, "sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="], "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="], "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="], "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="], "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="], "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.5", "", { "os": "linux", "cpu": "arm" }, "sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="], "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.5", "", { "os": "linux", "cpu": "arm" }, "sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="], "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="], "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw=="],
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="], "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w=="],
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="], "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw=="],
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="], "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ=="],
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="], "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="], "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="], "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.5", "", { "os": "linux", "cpu": "none" }, "sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="], "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="], "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.5", "", { "os": "linux", "cpu": "x64" }, "sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="], "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.5", "", { "os": "linux", "cpu": "x64" }, "sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw=="],
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="], "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw=="],
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="], "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.5", "", { "os": "none", "cpu": "arm64" }, "sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="], "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="], "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA=="],
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="], "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.5", "", { "os": "win32", "cpu": "x64" }, "sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.5", "", { "os": "win32", "cpu": "x64" }, "sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg=="],
"@tailwindcss/cli": ["@tailwindcss/cli@4.3.3", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "enhanced-resolve": "^5.24.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.3" }, "bin": { "tailwindcss": "./dist/index.mjs" } }, "sha512-ZvS/n1ZHOBKcVlhkt8l5NNr1EDXk1NboYO5CYDOs6NUmvT9z6bzkwsosaJftY57T/3gWNzWMJzIXLodZC8ssdw=="], "@tailwindcss/cli": ["@tailwindcss/cli@4.3.3", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "enhanced-resolve": "^5.24.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.3" }, "bin": { "tailwindcss": "./dist/index.mjs" } }, "sha512-ZvS/n1ZHOBKcVlhkt8l5NNr1EDXk1NboYO5CYDOs6NUmvT9z6bzkwsosaJftY57T/3gWNzWMJzIXLodZC8ssdw=="],
@@ -975,7 +978,7 @@
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="], "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="],
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="],
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
@@ -983,11 +986,11 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@26.0.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw=="], "@types/node": ["@types/node@26.2.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg=="],
"@types/react": ["@types/react@19.2.18", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w=="], "@types/react": ["@types/react@19.2.18", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w=="],
"@types/react-dom": ["@types/react-dom@19.2.4", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw=="], "@types/react-dom": ["@types/react-dom@19.2.5", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg=="],
"@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
@@ -1119,7 +1122,7 @@
"@wrnexus/validation": ["@wrnexus/validation@workspace:packages/validation"], "@wrnexus/validation": ["@wrnexus/validation@workspace:packages/validation"],
"acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
@@ -1145,7 +1148,7 @@
"buffer-image-size": ["buffer-image-size@0.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ=="], "buffer-image-size": ["buffer-image-size@0.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ=="],
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="],
"bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="],
@@ -1177,7 +1180,7 @@
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
@@ -1199,7 +1202,7 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@10.8.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ=="], "eslint": ["eslint@10.9.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-5KeEOJZBfEVA47boFiBsf+6MmmJpffM7qEBg4pLla2e4nlKgdKlqCW0oSLOGsT8Wl5uCGJptLV1bkaiShj90Gw=="],
"eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], "eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
@@ -1235,7 +1238,7 @@
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], "flatted": ["flatted@3.4.4", "", {}, "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
@@ -1243,7 +1246,7 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"happy-dom": ["happy-dom@20.11.2", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-7MB+bJLkxu3SowAfBJbjW+c55kNz5tkR45gu2qzrxznezhLeN5YIlJbwUgSzlGc+qWoZ8Ykg71H5ezz69xixrw=="], "happy-dom": ["happy-dom@20.11.6", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-Hldbg8AdAa5a5oDcZpjqnGitp7JB0hqWmfv/8qr+kft4vzSD8BHsbdRfzYvL/0QcbKcURC/yyoygbeDQarPvYg=="],
"i18n-showcase": ["i18n-showcase@workspace:examples/i18n-showcase"], "i18n-showcase": ["i18n-showcase@workspace:examples/i18n-showcase"],
@@ -1315,7 +1318,7 @@
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
"mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="], "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
@@ -1351,7 +1354,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
"pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
@@ -1369,7 +1372,7 @@
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
"pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], "pvutils": ["pvutils@1.2.0", "", {}, "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg=="],
"react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], "react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
@@ -1381,9 +1384,9 @@
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
"rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="], "rollup": ["rollup@4.62.5", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.62.5", "@rollup/rollup-android-arm64": "4.62.5", "@rollup/rollup-darwin-arm64": "4.62.5", "@rollup/rollup-darwin-x64": "4.62.5", "@rollup/rollup-freebsd-arm64": "4.62.5", "@rollup/rollup-freebsd-x64": "4.62.5", "@rollup/rollup-linux-arm-gnueabihf": "4.62.5", "@rollup/rollup-linux-arm-musleabihf": "4.62.5", "@rollup/rollup-linux-arm64-gnu": "4.62.5", "@rollup/rollup-linux-arm64-musl": "4.62.5", "@rollup/rollup-linux-loong64-gnu": "4.62.5", "@rollup/rollup-linux-loong64-musl": "4.62.5", "@rollup/rollup-linux-ppc64-gnu": "4.62.5", "@rollup/rollup-linux-ppc64-musl": "4.62.5", "@rollup/rollup-linux-riscv64-gnu": "4.62.5", "@rollup/rollup-linux-riscv64-musl": "4.62.5", "@rollup/rollup-linux-s390x-gnu": "4.62.5", "@rollup/rollup-linux-x64-gnu": "4.62.5", "@rollup/rollup-linux-x64-musl": "4.62.5", "@rollup/rollup-openbsd-x64": "4.62.5", "@rollup/rollup-openharmony-arm64": "4.62.5", "@rollup/rollup-win32-arm64-msvc": "4.62.5", "@rollup/rollup-win32-ia32-msvc": "4.62.5", "@rollup/rollup-win32-x64-gnu": "4.62.5", "@rollup/rollup-win32-x64-msvc": "4.62.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], "sax": ["sax@1.6.1", "", {}, "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
@@ -1459,27 +1462,27 @@
"wrnexus-crm-example": ["wrnexus-crm-example@workspace:examples/crm-app"], "wrnexus-crm-example": ["wrnexus-crm-example@workspace:examples/crm-app"],
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"@antfu/install-pkg/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], "@antfu/install-pkg/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="],
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
+182 -2
View File
@@ -968,6 +968,7 @@
}, },
"@wrnexus/cli": { "@wrnexus/cli": {
".": [], ".": [],
"./test-setup": [],
"./workspace": [ "./workspace": [
"ResolvedWorkspaceApp", "ResolvedWorkspaceApp",
"ResolvedWorkspaceConfig", "ResolvedWorkspaceConfig",
@@ -1352,6 +1353,8 @@
"ActionClientError", "ActionClientError",
"ActionClientOptions", "ActionClientOptions",
"ActionResult", "ActionResult",
"ApiClientOptions",
"ApiRequest",
"ClientModuleScope", "ClientModuleScope",
"HydrationScopeApi", "HydrationScopeApi",
"NAV_RUNTIME", "NAV_RUNTIME",
@@ -1365,6 +1368,7 @@
"callServerFunction", "callServerFunction",
"collectRefs", "collectRefs",
"createActionClient", "createActionClient",
"createApiClient",
"createOutputProxy", "createOutputProxy",
"createServerProxy", "createServerProxy",
"getActionRuntime", "getActionRuntime",
@@ -1375,11 +1379,13 @@
"invalidateClientModule", "invalidateClientModule",
"invokeOutput", "invokeOutput",
"loadClientFunctions", "loadClientFunctions",
"registerOutputHandler" "registerOutputHandler",
"useFetch"
] ]
}, },
"@wrnexus/db": { "@wrnexus/db": {
".": [ ".": [
"AddSeedOptions",
"BaseType", "BaseType",
"Column", "Column",
"ColumnDef", "ColumnDef",
@@ -1390,6 +1396,7 @@
"Dialect", "Dialect",
"Driver", "Driver",
"ExecResult", "ExecResult",
"LedgerOptions",
"Migration", "Migration",
"MigrationRunOptions", "MigrationRunOptions",
"MigrationSafetyIssue", "MigrationSafetyIssue",
@@ -1406,7 +1413,13 @@
"RelationOptions", "RelationOptions",
"Repository", "Repository",
"Row", "Row",
"SeedDatabase",
"SeedRow",
"SeedStep",
"SeedUserAccount",
"TxHandle", "TxHandle",
"UpsertSeedOptions",
"addSeedData",
"analyzeMigrationSafety", "analyzeMigrationSafety",
"analyzeMigrations", "analyzeMigrations",
"appliedMigrations", "appliedMigrations",
@@ -1421,6 +1434,8 @@
"cursorPaginate", "cursorPaginate",
"databaseHealth", "databaseHealth",
"databaseNames", "databaseNames",
"defineLedger",
"defineSeed",
"exists", "exists",
"firstOrThrow", "firstOrThrow",
"generateQueriesFile", "generateQueriesFile",
@@ -1438,15 +1453,20 @@
"queryOperation", "queryOperation",
"registerDb", "registerDb",
"registerLazyDb", "registerLazyDb",
"removeSeedData",
"resetDbPerformanceSnapshot", "resetDbPerformanceSnapshot",
"retryTransaction", "retryTransaction",
"rollback", "rollback",
"runSeedQuery",
"scaffoldMigration", "scaffoldMigration",
"seedIfMissing",
"seedUsers",
"setDb", "setDb",
"softDeleteClause", "softDeleteClause",
"status", "status",
"table", "table",
"tenantScope", "tenantScope",
"upsertSeedData",
"v", "v",
"withTransaction" "withTransaction"
], ],
@@ -1647,6 +1667,7 @@
"EncryptionKey", "EncryptionKey",
"EncryptionKeyring", "EncryptionKeyring",
"ReplayStore", "ReplayStore",
"SecretResourceOptions",
"createEncryptedRequest", "createEncryptedRequest",
"createKeyring", "createKeyring",
"createMemoryReplayStore", "createMemoryReplayStore",
@@ -1654,6 +1675,7 @@
"decryptEncryptedResponse", "decryptEncryptedResponse",
"decryptHttpBody", "decryptHttpBody",
"decryptRequest", "decryptRequest",
"defineSecretResource",
"deriveKey", "deriveKey",
"encrypt", "encrypt",
"encryptHttpBody", "encryptHttpBody",
@@ -1687,26 +1709,39 @@
"@wrnexus/helpers": { "@wrnexus/helpers": {
".": [ ".": [
"AllowedHosts", "AllowedHosts",
"ApiHandler",
"HttpError",
"LoginRedirectOptions", "LoginRedirectOptions",
"OriginalRequestOptions", "OriginalRequestOptions",
"RequestContext", "RequestContext",
"ResourceDefinition",
"ResourceHandlers",
"RetryOptions", "RetryOptions",
"appOrigin", "appOrigin",
"appUrl", "appUrl",
"authorized",
"backoffDelay", "backoffDelay",
"clamp", "clamp",
"currentAppName", "currentAppName",
"currentAppOrigin", "currentAppOrigin",
"defineApiRoute",
"defineResource",
"getOriginalRequestMethod", "getOriginalRequestMethod",
"getOriginalRequestOrigin", "getOriginalRequestOrigin",
"getOriginalRequestPath", "getOriginalRequestPath",
"getOriginalRequestUrl", "getOriginalRequestUrl",
"json",
"once", "once",
"redirectToLogin", "redirectToLogin",
"requireJson",
"requireParam",
"requirePermission",
"requireUser",
"retry", "retry",
"safeJsonParse", "safeJsonParse",
"sleep", "sleep",
"stableStringify", "stableStringify",
"subjectId",
"withTimeout", "withTimeout",
"workspaceAppOrigins", "workspaceAppOrigins",
"workspaceRootDomain" "workspaceRootDomain"
@@ -1902,6 +1937,25 @@
"isApiReferenceAt" "isApiReferenceAt"
] ]
}, },
"@wrnexus/mail": {
".": [
"MailDriver",
"MailMessage",
"MailOptions",
"MailSandboxDecision",
"MailTemplate",
"SealedMailCredentialsOptions",
"SealedMailDriverOptions",
"defineMail",
"defineMailTemplate",
"defineSealedMailCredentials",
"interpolateTemplate",
"mailSandboxDecision",
"queuedMail",
"sealedMailDriver",
"storedMailTemplate"
]
},
"@wrnexus/mcp": { "@wrnexus/mcp": {
".": [ ".": [
"McpServer", "McpServer",
@@ -1913,6 +1967,20 @@
"runMcpStdio" "runMcpStdio"
] ]
}, },
"@wrnexus/metering": {
".": [
"EntitlementPlan",
"EntitlementsOptions",
"MeterKind",
"MeterOptions",
"MeterPack",
"MeterResult",
"MeterStore",
"defineEntitlements",
"defineMeter",
"definePacks"
]
},
"@wrnexus/mobile": { "@wrnexus/mobile": {
".": [ ".": [
"CapacitorBridge", "CapacitorBridge",
@@ -2113,6 +2181,70 @@
"traceMiddleware" "traceMiddleware"
] ]
}, },
"@wrnexus/payment": {
".": [
"CreateIntentInput",
"CustomerInput",
"GatewayCapabilities",
"GatewayContractFixture",
"GatewayContractReport",
"GatewayIntent",
"GatewayMethod",
"GatewayRefund",
"GatewaySupport",
"InitializePaymentInput",
"Money",
"PayPalConfig",
"PaymentEvent",
"PaymentGateway",
"PaymentIntent",
"PaymentMethod",
"PaymentOptions",
"PaymentRefund",
"PaymentResult",
"PaymentService",
"PaymentStatus",
"PaymentStore",
"RazorpayConfig",
"RefundInput",
"SandboxOptions",
"StripeConfig",
"WebhookEvent",
"configurePayment",
"databasePaymentStore",
"defineGateway",
"definePayment",
"derivePaymentStatus",
"exerciseGatewayContract",
"getPaymentService",
"memoryPaymentStore",
"paymentPlugin",
"paypalGateway",
"razorpayGateway",
"sandboxGateway",
"signSandboxWebhook",
"stripeGateway",
"verifyGatewayContract"
],
"./adapters": [
"PayPalConfig",
"RazorpayConfig",
"SandboxOptions",
"StripeConfig",
"paypalGateway",
"razorpayGateway",
"sandboxGateway",
"signSandboxWebhook",
"stripeGateway"
],
"./plugin": [
"default",
"paymentPlugin"
],
"./runtime-webhook": [
"POST"
]
},
"@wrnexus/playground": { "@wrnexus/playground": {
".": [ ".": [
"PlaygroundCompilation", "PlaygroundCompilation",
@@ -2277,21 +2409,41 @@
"@wrnexus/queue": { "@wrnexus/queue": {
".": [ ".": [
"AddOptions", "AddOptions",
"DefineQueueOptions",
"DefinedJob",
"DefinedJobOptions",
"DefinedJobs",
"DefinedQueue",
"DurableQueue", "DurableQueue",
"DurableQueueOptions", "DurableQueueOptions",
"Job", "Job",
"JobContext", "JobContext",
"JobDefinition", "JobDefinition",
"JobHandler", "JobHandler",
"JobStateMachineOptions",
"JobStatus",
"OutboxEntry",
"OutboxWriter",
"POSTGRES_QUEUE_SCHEMA", "POSTGRES_QUEUE_SCHEMA",
"Queue", "Queue",
"QueueAdminOptions",
"QueueDashboardSnapshot", "QueueDashboardSnapshot",
"QueueEvent",
"QueueEventType",
"QueueHealth",
"QueueJobDefinitions",
"QueueJobRecord",
"QueueJobState",
"QueueOptions", "QueueOptions",
"QueueScheduler", "QueueScheduler",
"QueueStorage",
"QueueStorageConfig",
"QueueStore", "QueueStore",
"RedisQueueClient", "RedisQueueClient",
"SagaStep",
"ScheduledJob", "ScheduledJob",
"SqlQueueClient", "SqlQueueClient",
"SqliteQueueClient",
"SubjectJob", "SubjectJob",
"SubjectQueue", "SubjectQueue",
"WorkerDefinition", "WorkerDefinition",
@@ -2303,24 +2455,46 @@
"WorkflowStep", "WorkflowStep",
"WorkflowStore", "WorkflowStore",
"addBatch", "addBatch",
"configureQueueStorage",
"configuredQueueStore",
"createDurableQueue", "createDurableQueue",
"createQueue", "createQueue",
"createQueueAdminHandler",
"createQueueScheduler", "createQueueScheduler",
"createWorkflowEngine", "createWorkflowEngine",
"cronToInterval", "cronToInterval",
"databaseQueueStore",
"defineDurableWorkflow", "defineDurableWorkflow",
"defineJob", "defineJob",
"defineJobStateMachine",
"defineQueue",
"defineSaga",
"defineWorker", "defineWorker",
"defineWorkflow", "defineWorkflow",
"drainOutbox",
"installDatabaseQueueSchema",
"installOutboxSchema",
"installSqliteQueueSchema",
"memoryQueueStore", "memoryQueueStore",
"memoryWorkflowStore", "memoryWorkflowStore",
"postgresQueueStore", "postgresQueueStore",
"queueDashboardSnapshot", "queueDashboardSnapshot",
"queueStorageConfig",
"redisQueueStore", "redisQueueStore",
"renderQueueDashboard", "renderQueueDashboard",
"runQueueDaemon", "runQueueDaemon",
"runWorker", "runWorker",
"subjectQueue" "sqliteQueueSchema",
"sqliteQueueStore",
"subjectQueue",
"testQueue",
"withOutbox"
],
"./sqlite": [
"SqliteQueueClient",
"installSqliteQueueSchema",
"sqliteQueueSchema",
"sqliteQueueStore"
] ]
}, },
"@wrnexus/react": { "@wrnexus/react": {
@@ -2363,6 +2537,7 @@
".": [ ".": [
"AnimationTimeline", "AnimationTimeline",
"Cleanup", "Cleanup",
"FormState",
"HistorySignal", "HistorySignal",
"ReactiveContext", "ReactiveContext",
"ReactiveScope", "ReactiveScope",
@@ -2389,6 +2564,7 @@
"transition", "transition",
"untrack", "untrack",
"urlSignal", "urlSignal",
"useForm",
"watch" "watch"
] ]
}, },
@@ -2916,11 +3092,14 @@
".": [ ".": [
"BrowserArtifactPage", "BrowserArtifactPage",
"Deferred", "Deferred",
"DetectMutationsOptions",
"FetchMock", "FetchMock",
"Harness", "Harness",
"HarnessOptions", "HarnessOptions",
"JsonResponse", "JsonResponse",
"MemoryCookieJar", "MemoryCookieJar",
"MutationCase",
"MutationReport",
"TestRequestOptions", "TestRequestOptions",
"TransactionalDatabase", "TransactionalDatabase",
"WaitForOptions", "WaitForOptions",
@@ -2938,6 +3117,7 @@
"createTestContext", "createTestContext",
"deferred", "deferred",
"describe", "describe",
"detectMutations",
"expect", "expect",
"expectProblem", "expectProblem",
"it", "it",
+30 -30
View File
@@ -4,22 +4,22 @@
"artifacts": { "artifacts": {
"packages/ui/components/Accordion.wrn": "09251f76a2f9385e97eb67e06c0cf868e1771ac5f420647221bec5160287034b", "packages/ui/components/Accordion.wrn": "09251f76a2f9385e97eb67e06c0cf868e1771ac5f420647221bec5160287034b",
"packages/ui/components/AdvancedSelect.wrn": "b952b894a5b35050fc2f6e43c4182101eeee05c75662e2aca4abfe61512b305b", "packages/ui/components/AdvancedSelect.wrn": "b952b894a5b35050fc2f6e43c4182101eeee05c75662e2aca4abfe61512b305b",
"packages/ui/components/AnnouncementBar.wrn": "cb66b8d0619ee798a115b43f0ad93fda93dfb46571589e7a9f0a8d65ec3c84c9", "packages/ui/components/AnnouncementBar.wrn": "5b3219db555e0fcdb94ec41dc18186ee1b7c0cf9c260469f7fb1fc43435c28c4",
"packages/ui/components/AuthForm.wrn": "46a3e1db1c9e69efc52473cbf2fababdcbfe7144e7571469638d2c3addd4d7f0", "packages/ui/components/AuthForm.wrn": "46a3e1db1c9e69efc52473cbf2fababdcbfe7144e7571469638d2c3addd4d7f0",
"packages/ui/components/AuthSplitLayout.wrn": "724938921bc38cc5a0422de69c237b630f099f2df94c66977362b048e4a420a5", "packages/ui/components/AuthSplitLayout.wrn": "724938921bc38cc5a0422de69c237b630f099f2df94c66977362b048e4a420a5",
"packages/ui/components/AvatarGroup.wrn": "30b702e06a1adff8b9bae32820206020e086a69560cbdcbadb19f04f25951f97", "packages/ui/components/AvatarGroup.wrn": "30b702e06a1adff8b9bae32820206020e086a69560cbdcbadb19f04f25951f97",
"packages/ui/components/BackToTop.wrn": "5b760ec7941ffd8e58c5dedae1b322ee46e072e927533d7ec077b511a816e436", "packages/ui/components/BackToTop.wrn": "d4b8d1fd37ff26c89b4276ce54ce6ac9581dc2ff8bbcbe160e072778bba76eab",
"packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e", "packages/ui/components/Blockquote.wrn": "3761bf13babb6eb07ce7392258ee439ef5478064cf589eff63f945b950e00d2e",
"packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a", "packages/ui/components/Breadcrumb.wrn": "9f6c23550ebfc635c36ca8660953775170b4a471c0cd0206b9a7ba760994980a",
"packages/ui/components/ButtonGroup.wrn": "fc23565ad56bd8483fcb777d9e02ccba2f9ea476c085754d42503bb83b718f72", "packages/ui/components/ButtonGroup.wrn": "fc23565ad56bd8483fcb777d9e02ccba2f9ea476c085754d42503bb83b718f72",
"packages/ui/components/CTASection.wrn": "d0665f2aa33eea95e6f84120f739b3dd9c8723c882138bcb1a7f80dcd7909904", "packages/ui/components/CTASection.wrn": "f6872b980432d5ee9d88388250be9838ccce51bd706349b283ef472ee692b237",
"packages/ui/components/Card.wrn": "3cc2ff238bda279c169026a462236144ad11ec0046deef00ab00e001a07e8a4c", "packages/ui/components/Card.wrn": "be8cd6a438d1ede4c284a302b76f0fbc235a2423b83a7ced21131763fecc8a90",
"packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9", "packages/ui/components/Carousel.wrn": "e7d921f802aa19210f3f415b59b70750a2c8d78e30684ad334809741c70a6bf9",
"packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4", "packages/ui/components/Chart.wrn": "07bc01247b5a1c2b82c2ba8386b7fcdbf480efead75d52b7da04d471072c7ee4",
"packages/ui/components/ChatBubble.wrn": "d71f7d6567d76ac0eb3ecc8e2e567fec12bb3236ea05f4d0a72286abc0b7cbb1", "packages/ui/components/ChatBubble.wrn": "d71f7d6567d76ac0eb3ecc8e2e567fec12bb3236ea05f4d0a72286abc0b7cbb1",
"packages/ui/components/Clipboard.wrn": "5a93f2bae337d7ce364229b796bac673f60dde6dc135f0969f4e106e04854d30", "packages/ui/components/Clipboard.wrn": "5a93f2bae337d7ce364229b796bac673f60dde6dc135f0969f4e106e04854d30",
"packages/ui/components/Collapse.wrn": "26df087a61c82f1aa1749785f4c9be654c4f934859c1a47acfcb8cc67eecc75d", "packages/ui/components/Collapse.wrn": "26df087a61c82f1aa1749785f4c9be654c4f934859c1a47acfcb8cc67eecc75d",
"packages/ui/components/ColorPicker.wrn": "557e36adbdee66d8ba9dcdc2446c13f3385a243f2766777feef8504908006c99", "packages/ui/components/ColorPicker.wrn": "ccac7eb49c0f443b404004f1298fa905fde45c52b51466194920939c920348c9",
"packages/ui/components/Columns.wrn": "8ccd9e53f6799b09f8b996740d12e4e73ee782f6fd3e57595c6c16f788b9b812", "packages/ui/components/Columns.wrn": "8ccd9e53f6799b09f8b996740d12e4e73ee782f6fd3e57595c6c16f788b9b812",
"packages/ui/components/Combobox.wrn": "58488dbb60e41dacbf223afdb4a05abdbec53b7f54cd79a0db659add7057b1c1", "packages/ui/components/Combobox.wrn": "58488dbb60e41dacbf223afdb4a05abdbec53b7f54cd79a0db659add7057b1c1",
"packages/ui/components/Confetti.wrn": "0e81426a3ee8aff672385a5466cf96085c25ba17677a2fef432bcddf13f4d58e", "packages/ui/components/Confetti.wrn": "0e81426a3ee8aff672385a5466cf96085c25ba17677a2fef432bcddf13f4d58e",
@@ -28,7 +28,7 @@
"packages/ui/components/CustomScrollbar.wrn": "2a3a2719580c21823d079c92ebb000c7a5a7d9d16e3fa9275b8fb7d4449cb437", "packages/ui/components/CustomScrollbar.wrn": "2a3a2719580c21823d079c92ebb000c7a5a7d9d16e3fa9275b8fb7d4449cb437",
"packages/ui/components/DataMap.wrn": "338f0d0a3307289dd0387b7fe7ba631fa0b65b6be8c7b9ee3feb2203306f76a3", "packages/ui/components/DataMap.wrn": "338f0d0a3307289dd0387b7fe7ba631fa0b65b6be8c7b9ee3feb2203306f76a3",
"packages/ui/components/DataTable.wrn": "e929fdb4e59613878a03fb6a11e09c42f773c06ca7b20e43ae16a266835f2af4", "packages/ui/components/DataTable.wrn": "e929fdb4e59613878a03fb6a11e09c42f773c06ca7b20e43ae16a266835f2af4",
"packages/ui/components/DatePicker.wrn": "91bcfd3832d3bccc4de5402c0c06209947bde593bad4f869b4c29ee14a5c0bef", "packages/ui/components/DatePicker.wrn": "cefbbb2abb57ad4c57864a0c65589592a20f9ea1695f9fc7693c3135dd82e71a",
"packages/ui/components/DeviceFrame.wrn": "c38d25419ee44b05d7c443cc4e854aeeb7a53a9289e374d71834de2dd2e4d979", "packages/ui/components/DeviceFrame.wrn": "c38d25419ee44b05d7c443cc4e854aeeb7a53a9289e374d71834de2dd2e4d979",
"packages/ui/components/Divider.wrn": "bfa36cf16ccd48c996bb0b08a438f1b281ea1811049ed69fc7680925b1eba019", "packages/ui/components/Divider.wrn": "bfa36cf16ccd48c996bb0b08a438f1b281ea1811049ed69fc7680925b1eba019",
"packages/ui/components/DragAndDrop.wrn": "84305f07d43cdb9eff1f63c19458d88b0aec013424d05a49a2cafbecedc60513", "packages/ui/components/DragAndDrop.wrn": "84305f07d43cdb9eff1f63c19458d88b0aec013424d05a49a2cafbecedc60513",
@@ -36,15 +36,15 @@
"packages/ui/components/Dropdown.wrn": "978367a9dde22448cc664d686e4dd9c579fe8ba0b2cf02d610d95a3501f8dbb0", "packages/ui/components/Dropdown.wrn": "978367a9dde22448cc664d686e4dd9c579fe8ba0b2cf02d610d95a3501f8dbb0",
"packages/ui/components/FeatureCard.wrn": "1bc548c115f924a0c7ade4c557852757083755c60ca0ee747b150a7cca3ebced", "packages/ui/components/FeatureCard.wrn": "1bc548c115f924a0c7ade4c557852757083755c60ca0ee747b150a7cca3ebced",
"packages/ui/components/FeatureGrid.wrn": "15b5d6f9ddb7e3697682e7247fdf3c2dd080b770335f84ba9fab2692166fe897", "packages/ui/components/FeatureGrid.wrn": "15b5d6f9ddb7e3697682e7247fdf3c2dd080b770335f84ba9fab2692166fe897",
"packages/ui/components/FeatureIconCard.wrn": "3b4f3fa62c6729e686886a5b848e26c255766684829cec2b715967b4e73d6f07", "packages/ui/components/FeatureIconCard.wrn": "5f65d64235babc3e653a5695204412202015e6f3ffadca566fc18c7f9ce70428",
"packages/ui/components/FileInput.wrn": "866a292a3280527bf429893469e7c50f7cf38965d8adb37b45a35ede1606d5b8", "packages/ui/components/FileInput.wrn": "611047c362458a4e2513b741cc3ef43fbe54c10ddafc13775e4ff39c9de34931",
"packages/ui/components/FileUploadProgress.wrn": "e5da29c562a521cdd6bb50b8f4d217aa1ab981d7b2a2432af47391472f0a8034", "packages/ui/components/FileUploadProgress.wrn": "ba00557afebf60e6221385d6efcf08635789328deee6e551fc53fbb71e1f413b",
"packages/ui/components/Footer.wrn": "f7c5a77064a09e244f689d42475c4c20143f4c8859cac451c10a50c14652db33", "packages/ui/components/Footer.wrn": "e3515e05ef3ef51e6dad71d67ddfe110c80fdcf69e952aecdc7973e69d4f08c7",
"packages/ui/components/Grid.wrn": "83d4f5f2656d539538723f5791ba3c238901432e9d8c55c74f068d3eb5a74517", "packages/ui/components/Grid.wrn": "83d4f5f2656d539538723f5791ba3c238901432e9d8c55c74f068d3eb5a74517",
"packages/ui/components/Hero.wrn": "345478b212701817ff57060f87982a987baf01a7179c979768e1cd4218b50900", "packages/ui/components/Hero.wrn": "345478b212701817ff57060f87982a987baf01a7179c979768e1cd4218b50900",
"packages/ui/components/HeroActions.wrn": "67cd31400ccb6abdbbf16219267dee946b44b064b79f25276d20ceb7d6e8a790", "packages/ui/components/HeroActions.wrn": "67cd31400ccb6abdbbf16219267dee946b44b064b79f25276d20ceb7d6e8a790",
"packages/ui/components/Image.wrn": "361179c478d7674c3b998891a366d73de9514fc6cc8786440c6c50b9ef065357", "packages/ui/components/Image.wrn": "361179c478d7674c3b998891a366d73de9514fc6cc8786440c6c50b9ef065357",
"packages/ui/components/InputGroup.wrn": "30e9f2e4364542179180b6d6e796002d0ffe1e30305146749874697cde6629ea", "packages/ui/components/InputGroup.wrn": "9e259d15207500d07ff626f09c2e2f5eaf4e471f805a7bdbec154b1a23ce0f8d",
"packages/ui/components/InputNumber.wrn": "069195cc483f3f7d6f305d03fbe5a821906a030376fafb6f0d969e4c95b68fd0", "packages/ui/components/InputNumber.wrn": "069195cc483f3f7d6f305d03fbe5a821906a030376fafb6f0d969e4c95b68fd0",
"packages/ui/components/Kbd.wrn": "6b402cb93f0fa76bb978d1ba483215f70ec7214b1c5e84b568fa3e26e87ed9d9", "packages/ui/components/Kbd.wrn": "6b402cb93f0fa76bb978d1ba483215f70ec7214b1c5e84b568fa3e26e87ed9d9",
"packages/ui/components/LayoutSplitter.wrn": "41d070e801a271552023e4392e4c0f67327c61e2e8eaee5f8b87af254b1d79c1", "packages/ui/components/LayoutSplitter.wrn": "41d070e801a271552023e4392e4c0f67327c61e2e8eaee5f8b87af254b1d79c1",
@@ -55,38 +55,38 @@
"packages/ui/components/Map.wrn": "3e6d5de5a21dd050c5c65950d6fb2f89af4024e1856a48f055b65b88fcd7b890", "packages/ui/components/Map.wrn": "3e6d5de5a21dd050c5c65950d6fb2f89af4024e1856a48f055b65b88fcd7b890",
"packages/ui/components/MarketingSectionHeader.wrn": "39b79499b77dc1c7d34e4e5ecb45a9ddc6182f78c2a2586671c4fa07f44679ad", "packages/ui/components/MarketingSectionHeader.wrn": "39b79499b77dc1c7d34e4e5ecb45a9ddc6182f78c2a2586671c4fa07f44679ad",
"packages/ui/components/Marquee.wrn": "81cd80732be34c8d7e4bf28b00347e1a9273166afdf66589d9f5d5774b69403b", "packages/ui/components/Marquee.wrn": "81cd80732be34c8d7e4bf28b00347e1a9273166afdf66589d9f5d5774b69403b",
"packages/ui/components/MegaMenu.wrn": "6a6db166259f8f2bd7ac8fbb21dba6d915c4b936d87b19805caa78189f54730a", "packages/ui/components/MegaMenu.wrn": "a34a84f31f7a3016cf5a607a22f8f7b2322552c550278f6e8c5e6d6cb18848a8",
"packages/ui/components/MetricCard.wrn": "513a6e99be3d9db46d96af9942b51331a0a048b184603e77e814506f0a52bdc5", "packages/ui/components/MetricCard.wrn": "513a6e99be3d9db46d96af9942b51331a0a048b184603e77e814506f0a52bdc5",
"packages/ui/components/MetricGrid.wrn": "c71e53249835908833055b553e64a8ee55fdf11ac4605436a9581ebb87a0608b", "packages/ui/components/MetricGrid.wrn": "1fcebf51dcbe8520fe16bda904c1ef206c25c3900438d53877492c64ea21a1a5",
"packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2", "packages/ui/components/Modal.wrn": "7fa4b877772736f29f690e24d8742922b310397ef3083f0b78acb67a9f0d14b2",
"packages/ui/components/Nav.wrn": "c45b0ecb42250f4b3ede33c8932a025f789dda9ef48dbf332459ae458163e69a", "packages/ui/components/Nav.wrn": "c45b0ecb42250f4b3ede33c8932a025f789dda9ef48dbf332459ae458163e69a",
"packages/ui/components/Navbar.wrn": "01903230210cd2e5e6d9325a0708f85af9623d4118e3a53f1adb770fd545425d", "packages/ui/components/Navbar.wrn": "b68a824e479ce6957afc3e9710442281cdc1a8d02cd96501fe060271f54368e4",
"packages/ui/components/PageHeader.wrn": "321cde36ce8d521a57901033d46e8b437f42e558cca27f49ca26f7172aff1d54", "packages/ui/components/PageHeader.wrn": "29a93cdca7285548b79ebe5b6421eef6dbb4d5ae8be2508fa2d681e1068ba07f",
"packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39", "packages/ui/components/Pagination.wrn": "d54226705d4556f76ee5f0d6ae82d101ca6d006397756d547a9aa5954415ba39",
"packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602", "packages/ui/components/PinInput.wrn": "4dc398456f6392d7925db941debb484c7cb0358ecef527f696fe5ec4800a7602",
"packages/ui/components/Popover.wrn": "1246b99b4236bc7109a5d1427d013bb07bbea7a2da4f821517d2545ee0b5c019", "packages/ui/components/Popover.wrn": "1246b99b4236bc7109a5d1427d013bb07bbea7a2da4f821517d2545ee0b5c019",
"packages/ui/components/PortalDashboard.wrn": "f04171eb1f8cabef06de3c20e4694b766def3407e1e20d2c9aaebc9bb7da03ee", "packages/ui/components/PortalDashboard.wrn": "f04171eb1f8cabef06de3c20e4694b766def3407e1e20d2c9aaebc9bb7da03ee",
"packages/ui/components/PreferenceSwitcher.wrn": "77229e15449182563ebf49159507139928eb74e799e4438e00e9aa81fd60f982", "packages/ui/components/PreferenceSwitcher.wrn": "519cf911390f529fff3fd16e11fb58e79d618fe851902202dc37552ee1182395",
"packages/ui/components/PublicPageShell.wrn": "3d3f25c03d48f3d3d7e3c567e79cb137c7fce1867e054fd6d440fa1d2e961077", "packages/ui/components/PublicPageShell.wrn": "3d3f25c03d48f3d3d7e3c567e79cb137c7fce1867e054fd6d440fa1d2e961077",
"packages/ui/components/RangeSlider.wrn": "bbf646c5d5a89a62986290db811d77b8f635ebcaea9ca2d334b569aa5edc785b", "packages/ui/components/RangeSlider.wrn": "e8ab41782c9d6e58638f0bfccf6e3172af4ebf552189d2199cc6c96bd1652067",
"packages/ui/components/Rating.wrn": "902b55c64e3798f3097f09b1558d1a2372f66d4f760662de085783108ee04587", "packages/ui/components/Rating.wrn": "902b55c64e3798f3097f09b1558d1a2372f66d4f760662de085783108ee04587",
"packages/ui/components/Scrollspy.wrn": "b9c6400ee92a7c228362610a2051f37bff1d3baba1dbacd66d58397dfda5d856", "packages/ui/components/Scrollspy.wrn": "4b452fcbd524feda9c9f2c81894ee01747a51888e3b86fafb37c5d4d192a3a51",
"packages/ui/components/SearchBox.wrn": "3f88340e0b69c0cd552023c72bd22dc511bc00422d46060097bcf0777e683675", "packages/ui/components/SearchBox.wrn": "3f88340e0b69c0cd552023c72bd22dc511bc00422d46060097bcf0777e683675",
"packages/ui/components/Section.wrn": "45b7a1977c25a6020d5cc8bab2ce9c80730e7ec2d51ad53e8c9f64cd9b5b64cd", "packages/ui/components/Section.wrn": "78da13eb8bc1acafe8e6151cbaeed4baaac2ab860e3209f1d13c467f279d2e68",
"packages/ui/components/SectionHeader.wrn": "aca2df8eb35806096f5b9af466b018e5c22a19422e8665f488380da577a0ceb5", "packages/ui/components/SectionHeader.wrn": "aca2df8eb35806096f5b9af466b018e5c22a19422e8665f488380da577a0ceb5",
"packages/ui/components/Sidebar.wrn": "e0250a973aea589a4d7dcc8b30dd01f22592bf22ba012e54a672b2a2288d8af6", "packages/ui/components/Sidebar.wrn": "e0250a973aea589a4d7dcc8b30dd01f22592bf22ba012e54a672b2a2288d8af6",
"packages/ui/components/SplitHero.wrn": "843387dc7b920b3cf6a1b2b785b8653c281982e524479edc2638a89381ff3577", "packages/ui/components/SplitHero.wrn": "7127cb869d1228522d066e028818dd9e512de8b6dd8f99e3dec500e6da5a3cb0",
"packages/ui/components/StatsBar.wrn": "c273bd6ca417a0a20e5e6f1214414f678741b73bedd885fea1037575fa8c5b95", "packages/ui/components/StatsBar.wrn": "6fabcb4374b1e074f58ebcaba968f235c8a3409762163aea51298e2515a14435",
"packages/ui/components/Stepper.wrn": "7ec0902acd7cb74b44d490215b6421dae9a41731735ad1e9aec17e8bfc3fa142", "packages/ui/components/Stepper.wrn": "7ec0902acd7cb74b44d490215b6421dae9a41731735ad1e9aec17e8bfc3fa142",
"packages/ui/components/StrongPassword.wrn": "407eabcc13affc9323a346ddba8e39c81b885a59ef7043d2bd5567d4b7553282", "packages/ui/components/StrongPassword.wrn": "cc31cb080c4b4116c544b4340b8d740d08fec1b05563372b3280c50759416c87",
"packages/ui/components/StyledIcon.wrn": "0874f9a234b053a7c8b9c6983be5d664453a64c4576dff1a049101e249a2866d", "packages/ui/components/StyledIcon.wrn": "0874f9a234b053a7c8b9c6983be5d664453a64c4576dff1a049101e249a2866d",
"packages/ui/components/Tabs.wrn": "f85214960e62da2fac41228deba511a50c57fd94f2830a8233b75048f6a542e9", "packages/ui/components/Tabs.wrn": "f85214960e62da2fac41228deba511a50c57fd94f2830a8233b75048f6a542e9",
"packages/ui/components/TextLink.wrn": "9d561631c5f65a2bcdc59f5c0b166a5dd794a396f42093f2cd26388386f6f954", "packages/ui/components/TextLink.wrn": "9d561631c5f65a2bcdc59f5c0b166a5dd794a396f42093f2cd26388386f6f954",
"packages/ui/components/TimePicker.wrn": "ec0860a4a82a042a65acf9aa81519a5c815b61e0b8080cf6f1c986d26ce7f0e8", "packages/ui/components/TimePicker.wrn": "33bd7a7069b7abe858cf1617b5b950733b37a8d522469ed673cff00b4dfcad0e",
"packages/ui/components/Timeline.wrn": "fb553f69763840532eef1c0aafa79013f57411dc5335e7cb7e7c20c5a3753008", "packages/ui/components/Timeline.wrn": "fb553f69763840532eef1c0aafa79013f57411dc5335e7cb7e7c20c5a3753008",
"packages/ui/components/Toaster.wrn": "5e4c9b2c1bfd5675a81a32e57684b99393ac7fe45d8420c7409a4e95d47d080c", "packages/ui/components/Toaster.wrn": "5e4c9b2c1bfd5675a81a32e57684b99393ac7fe45d8420c7409a4e95d47d080c",
"packages/ui/components/ToggleCount.wrn": "e577195ee05ddb43cceec47145f6b90c0149f2f17055733d58911fcd02de2c7f", "packages/ui/components/ToggleCount.wrn": "e577195ee05ddb43cceec47145f6b90c0149f2f17055733d58911fcd02de2c7f",
"packages/ui/components/TogglePassword.wrn": "6bd4d9fa82b0c8e80178430d5a42b1b3ec9725fb62be158585f68af9338c901a", "packages/ui/components/TogglePassword.wrn": "caa58479e5c8b1e3e504181a56a7d6bcf71d0a230010733710961c31e1cc58ab",
"packages/ui/components/TreeView.wrn": "f54d8495730203b923629bcfb167d5ca41236af0101ea2723d33354413555b4b", "packages/ui/components/TreeView.wrn": "f54d8495730203b923629bcfb167d5ca41236af0101ea2723d33354413555b4b",
"packages/ui/components/Typography.wrn": "9676af57f2a937b21f45512e0a290b5bee4a16267fbf404850297d29645d88b5", "packages/ui/components/Typography.wrn": "9676af57f2a937b21f45512e0a290b5bee4a16267fbf404850297d29645d88b5",
"packages/ui/components/WysiwygEditor.wrn": "a858ebd4e6e2672463ba57d4d049176ca1cc98dac32ab3df3638d6a9cdcd0f2d", "packages/ui/components/WysiwygEditor.wrn": "a858ebd4e6e2672463ba57d4d049176ca1cc98dac32ab3df3638d6a9cdcd0f2d",
@@ -94,15 +94,15 @@
"packages/ui/components/avatar.wrn": "8bc705e459b13c7f063c57e1506ce540e50fa8ef9020ae85b6e8b6a3579fc5dc", "packages/ui/components/avatar.wrn": "8bc705e459b13c7f063c57e1506ce540e50fa8ef9020ae85b6e8b6a3579fc5dc",
"packages/ui/components/badge.wrn": "e44e33633fb34e897696cd9290f210108e35a3e2dc3b2a4a367411f45c69a1e1", "packages/ui/components/badge.wrn": "e44e33633fb34e897696cd9290f210108e35a3e2dc3b2a4a367411f45c69a1e1",
"packages/ui/components/button.wrn": "65570c58cb2547f1e17cd158eec7cf7b1ffc721e6976c6486c9df564627b1219", "packages/ui/components/button.wrn": "65570c58cb2547f1e17cd158eec7cf7b1ffc721e6976c6486c9df564627b1219",
"packages/ui/components/checkbox.wrn": "18046396b75d0b9c6bb2bdb09dbff1846c84fb07490f390a25ca7b5367fd2ef4", "packages/ui/components/checkbox.wrn": "da735354b57f33946591e1bd018b521ff709bae1325e2317735824673188377a",
"packages/ui/components/input.wrn": "e4c14527f009b610b4aa96d8d7b7d5ac3ca1ffd34237bab348ee9dc4ddc55847", "packages/ui/components/input.wrn": "2f17353cbf0711ca50a935ae03ee9ee53594d7e35eb73f5df573fadb7958fac2",
"packages/ui/components/progress.wrn": "6307a90585197d7aab19a8710b2430f5d4ed27ce77e9b90b1414ea0eed876492", "packages/ui/components/progress.wrn": "6307a90585197d7aab19a8710b2430f5d4ed27ce77e9b90b1414ea0eed876492",
"packages/ui/components/radio.wrn": "09425a78358de5bbd2f47482f313e80065135335b969ce5ccdd5c7cc3ea5232c", "packages/ui/components/radio.wrn": "df8e562324b6d99f5a157b0971d63bd715daa0cf6189e7b0d4df04d6f0411388",
"packages/ui/components/select.wrn": "1d8d47a74d5ab9ed58d0ba11f46910897233b96652d17f5962f5dda60bd4cf8a", "packages/ui/components/select.wrn": "747321d6292b8052e44e8014e87ce25f4b56d8e07f86f1061e0ed0cec7651356",
"packages/ui/components/skeleton.wrn": "4fc5e0846eeefd7830c038e1789be995c4f9d833aa913ff079eb4863baa65648", "packages/ui/components/skeleton.wrn": "4fc5e0846eeefd7830c038e1789be995c4f9d833aa913ff079eb4863baa65648",
"packages/ui/components/spinner.wrn": "2322645da7ef53f7c06035ff071d9a2f6ffe2901ff9338daed84037369367105", "packages/ui/components/spinner.wrn": "2322645da7ef53f7c06035ff071d9a2f6ffe2901ff9338daed84037369367105",
"packages/ui/components/switch.wrn": "874504e4828e9db6a570d78984c4d3a76d0ceb39d70baa877649cb3798c82c85", "packages/ui/components/switch.wrn": "243f521431ffb644089df7f629bace0c07094638dfdd27e75b2017efa9cbba4f",
"packages/ui/components/textarea.wrn": "9870d37664471a103d44435a3977bf40b087a743acd76ee4d77df9b56cccbb2b", "packages/ui/components/textarea.wrn": "aa3a75214e998a0b57c50e416476d6f85fa0313aaf97262f2f9222150c6c9e26",
"packages/ui/components/tooltip.wrn": "f3dfdfa5cd9661fe5e95ef3580eef4c7437c069726421370c2d7328fbe7d840d", "packages/ui/components/tooltip.wrn": "f3dfdfa5cd9661fe5e95ef3580eef4c7437c069726421370c2d7328fbe7d840d",
"packages/ui/styles/SelectStyles.wrn": "074fe0d67de4ef5e9f9cfe879beb72fd5c83352888687d3a1a4b7722c87af3ca", "packages/ui/styles/SelectStyles.wrn": "074fe0d67de4ef5e9f9cfe879beb72fd5c83352888687d3a1a4b7722c87af3ca",
"packages/ui/ui.css": "9ea591404e9cf675bbf1003e15e9fad00327094ff217dc20733f0fc87c0f4a62" "packages/ui/ui.css": "9ea591404e9cf675bbf1003e15e9fad00327094ff217dc20733f0fc87c0f4a62"
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "wrnexus", "name": "wrnexus",
"displayName": "WRNexus Language Support", "displayName": "WRNexus Language Support",
"description": "Complete WRNexus v0.8.3 language support for typed imports, props, state, outputs, runtime functions, stores, diagnostics, formatting, navigation, and migration assistance.", "description": "Complete WRNexus v0.8.3 language support for typed imports, props, state, outputs, runtime functions, stores, diagnostics, formatting, navigation, and migration assistance.",
"version": "0.8.9", "version": "0.8.11",
"publisher": "wrnexus", "publisher": "wrnexus",
"private": true, "private": true,
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
+130 -20
View File
@@ -1,7 +1,7 @@
"use strict"; "use strict";
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly. // Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
// WRN editor compiler source hash: 1e7593d6b6bd1c9b8439fc34a30f9db9f7ae64f1788365a74115170aa34cd185 // WRN editor compiler source hash: 3d346a264807ba7b107584dc9a7b505b1d14057b2ad1aa85c5ea890f9b09c272
// WRN editor compiler generator hash: a54ca847c758bc98d8e353ad6d70088df31de1820f6cf9d1c3462505f563e6b8 // WRN editor compiler generator hash: f8c1094b47e695151205899de28cd63d574356d26ac49219c4a43db0f2391ef5
// Generated with TypeScript: 6.0.3 // Generated with TypeScript: 6.0.3
const __nodeRequire = require; const __nodeRequire = require;
const __path = __nodeRequire("node:path"); const __path = __nodeRequire("node:path");
@@ -1148,6 +1148,31 @@ function componentEventAttribute(name) {
} }
return `data-wrn-out-${name}`; return `data-wrn-out-${name}`;
} }
/** Expand bind:value/bind:checked into a reactive prop plus assignment handler. */
function expandBindings(attrs) {
const output = [];
for (const attr of attrs) {
if (attr.event || !attr.name.startsWith("bind:")) {
output.push(attr);
continue;
}
const target = attr.name.slice("bind:".length);
if (target !== "value" && target !== "checked") {
throw new Error(`Unknown .wrn binding '${attr.name}'. Use bind:value or bind:checked.`);
}
const expression = wholeAttributeExpression(attr.value) ?? attr.value.trim();
if (!/^[A-Za-z_$][\w$]*$/.test(expression)) {
throw new Error(`${attr.name} requires a writable state name, received '${expression}'.`);
}
output.push({ name: target, value: `{${expression}}`, event: false });
output.push({
name: target === "checked" ? "change" : "input",
value: `${expression} = payload.${target}`,
event: true,
});
}
return output;
}
function reactiveAttrValue(raw, reactive) { function reactiveAttrValue(raw, reactive) {
let found = false; let found = false;
const value = raw.replace(/\{([^{}]+)\}/g, (whole, inner) => { const value = raw.replace(/\{([^{}]+)\}/g, (whole, inner) => {
@@ -1166,6 +1191,7 @@ function reactiveAttrValue(raw, reactive) {
return found ? value : null; return found ? value : null;
} }
function renderAttrs(attrs, csrId, reactive = null, dynamicExpressions) { function renderAttrs(attrs, csrId, reactive = null, dynamicExpressions) {
attrs = expandBindings(attrs);
let bindIndex = 0; let bindIndex = 0;
const rendered = attrs const rendered = attrs
.map((attr) => { .map((attr) => {
@@ -1302,7 +1328,7 @@ function renderLoopBody(node, locals = []) {
return compileIfExpr(node, locals); return compileIfExpr(node, locals);
} }
const componentTag = isComponentTag(node.tag); const componentTag = isComponentTag(node.tag);
const attrs = node.attrs const attrs = expandBindings(node.attrs)
.filter((attr) => attr.name !== "data-component") .filter((attr) => attr.name !== "data-component")
.map((attr) => { .map((attr) => {
const name = attr.event const name = attr.event
@@ -1562,9 +1588,9 @@ function renderPageComponentInvocation(node, ssrBindings, csrBindings, apiBindin
const island = islandMarkerFor(node); const island = islandMarkerFor(node);
if (island) if (island)
return island; return island;
const attrs = node.attrs const attrs = expandBindings(node.attrs)
.filter((attr) => attr.name !== "data-component") .filter((attr) => attr.name !== "data-component")
.map((attr) => renderPageComponentAttr(attr, loops)) .map((attr) => renderPageComponentAttr(attr, loops, reactive))
.join(""); .join("");
const inner = node.children const inner = node.children
.map((child) => renderNode(child, ssrBindings, csrBindings, apiBindings, loops, reactive)) .map((child) => renderNode(child, ssrBindings, csrBindings, apiBindings, loops, reactive))
@@ -1577,7 +1603,7 @@ function renderNestedComponentInvocation(node, ctx) {
if (island) if (island)
return island; return island;
let bindIndex = 0; let bindIndex = 0;
const attrs = node.attrs const attrs = expandBindings(node.attrs)
.filter((attr) => attr.name !== "data-component") .filter((attr) => attr.name !== "data-component")
.map((attr) => { .map((attr) => {
const spread = /^\{\.\.\.([A-Za-z_$][\w$]*)\}$/.exec(attr.name); const spread = /^\{\.\.\.([A-Za-z_$][\w$]*)\}$/.exec(attr.name);
@@ -2446,6 +2472,8 @@ ${exposed
${declarations} ${declarations}
${visible.length ${visible.length
? ` const __values = await Promise.all([${visible.map((entry) => `__load_${entry.name}()`).join(", ")}]); ? ` const __values = await Promise.all([${visible.map((entry) => `__load_${entry.name}()`).join(", ")}]);
const __response = __values.find((value) => value instanceof Response);
if (__response) return __response;
return { ${visible.map((entry, index) => `${JSON.stringify(entry.name)}: __values[${index}]`).join(", ")} };` return { ${visible.map((entry, index) => `${JSON.stringify(entry.name)}: __values[${index}]`).join(", ")} };`
: ""} : ""}
}`; }`;
@@ -2765,7 +2793,7 @@ function renderClientControlTemplate(nodes) {
} }
const componentTag = isComponentTag(node.tag); const componentTag = isComponentTag(node.tag);
let bindIndex = 0; let bindIndex = 0;
const attrs = node.attrs const attrs = expandBindings(node.attrs)
.map((attribute) => { .map((attribute) => {
const name = attribute.event const name = attribute.event
? componentTag ? componentTag
@@ -2803,19 +2831,21 @@ function encodeClientControl(value) {
} }
function renderComponentIfNode(node, ctx) { function renderComponentIfNode(node, ctx) {
let expression = "``"; let expression = "``";
const clientBranches = [];
for (let index = node.branches.length - 1; index >= 0; index--) { for (let index = node.branches.length - 1; index >= 0; index--) {
const branch = node.branches[index]; const branch = node.branches[index];
const body = branch.body.map((child) => renderComponentNode(child, ctx)).join(""); const body = branch.body.map((child) => renderComponentNode(child, ctx)).join("");
const bodyExpression = "`" + body + "`"; const bodyExpression = "`" + body + "`";
clientBranches.unshift(`{ cond: ${branch.cond === null ? "null" : JSON.stringify(branch.cond)}, body: ${bodyExpression} }`);
expression = expression =
branch.cond === null branch.cond === null
? bodyExpression ? bodyExpression
: `(${ctx.resolveExpr(branch.cond)}) ? ${bodyExpression} : ${expression}`; : `(${ctx.resolveExpr(branch.cond)}) ? ${bodyExpression} : ${expression}`;
} }
const definition = encodeClientControl(node.branches.map((branch) => ({ // Render all branches on the server into an inert, encoded definition.
cond: branch.cond, // A branch first selected in the browser therefore contains the real nested
body: renderClientControlTemplate(branch.body), // component HTML and scope metadata, not a data-component placeholder.
}))); const definition = `\${__wrnexusEncodeControl([${clientBranches.join(",")}])}`;
return `<template data-wrn-if="${definition}"></template>${"${" + expression + "}"}<template data-wrn-control-end></template>`; return `<template data-wrn-if="${definition}"></template>${"${" + expression + "}"}<template data-wrn-control-end></template>`;
} }
function renderComponentEachNode(node, ctx) { function renderComponentEachNode(node, ctx) {
@@ -2955,7 +2985,7 @@ function renderComponentNode(node, ctx) {
} }
} }
const isExplicitComponentMount = node.attrs.some((attribute) => attribute.name === "data-component"); const isExplicitComponentMount = node.attrs.some((attribute) => attribute.name === "data-component");
const attrs = node.attrs const attrs = expandBindings(node.attrs)
.filter((a) => a.name !== "class" && !a.name.startsWith("class:")) .filter((a) => a.name !== "class" && !a.name.startsWith("class:"))
.map((a) => { .map((a) => {
const spread = /^\{\.\.\.([A-Za-z_$][\w$]*)\}$/.exec(a.name); const spread = /^\{\.\.\.([A-Za-z_$][\w$]*)\}$/.exec(a.name);
@@ -3413,6 +3443,9 @@ function __wrnRaw(v: unknown): string {
}`); }`);
} }
if (needsScope) { if (needsScope) {
out.push(`function __wrnexusEncodeControl(value: unknown): string {
return __WrnexusBuffer.from(JSON.stringify(value), "utf8").toString("base64");
}`);
out.push(`function __wrnexusSerializeScopeValue(value: unknown): string { out.push(`function __wrnexusSerializeScopeValue(value: unknown): string {
if (value === undefined) { if (value === undefined) {
return "undefined"; return "undefined";
@@ -3495,7 +3528,7 @@ function __wrnProp(v) {
const value = v !== null && typeof v === "object" ? JSON.stringify(v) : String(v == null ? "" : v); const value = v !== null && typeof v === "object" ? JSON.stringify(v) : String(v == null ? "" : v);
return __wrnAttr(value); return __wrnAttr(value);
} }
function renderPageComponentAttr(attr, dynamicExpressions) { function renderPageComponentAttr(attr, dynamicExpressions, _reactive) {
if (attr.event) { if (attr.event) {
return ` ${componentEventAttribute(attr.name)}="${attrEscape(attr.value)}"`; return ` ${componentEventAttribute(attr.name)}="${attrEscape(attr.value)}"`;
} }
@@ -3508,7 +3541,11 @@ function renderPageComponentAttr(attr, dynamicExpressions) {
} }
dynamicExpressions.push(`\${__wrnexusPropAttr(${expression})}`); dynamicExpressions.push(`\${__wrnexusPropAttr(${expression})}`);
const marker = `\x00WRNEACH${dynamicExpressions.length - 1}\x00`; const marker = `\x00WRNEACH${dynamicExpressions.length - 1}\x00`;
return ` ${attr.name}="${marker}"`; // The mount disappears during SSR. Preserve every dynamic component prop
// as a parent-owned binding; server-only expressions simply fail closed in
// the browser, while page state can continue driving the mounted child.
const binding = ` data-wrn-prop-bind-${dynamicExpressions.length - 1}="${attrEscape(JSON.stringify([attr.name, attr.value]))}"`;
return ` ${attr.name}="${marker}"${binding}`;
} }
}, },
@@ -4030,6 +4067,7 @@ function islandNamesFrom(imports) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.NativeCompileError = void 0; exports.NativeCompileError = void 0;
exports.generateNative = generateNative; exports.generateNative = generateNative;
const syntax_1 = require("@wrnexus/syntax");
class NativeCompileError extends Error { class NativeCompileError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
@@ -4095,12 +4133,12 @@ function expression(value) {
function textJsx(value) { function textJsx(value) {
const pieces = []; const pieces = [];
let last = 0; let last = 0;
for (const match of value.matchAll(/\{([^{}]+)\}/g)) { for (const segment of (0, syntax_1.extractInterpolations)(value)) {
if (match.index > last) if (segment.start > last)
pieces.push(value.slice(last, match.index)); pieces.push(value.slice(last, segment.start));
const expr = match[1].trim(); const expr = segment.expression;
pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`); pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`);
last = match.index + match[0].length; last = segment.end;
} }
pieces.push(value.slice(last)); pieces.push(value.slice(last));
return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "&lt;" : "&gt;")); return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "&lt;" : "&gt;"));
@@ -6190,6 +6228,26 @@ function expandStructuredStateDeclarations(lines, unit) {
} }
}); });
} }
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value);
if (!match)
return null;
try {
const parsed = JSON.parse(match[2].trim());
const compact = JSON.stringify(parsed);
const indentation = unit.repeat(depth);
if (indentation.length + match[1].length + compact.length <= printWidth)
return null;
const jsonLines = JSON.stringify(parsed, null, unit).split("\n");
return [
`${indentation}${match[1]}${jsonLines[0]}`,
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`),
];
}
catch {
return null;
}
}
function formatWrnPass(source, options = {}) { function formatWrnPass(source, options = {}) {
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
const printWidth = options.printWidth ?? 100; const printWidth = options.printWidth ?? 100;
@@ -6245,6 +6303,12 @@ function formatWrnPass(source, options = {}) {
index += 1; index += 1;
continue; continue;
} }
const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth);
if (structuredProp) {
output.push(...structuredProp);
index += 1;
continue;
}
const leadingClosingBraces = countLeadingClosingBraces(value); const leadingClosingBraces = countLeadingClosingBraces(value);
const closesControlBlock = isControlBlockClose(value); const closesControlBlock = isControlBlockClose(value);
const continuesControlBlock = isControlBlockMiddle(value); const continuesControlBlock = isControlBlockMiddle(value);
@@ -6325,7 +6389,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.stripRuntimeFunctionModifiers = exports.parseStructuredImports = exports.parseStoreLifecycle = exports.parseStateDeclarations = exports.parseRuntimeFunctions = exports.parsePersist = exports.parseOutputs = exports.parseComputedDeclarations = exports.supportsSyntaxFeature = exports.diagnosticSummary = exports.sliceSource = exports.createSourceRange = exports.WRN_SYNTAX_FEATURES = exports.WRN_SYNTAX_VERSION = exports.positionAt = exports.isRuntimeTarget = exports.isHydrationStrategy = exports.formatDiagnostic = exports.diagnosticFromError = exports.diagnose = exports.containsReadonlyPropMutation = exports.classifyParseError = exports.assertValidAst = exports.validateTypedInitializer = exports.runtimeTypeOf = exports.inferredRuntimeType = exports.eraseFunctionTypes = exports.VOID_ELEMENTS = exports.ParseError = exports.parseHtmlView = exports.parse = exports.formatWrn = exports.skipLiteralOrComment = exports.LexError = exports.Lexer = void 0; exports.stripRuntimeFunctionModifiers = exports.parseStructuredImports = exports.parseStoreLifecycle = exports.parseStateDeclarations = exports.parseRuntimeFunctions = exports.parsePersist = exports.parseOutputs = exports.parseComputedDeclarations = exports.supportsSyntaxFeature = exports.diagnosticSummary = exports.sliceSource = exports.createSourceRange = exports.WRN_SYNTAX_FEATURES = exports.WRN_SYNTAX_VERSION = exports.positionAt = exports.isRuntimeTarget = exports.isHydrationStrategy = exports.formatDiagnostic = exports.diagnosticFromError = exports.diagnose = exports.containsReadonlyPropMutation = exports.classifyParseError = exports.assertValidAst = exports.validateTypedInitializer = exports.runtimeTypeOf = exports.inferredRuntimeType = exports.eraseFunctionTypes = exports.extractInterpolations = exports.VOID_ELEMENTS = exports.ParseError = exports.parseHtmlView = exports.parse = exports.formatWrn = exports.skipLiteralOrComment = exports.LexError = exports.Lexer = void 0;
var tokenizer_ts_1 = require("./tokenizer.js"); var tokenizer_ts_1 = require("./tokenizer.js");
Object.defineProperty(exports, "Lexer", { enumerable: true, get: function () { return tokenizer_ts_1.Lexer; } }); Object.defineProperty(exports, "Lexer", { enumerable: true, get: function () { return tokenizer_ts_1.Lexer; } });
Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return tokenizer_ts_1.LexError; } }); Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return tokenizer_ts_1.LexError; } });
@@ -6337,6 +6401,8 @@ Object.defineProperty(exports, "parse", { enumerable: true, get: function () { r
Object.defineProperty(exports, "parseHtmlView", { enumerable: true, get: function () { return parser_ts_1.parseHtmlView; } }); Object.defineProperty(exports, "parseHtmlView", { enumerable: true, get: function () { return parser_ts_1.parseHtmlView; } });
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return parser_ts_1.ParseError; } }); Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return parser_ts_1.ParseError; } });
Object.defineProperty(exports, "VOID_ELEMENTS", { enumerable: true, get: function () { return parser_ts_1.VOID_ELEMENTS; } }); Object.defineProperty(exports, "VOID_ELEMENTS", { enumerable: true, get: function () { return parser_ts_1.VOID_ELEMENTS; } });
var interpolation_ts_1 = require("./interpolation.js");
Object.defineProperty(exports, "extractInterpolations", { enumerable: true, get: function () { return interpolation_ts_1.extractInterpolations; } });
var types_ts_1 = require("./types.js"); var types_ts_1 = require("./types.js");
Object.defineProperty(exports, "eraseFunctionTypes", { enumerable: true, get: function () { return types_ts_1.eraseFunctionTypes; } }); Object.defineProperty(exports, "eraseFunctionTypes", { enumerable: true, get: function () { return types_ts_1.eraseFunctionTypes; } });
Object.defineProperty(exports, "inferredRuntimeType", { enumerable: true, get: function () { return types_ts_1.inferredRuntimeType; } }); Object.defineProperty(exports, "inferredRuntimeType", { enumerable: true, get: function () { return types_ts_1.inferredRuntimeType; } });
@@ -6370,6 +6436,50 @@ Object.defineProperty(exports, "parseStoreLifecycle", { enumerable: true, get: f
Object.defineProperty(exports, "parseStructuredImports", { enumerable: true, get: function () { return v060_ts_1.parseStructuredImports; } }); Object.defineProperty(exports, "parseStructuredImports", { enumerable: true, get: function () { return v060_ts_1.parseStructuredImports; } });
Object.defineProperty(exports, "stripRuntimeFunctionModifiers", { enumerable: true, get: function () { return v060_ts_1.stripRuntimeFunctionModifiers; } }); Object.defineProperty(exports, "stripRuntimeFunctionModifiers", { enumerable: true, get: function () { return v060_ts_1.stripRuntimeFunctionModifiers; } });
},
"packages/syntax/src/interpolation.ts": function (module, exports, require, __filename, __dirname) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractInterpolations = extractInterpolations;
/** Extract top-level `{...}` expressions while preserving nested object/array literals. */
function extractInterpolations(value) {
const segments = [];
let index = 0;
while (index < value.length) {
if (value[index] !== "{") {
index++;
continue;
}
const start = index++;
let depth = 1;
let quote = null;
while (index < value.length && depth > 0) {
const char = value[index];
if (quote) {
if (char === "\\" && index + 1 < value.length)
index += 2;
else {
if (char === quote)
quote = null;
index++;
}
continue;
}
if (char === '"' || char === "'" || char === "`")
quote = char;
else if (char === "{")
depth++;
else if (char === "}")
depth--;
index++;
}
if (depth === 0) {
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
}
}
return segments;
}
}, },
"packages/syntax/src/parser.ts": function (module, exports, require, __filename, __dirname) { "packages/syntax/src/parser.ts": function (module, exports, require, __filename, __dirname) {
"use strict"; "use strict";
File diff suppressed because it is too large Load Diff
+145 -8
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
// WRN editor language server source hash: 3c6060ff9db332cf1dc01a467d795fb2d002300400072a4b87437c48d61ab7f9 // WRN editor language server source hash: 5258b3e76cec47df752d4bb886fbd57a3f3c4af9ca9b6b2bf6742c8d78025ff1
// WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72 // WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72
// @bun @bun-cjs // @bun @bun-cjs
(function(exports, require, module, __filename, __dirname) {var __create = Object.create; (function(exports, require, module, __filename, __dirname) {var __create = Object.create;
@@ -23,12 +23,14 @@ var __toESM = (mod, isNodeMode, target) => {
} }
target = mod != null ? __create(__getProtoOf(mod)) : {}; target = mod != null ? __create(__getProtoOf(mod)) : {};
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
if (mod && typeof mod === "object" || typeof mod === "function") {
for (let key of __getOwnPropNames(mod)) for (let key of __getOwnPropNames(mod))
if (!__hasOwnProp.call(to, key)) if (!__hasOwnProp.call(to, key))
__defProp(to, key, { __defProp(to, key, {
get: __accessProp.bind(mod, key), get: __accessProp.bind(mod, key),
enumerable: true enumerable: true
}); });
}
if (canCache) if (canCache)
cache.set(mod, to); cache.set(mod, to);
return to; return to;
@@ -66,7 +68,7 @@ var __export = (target, all) => {
}; };
// node_modules/.bun/typescript@6.0.3/node_modules/typescript/lib/typescript.js // node_modules/.bun/typescript@6.0.3/node_modules/typescript/lib/typescript.js
var require_typescript = __commonJS((exports2, module2) => { var require_typescript = __commonJS(function(exports2, module2) {
var __dirname = "E:\\WireJS\\node_modules\\.bun\\typescript@6.0.3\\node_modules\\typescript\\lib", __filename = "E:\\WireJS\\node_modules\\.bun\\typescript@6.0.3\\node_modules\\typescript\\lib\\typescript.js"; var __dirname = "E:\\WireJS\\node_modules\\.bun\\typescript@6.0.3\\node_modules\\typescript\\lib", __filename = "E:\\WireJS\\node_modules\\.bun\\typescript@6.0.3\\node_modules\\typescript\\lib\\typescript.js";
/*! ***************************************************************************** /*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved. Copyright (c) Microsoft Corporation. All rights reserved.
@@ -169514,7 +169516,7 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
}); });
// node_modules/.bun/@vscode+l10n@0.0.18/node_modules/@vscode/l10n/dist/main.js // node_modules/.bun/@vscode+l10n@0.0.18/node_modules/@vscode/l10n/dist/main.js
var require_main = __commonJS((exports2, module2) => { var require_main = __commonJS(function(exports2, module2) {
var __defProp2 = Object.defineProperty; var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames; var __getOwnPropNames2 = Object.getOwnPropertyNames;
@@ -169658,9 +169660,9 @@ var require_main = __commonJS((exports2, module2) => {
// packages/language-server/src/server.ts // packages/language-server/src/server.ts
var exports_server = {}; var exports_server = {};
__export(exports_server, { __export(exports_server, {
isApiReferenceAt: () => isApiReferenceAt, apiCallCompletions: () => apiCallCompletions,
apiCallHover: () => apiCallHover, apiCallHover: () => apiCallHover,
apiCallCompletions: () => apiCallCompletions isApiReferenceAt: () => isApiReferenceAt
}); });
module.exports = __toCommonJS(exports_server); module.exports = __toCommonJS(exports_server);
@@ -170720,6 +170722,26 @@ function expandStructuredStateDeclarations(lines, unit) {
} }
}); });
} }
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(value);
if (!match)
return null;
try {
const parsed = JSON.parse(match[2].trim());
const compact = JSON.stringify(parsed);
const indentation = unit.repeat(depth);
if (indentation.length + match[1].length + compact.length <= printWidth)
return null;
const jsonLines = JSON.stringify(parsed, null, unit).split(`
`);
return [
`${indentation}${match[1]}${jsonLines[0]}`,
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`)
];
} catch {
return null;
}
}
function formatWrnPass(source, options = {}) { function formatWrnPass(source, options = {}) {
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
const printWidth = options.printWidth ?? 100; const printWidth = options.printWidth ?? 100;
@@ -170776,6 +170798,12 @@ function formatWrnPass(source, options = {}) {
index += 1; index += 1;
continue; continue;
} }
const structuredProp = formatStructuredPropDeclaration(value, unit, codeDepth + htmlDepth + controlDepth, printWidth);
if (structuredProp) {
output.push(...structuredProp);
index += 1;
continue;
}
const leadingClosingBraces = countLeadingClosingBraces(value); const leadingClosingBraces = countLeadingClosingBraces(value);
const closesControlBlock = isControlBlockClose(value); const closesControlBlock = isControlBlockClose(value);
const continuesControlBlock = isControlBlockMiddle(value); const continuesControlBlock = isControlBlockMiddle(value);
@@ -172391,6 +172419,44 @@ function parseHtmlView(src, pos) {
const nodes = parseNodeList("root"); const nodes = parseNodeList("root");
return { nodes, endPos: i }; return { nodes, endPos: i };
} }
// packages/syntax/src/interpolation.ts
function extractInterpolations(value) {
const segments = [];
let index = 0;
while (index < value.length) {
if (value[index] !== "{") {
index++;
continue;
}
const start = index++;
let depth = 1;
let quote = null;
while (index < value.length && depth > 0) {
const char = value[index];
if (quote) {
if (char === "\\" && index + 1 < value.length)
index += 2;
else {
if (char === quote)
quote = null;
index++;
}
continue;
}
if (char === '"' || char === "'" || char === "`")
quote = char;
else if (char === "{")
depth++;
else if (char === "}")
depth--;
index++;
}
if (depth === 0) {
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
}
}
return segments;
}
// packages/syntax/src/diagnostics.ts // packages/syntax/src/diagnostics.ts
function stripAsciiControlAndSpace(value) { function stripAsciiControlAndSpace(value) {
let result = ""; let result = "";
@@ -172988,7 +173054,7 @@ function runtimeNamespace(runtime) {
return `__wrn_${runtime}`; return `__wrn_${runtime}`;
} }
function functionDeclaration(fn) { function functionDeclaration(fn) {
const params = fn.parameters.map((param) => `${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : param.name === "event" ? ": CustomEvent<any> & { target: HTMLElement }" : ": any"}${param.default ? ` = ${param.default}` : ""}`).join(", "); const params = fn.parameters.map((param) => `${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : param.name === "event" ? ": Event & { target: HTMLElement }" : ": any"}${param.default ? ` = ${param.default}` : ""}`).join(", ");
return `export ${fn.async ? "async " : ""}function ${fn.name}(${params})${fn.returnType ? `: ${fn.returnType}` : ""} {${fn.body}}`; return `export ${fn.async ? "async " : ""}function ${fn.name}(${params})${fn.returnType ? `: ${fn.returnType}` : ""} {${fn.body}}`;
} }
function retainedImports(ast) { function retainedImports(ast) {
@@ -173048,6 +173114,27 @@ var RESERVED_BINDING_NAMES = new Set([
function safeBindingName(name) { function safeBindingName(name) {
return /^[A-Za-z_$][\w$]*$/.test(name) && !RESERVED_BINDING_NAMES.has(name); return /^[A-Za-z_$][\w$]*$/.test(name) && !RESERVED_BINDING_NAMES.has(name);
} }
function safeViewExpression(expression, reservedProps) {
if (!reservedProps.size)
return expression;
const scanner = import_typescript.default.createScanner(import_typescript.default.ScriptTarget.Latest, false, import_typescript.default.LanguageVariant.Standard, expression);
let result = "";
let copied = 0;
for (let token = scanner.scan();token !== import_typescript.default.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
const start = scanner.getTokenPos();
const end = scanner.getTextPos();
const text = scanner.getTokenText();
if (!reservedProps.has(text))
continue;
const previous = expression.slice(0, start).trimEnd().at(-1);
const following = expression.slice(end).trimStart().at(0);
if (previous === "." || following === ":")
continue;
result += expression.slice(copied, start) + `props.${text}`;
copied = end;
}
return result + expression.slice(copied);
}
function virtualTypeScriptModule(source, filePath = "component.wrn", appRoot = findAppRoot(filePath)) { function virtualTypeScriptModule(source, filePath = "component.wrn", appRoot = findAppRoot(filePath)) {
const ast = parse(source); const ast = parse(source);
const chunks = []; const chunks = [];
@@ -173103,6 +173190,8 @@ function virtualTypeScriptModule(source, filePath = "component.wrn", appRoot = f
append(`declare const api: { ${apiType} };`); append(`declare const api: { ${apiType} };`);
append(`declare const props: Readonly<${ast.name}Props>;`); append(`declare const props: Readonly<${ast.name}Props>;`);
append("declare const refs: Record<string, Element | null>;"); append("declare const refs: Record<string, Element | null>;");
append("declare const toast: ((message: string, options?: Record<string, unknown>) => unknown) & { success(message: string, options?: Record<string, unknown>): unknown; error(message: string, options?: Record<string, unknown>): unknown; danger(message: string, options?: Record<string, unknown>): unknown; warning(message: string, options?: Record<string, unknown>): unknown; info(message: string, options?: Record<string, unknown>): unknown; dismiss(id: string | number): void; clear(): void };");
append("declare function useFetch(path: string, method?: string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown }, input?: unknown): Promise<any>;");
append(ast.kind === "global-store" || ast.kind === "page-store" ? storeContract(ast) : componentContract(ast)); append(ast.kind === "global-store" || ast.kind === "page-store" ? storeContract(ast) : componentContract(ast));
append(importedWrnDeclarations(ast, filePath, appRoot)); append(importedWrnDeclarations(ast, filePath, appRoot));
const sharedNames = new Set(ast.runtimeFunctions.filter((fn) => fn.runtime === "shared").map((fn) => fn.name)); const sharedNames = new Set(ast.runtimeFunctions.filter((fn) => fn.runtime === "shared").map((fn) => fn.name));
@@ -173119,6 +173208,53 @@ ${sharedAliases}`);
append(functionDeclaration(fn), fn.source); append(functionDeclaration(fn), fn.source);
append("}"); append("}");
} }
const viewFunctions = new Map;
for (const fn of ast.runtimeFunctions) {
if (fn.runtime !== "server")
viewFunctions.set(fn.name, fn.runtime);
}
for (const [name, runtime] of viewFunctions) {
append(`declare const ${name}: typeof ${runtimeNamespace(runtime)}.${name};`);
}
let bindingIndex = 0;
const reservedProps = new Set(ast.props.map((prop) => prop.name).filter((name) => !safeBindingName(name)));
const appendViewBindings = (nodes, locals) => {
for (const node of nodes) {
if (node.type === "element") {
for (const attr of node.attrs) {
const expressions = attr.event ? [attr.value] : extractInterpolations(attr.value).map((segment) => segment.expression);
for (const expression of expressions) {
if (!expression.trim())
continue;
const declarations = [
...[...locals].map((name) => `declare const ${name}: any;`),
...attr.event ? [
"declare const payload: any;",
"declare const event: Event & { target: HTMLElement };"
] : []
].join(" ");
append(`namespace __wrn_view_${bindingIndex++} { ${declarations} void (${safeViewExpression(expression, reservedProps)}); }`, attr.value);
}
}
appendViewBindings(node.children, locals);
} else if (node.type === "if") {
for (const branch of node.branches) {
if (branch.cond)
append(`namespace __wrn_view_${bindingIndex++} { void (${safeViewExpression(branch.cond, reservedProps)}); }`, branch.cond);
appendViewBindings(branch.body, locals);
}
} else if (node.type === "each") {
append(`namespace __wrn_view_${bindingIndex++} { void (${safeViewExpression(node.list, reservedProps)}); }`, node.list);
const nested = new Set(locals);
nested.add(node.item);
if (node.index)
nested.add(node.index);
appendViewBindings(node.body, nested);
appendViewBindings(node.empty, locals);
}
}
};
appendViewBindings(ast.view, new Set);
return { return {
ast, ast,
fileName: filePath.replace(/\.wrn$/i, ".wrn.ts"), fileName: filePath.replace(/\.wrn$/i, ".wrn.ts"),
@@ -173224,7 +173360,7 @@ function componentUsageDiagnostics(source, ast, filePath, appRoot) {
const shape = shapes.get(node.tag); const shape = shapes.get(node.tag);
if (!shape) if (!shape)
return; return;
const attributes = new Map(node.attrs.filter((attr) => !attr.event).map((attr) => [attr.name, attr])); const attributes = new Map(node.attrs.filter((attr) => !attr.event).map((attr) => [attr.name.startsWith("bind:") ? attr.name.slice(5) : attr.name, attr]));
const outputNames = new Set(shape.outputs.map((output) => output.name)); const outputNames = new Set(shape.outputs.map((output) => output.name));
const position = lineAt(source, `<${node.tag}`); const position = lineAt(source, `<${node.tag}`);
for (const prop of shape.props) { for (const prop of shape.props) {
@@ -173261,7 +173397,8 @@ function componentUsageDiagnostics(source, ast, filePath, appRoot) {
} }
if (/^(?:class|id|style|slot|data-|aria-)/.test(attr.name) || attr.name === "attrs") if (/^(?:class|id|style|slot|data-|aria-)/.test(attr.name) || attr.name === "attrs")
continue; continue;
const prop = known.get(attr.name); const propName = attr.name.startsWith("bind:") ? attr.name.slice(5) : attr.name;
const prop = known.get(propName);
if (!prop) { if (!prop) {
diagnostics.push({ diagnostics.push({
code: "WRN-COMPONENT-UNKNOWN-PROP", code: "WRN-COMPONENT-UNKNOWN-PROP",
+14
View File
@@ -20,6 +20,20 @@ test("preserves nested prop defaults while formatting", () => {
assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2 }), formatted); assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2 }), formatted);
}); });
test("formats large typed JSON prop defaults with the bundled formatter", () => {
const source = `component Catalog {
props {
categories: unknown[] = [{"label":"Platform & Core","apps":[{"label":"Home","href":"/home"},{"label":"SSO","href":"/sso"}]},{"label":"Sales","apps":[{"label":"CRM","href":"/crm"}]}]
}
view { <div></div> }
}`;
const formatted = formatWrn(source, { insertSpaces: true, tabSize: 2, printWidth: 70 });
assert.match(formatted, /categories: unknown\[\] = \[\n \{/);
assert.match(formatted, /"apps": \[\n \{/);
assert.equal(formatWrn(formatted, { insertSpaces: true, tabSize: 2, printWidth: 70 }), formatted);
});
test("formats native JSON state values with readable indentation", () => { test("formats native JSON state values with readable indentation", () => {
const source = `component Footer { const source = `component Footer {
state footerItems = [{"label":"Accessibility","href":"/accessibility","value":"accessibility"},{"label":"Privacy","href":"/privacy","value":"privacy"}] state footerItems = [{"label":"Accessibility","href":"/accessibility","value":"accessibility"},{"label":"Privacy","href":"/privacy","value":"privacy"}]
+2 -2
View File
@@ -55,7 +55,7 @@
"generate:ui-visual": "node scripts/check-ui-visual-contract.mjs --write", "generate:ui-visual": "node scripts/check-ui-visual-contract.mjs --write",
"sbom": "node scripts/generate-sbom.mjs", "sbom": "node scripts/generate-sbom.mjs",
"benchmark:framework": "node --experimental-transform-types scripts/benchmark-framework.mjs --write", "benchmark:framework": "node --experimental-transform-types scripts/benchmark-framework.mjs --write",
"check:production": "bun run check:workspace && bun run check:generated-types && bun run check:public-api && bun run check:ui-visual && bun run validate:0.8 && bun run security:framework && bun run security:asvs && bun run check:editor-compiler && bun run check:editor-language-server && bun run check:editor-extension && bun run check && bun run test:examples", "check:production": "bun run check:workspace && bun run check:generated-types && bun run check:public-api && bun run check:ui-visual && bun run validate:0.8 && bun run security:framework && bun run security:asvs && bun run check:editor-compiler && bun run check:editor-language-server && bun run check:editor-extension && bun run check && bun run test:examples && bun run test:services && bun run test:editor",
"validate:staging": "node --experimental-transform-types scripts/test-package-integrity.mjs", "validate:staging": "node --experimental-transform-types scripts/test-package-integrity.mjs",
"stage:packages": "bun run scripts/publish-packages.ts", "stage:packages": "bun run scripts/publish-packages.ts",
"test:staged-consumers": "node scripts/test-staged-consumers.mjs", "test:staged-consumers": "node scripts/test-staged-consumers.mjs",
@@ -70,7 +70,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@types/bun": "^1.3.14", "@types/bun": "^1.4.0",
"@types/react": "^19.2.18", "@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4", "@types/react-dom": "^19.2.4",
"eslint": "^10.8.1", "eslint": "^10.8.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/captcha", "name": "@wrnexus/captcha",
"version": "0.8.11", "version": "0.8.14",
"description": "First-class CAPTCHA challenges, providers, verification guards, page gates, and WRNexusJS UI.", "description": "First-class CAPTCHA challenges, providers, verification guards, page gates, and WRNexusJS UI.",
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
@@ -0,0 +1,63 @@
import { describe, expect, test } from "bun:test";
import {
captchaContext,
captchaResultResponse,
captchaTokenFrom,
verifyCaptchaOrThrow,
} from "../src/helpers.ts";
describe("CAPTCHA helper boundaries", () => {
test("extracts JSON request fallbacks without consuming the caller's request", async () => {
const request = new Request("https://example.test/verify", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ captchaToken: 12345 }),
});
expect(await captchaTokenFrom(request)).toBe("12345");
expect(await request.json()).toEqual({ captchaToken: 12345 });
});
test("turns verification failures into stable errors and no-store responses", async () => {
const provider = {
name: "custom" as const,
client: { responseField: "captchaToken" },
verify: async () => ({
success: false as const,
code: "expired",
message: "Try again",
provider: "custom" as const,
action: "login",
}),
};
await expect(
verifyCaptchaOrThrow(provider, { providerToken: "x", action: "login" }),
).rejects.toThrow("WRN-CAPTCHA-EXPIRED: Try again");
const response = captchaResultResponse({
success: false,
code: "invalid",
provider: "custom",
action: "login",
});
expect(response.status).toBe(403);
expect(response.headers.get("cache-control")).toBe("no-store");
expect(
captchaContext({
locals: { captcha: { success: true, provider: "custom", action: "login" } },
} as never),
).toEqual({ success: true, provider: "custom", action: "login" });
expect(captchaContext({ locals: { captcha: "forged" } } as never)).toBeNull();
});
test("does not convert provider transport failures into successful verification", async () => {
const provider = {
name: "custom" as const,
client: { responseField: "captchaToken" },
verify: async () => {
throw new Error("provider timeout");
},
};
await expect(
verifyCaptchaOrThrow(provider, { providerToken: "x", action: "checkout" }),
).rejects.toThrow("provider timeout");
});
});
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/cli", "name": "@wrnexus/cli",
"version": "0.8.60", "version": "0.8.64",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+5
View File
@@ -113,6 +113,11 @@ Update options: --dry-run previews changes; --no-verify skips post-update check/
async function main(): Promise<void> { async function main(): Promise<void> {
const [command, ...rest] = process.argv.slice(2); const [command, ...rest] = process.argv.slice(2);
if (command === "--version" || command === "-v" || command === "version") {
console.log(currentCliVersion());
return;
}
notifyIfUpdateAvailable(currentCliVersion(), command); notifyIfUpdateAvailable(currentCliVersion(), command);
switch (command) { switch (command) {
+3 -1
View File
@@ -72,7 +72,9 @@ export function createTestPlan(appRoot: string, args: string[]): TestCommandPlan
const watch = args.includes("--watch"); const watch = args.includes("--watch");
const profile = args.find((value) => value.startsWith("--profile="))?.slice(10) || "test"; const profile = args.find((value) => value.startsWith("--profile="))?.slice(10) || "test";
const setupSource = join(import.meta.dir, "test-setup.ts"); const setupSource = join(import.meta.dir, "test-setup.ts");
const setupModule = existsSync(setupSource) ? setupSource : join(import.meta.dir, "test-setup.js"); const setupModule = existsSync(setupSource)
? setupSource
: join(import.meta.dir, "test-setup.js");
const shard = args.find((value) => value.startsWith("--shard="))?.slice(8); const shard = args.find((value) => value.startsWith("--shard="))?.slice(8);
const browsers = (args.find((value) => value.startsWith("--browsers="))?.slice(11) ?? "chromium") const browsers = (args.find((value) => value.startsWith("--browsers="))?.slice(11) ?? "chromium")
.split(",") .split(",")
+13 -4
View File
@@ -63,14 +63,16 @@ const execFileAsync = promisify(execFile);
/** Resolve every installed WRNexus dependency independently and in parallel. */ /** Resolve every installed WRNexus dependency independently and in parallel. */
export async function latestPackageVersions(names: string[]): Promise<Map<string, string>> { export async function latestPackageVersions(names: string[]): Promise<Map<string, string>> {
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
const unique = [...new Set(names.filter((name) => name.startsWith("@wrnexus/")))]; const unique = [...new Set(names.filter((name) => name.startsWith("@wrnexus/")))];
const entries = await Promise.all( const entries = await Promise.all(
unique.map(async (name) => { unique.map(async (name) => {
try { try {
const { stdout } = await execFileAsync(npm, ["view", name, "version", "--json"], { const command = process.platform === "win32" ? (process.env.ComSpec ?? "cmd.exe") : "npm";
encoding: "utf8", const args =
}); process.platform === "win32"
? ["/d", "/s", "/c", "npm", "view", name, "version", "--json"]
: ["view", name, "version", "--json"];
const { stdout } = await execFileAsync(command, args, { encoding: "utf8" });
const parsed = JSON.parse(stdout.trim()) as string | string[]; const parsed = JSON.parse(stdout.trim()) as string | string[];
const version = Array.isArray(parsed) ? parsed.at(-1) : parsed; const version = Array.isArray(parsed) ? parsed.at(-1) : parsed;
return typeof version === "string" && /^\d+\.\d+\.\d+/.test(version) return typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)
@@ -1274,6 +1276,13 @@ export async function runUpdate(dir: string, args: string[]): Promise<void> {
? new Map<string, string>() ? new Map<string, string>()
: await latestPackageVersions(dependencyNames); : await latestPackageVersions(dependencyNames);
if (!versionArg && dependencyNames.length > 0 && packageVersions.size === 0) {
throw new Error(
`Unable to resolve any of ${new Set(dependencyNames).size} installed WRNexus package ` +
`version(s) from npm. Check the @wrnexus registry and authentication before retrying.`,
);
}
console.log(`\n ⚡ wrnexus update → migrations ${target}${dryRun ? " (dry run)" : ""}`); console.log(`\n ⚡ wrnexus update → migrations ${target}${dryRun ? " (dry run)" : ""}`);
if (!versionArg) { if (!versionArg) {
console.log( console.log(
+24 -2
View File
@@ -1,8 +1,12 @@
import { afterEach, expect, test } from "bun:test"; import { afterEach, expect, test } from "bun:test";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join, resolve } from "node:path";
import { compareVersions, notifyIfUpdateAvailable } from "../src/update-notifier.ts"; import {
compareVersions,
currentCliVersion,
notifyIfUpdateAvailable,
} from "../src/update-notifier.ts";
const originalCache = process.env.WRNEXUS_UPDATE_CACHE; const originalCache = process.env.WRNEXUS_UPDATE_CACHE;
@@ -35,3 +39,21 @@ test("cached update checks suggest the exact upgrade command", () => {
rmSync(root, { recursive: true, force: true }); rmSync(root, { recursive: true, force: true });
} }
}); });
test("--version prints the package version outside a WrNexus workspace", () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-version-"));
try {
const result = Bun.spawnSync({
cmd: [process.execPath, resolve(import.meta.dir, "../src/index.ts"), "--version"],
cwd: root,
env: { ...process.env, WRNEXUS_NO_UPDATE_CHECK: "1" },
stdout: "pipe",
stderr: "pipe",
});
expect(result.exitCode).toBe(0);
expect(result.stdout.toString().trim()).toBe(currentCliVersion());
expect(result.stderr.toString()).toBe("");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/compiler", "name": "@wrnexus/compiler",
"version": "0.8.21", "version": "0.8.22",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+1 -1
View File
@@ -3175,7 +3175,7 @@ function __wrnProp(v: unknown): string {
function renderPageComponentAttr( function renderPageComponentAttr(
attr: Attr, attr: Attr,
dynamicExpressions: string[], dynamicExpressions: string[],
reactive: PageReactive | null, _reactive: PageReactive | null,
): string { ): string {
if (attr.event) { if (attr.event) {
return ` ${componentEventAttribute(attr.name)}="${attrEscape(attr.value)}"`; return ` ${componentEventAttribute(attr.name)}="${attrEscape(attr.value)}"`;
+5 -5
View File
@@ -1,4 +1,4 @@
import type { Attr, PageAst, ViewNode } from "@wrnexus/syntax"; import { extractInterpolations, type Attr, type PageAst, type ViewNode } from "@wrnexus/syntax";
export class NativeCompileError extends Error { export class NativeCompileError extends Error {
constructor(message: string) { constructor(message: string) {
@@ -68,11 +68,11 @@ function expression(value: string): string | null {
function textJsx(value: string): string { function textJsx(value: string): string {
const pieces: string[] = []; const pieces: string[] = [];
let last = 0; let last = 0;
for (const match of value.matchAll(/\{([^{}]+)\}/g)) { for (const segment of extractInterpolations(value)) {
if (match.index! > last) pieces.push(value.slice(last, match.index)); if (segment.start > last) pieces.push(value.slice(last, segment.start));
const expr = match[1]!.trim(); const expr = segment.expression;
pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`); pieces.push(expr.startsWith("t:") ? `{${JSON.stringify(expr.slice(2).trim())}}` : `{${expr}}`);
last = match.index! + match[0].length; last = segment.end;
} }
pieces.push(value.slice(last)); pieces.push(value.slice(last));
return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "&lt;" : "&gt;")); return pieces.join("").replace(/([<>])/g, (char) => (char === "<" ? "&lt;" : "&gt;"));
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/csr", "name": "@wrnexus/csr",
"version": "0.8.33", "version": "0.8.35",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+1 -1
View File
@@ -90,7 +90,7 @@ const CONTROLLER_LOADER = String.raw`
if (!componentControllerPromise) { if (!componentControllerPromise) {
componentControllerPromise = new Promise(function (resolve, reject) { componentControllerPromise = new Promise(function (resolve, reject) {
var script = document.createElement("script"); var script = document.createElement("script");
script.src = componentControllerUrl; script.src = trustedScriptURL(componentControllerUrl);
script.defer = true; script.defer = true;
script.onload = resolve; script.onload = resolve;
script.onerror = reject; script.onerror = reject;
+53 -6
View File
@@ -26,6 +26,40 @@ export const REACTIVE_RUNTIME = String.raw`
var behaviorObserver; var behaviorObserver;
var clientModuleCache = new Map(); var clientModuleCache = new Map();
/*
* Production enables require-trusted-types-for 'script'. Every HTML write
* below uses compiler-emitted template markup (or the explicit data-html
* escape hatch), and controller URLs are generated by the framework. Route
* those known sinks through the policy allowed by the default security
* preset instead of letting the browser reject hydration midway through a
* state update.
*/
var wrnexusTrustedTypesPolicy = null;
if (window.trustedTypes && typeof window.trustedTypes.createPolicy === "function") {
try {
wrnexusTrustedTypesPolicy = window.trustedTypes.createPolicy("wrnexus", {
createHTML: function (value) { return value; },
createScriptURL: function (value) { return value; }
});
} catch (_) {
wrnexusTrustedTypesPolicy = window.trustedTypes.defaultPolicy || null;
}
}
function trustedHTML(value) {
var source = value == null ? "" : String(value);
return wrnexusTrustedTypesPolicy
? wrnexusTrustedTypesPolicy.createHTML(source)
: source;
}
function trustedScriptURL(value) {
var source = value == null ? "" : String(value);
return wrnexusTrustedTypesPolicy
? wrnexusTrustedTypesPolicy.createScriptURL(source)
: source;
}
/* /*
* Two builtin chains the runtime reaches for constantly. Aliasing them is * Two builtin chains the runtime reaches for constantly. Aliasing them is
* not only shorter: hasOwn keeps prototype keys from reading as data, and * not only shorter: hasOwn keeps prototype keys from reading as data, and
@@ -234,8 +268,21 @@ export const REACTIVE_RUNTIME = String.raw`
Array.prototype.forEach.call(rules || [], function (rule) { Array.prototype.forEach.call(rules || [], function (rule) {
var cssText = rule.cssText || ""; var cssText = rule.cssText || "";
var match; var match;
var pattern = /var\(\s*(--wrn-[A-Za-z0-9_-]+)/g; // Only inspect declarations that can affect the current document. A
while ((match = pattern.exec(cssText))) referenced[match[1]] = true; // component stylesheet may contain optional variants that are not
// rendered on this page, and variables with fallbacks are intentionally
// optional rather than missing theme tokens.
if (rule.selectorText) {
try {
if (!document.querySelector(rule.selectorText)) return;
} catch (_) {
// Keep inspecting selectors that the DOM implementation cannot parse.
}
}
var pattern = /var\(\s*(--wrn-[A-Za-z0-9_-]+)\s*(,)?/g;
while ((match = pattern.exec(cssText))) {
if (!match[2]) referenced[match[1]] = true;
}
if (rule.style) { if (rule.style) {
Array.prototype.forEach.call(rule.style, function (property) { Array.prototype.forEach.call(rule.style, function (property) {
if (String(property).indexOf("--wrn-") === 0) declared[property] = true; if (String(property).indexOf("--wrn-") === 0) declared[property] = true;
@@ -1818,7 +1865,7 @@ export const REACTIVE_RUNTIME = String.raw`
var next = markup == null ? "" : String(markup); var next = markup == null ? "" : String(markup);
if (htmlNode.innerHTML !== next) { if (htmlNode.innerHTML !== next) {
htmlNode.innerHTML = next; htmlNode.innerHTML = trustedHTML(next);
} }
}); });
runHtml(); runHtml();
@@ -2002,7 +2049,7 @@ export const REACTIVE_RUNTIME = String.raw`
} }
function clearControlContent() { function clearControlContent() {
if (!rangeEnd) { block.innerHTML = ""; return; } if (!rangeEnd) { block.innerHTML = trustedHTML(""); return; }
while (block.nextSibling && block.nextSibling !== rangeEnd) { while (block.nextSibling && block.nextSibling !== rangeEnd) {
block.parentNode.removeChild(block.nextSibling); block.parentNode.removeChild(block.nextSibling);
} }
@@ -2010,7 +2057,7 @@ export const REACTIVE_RUNTIME = String.raw`
function appendControlContent(markup, locals) { function appendControlContent(markup, locals) {
var template = document.createElement("template"); var template = document.createElement("template");
template.innerHTML = markup || ""; template.innerHTML = trustedHTML(markup || "");
var fragment = template.content; var fragment = template.content;
var elements = toArray(fragment.childNodes).filter(function (node) { var elements = toArray(fragment.childNodes).filter(function (node) {
return node.nodeType === 1; return node.nodeType === 1;
@@ -2399,7 +2446,7 @@ export const REACTIVE_RUNTIME = String.raw`
var markup = value == null ? "" : String(value); var markup = value == null ? "" : String(value);
if (node.innerHTML !== markup) { if (node.innerHTML !== markup) {
node.innerHTML = markup; node.innerHTML = trustedHTML(markup);
} }
}); });
}); });
+39
View File
@@ -62,6 +62,13 @@ test("split runtime hydrates a controller only from the controller asset", () =>
expect(win.document.activeElement).toBe(buttons[1]); expect(win.document.activeElement).toBe(buttons[1]);
}); });
test("production hydration uses Trusted Types for HTML and controller URLs", () => {
const runtime = getReactiveRuntime();
expect(runtime).toContain('createPolicy("wrnexus"');
expect(runtime).toContain('template.innerHTML = trustedHTML(markup || "")');
expect(runtime).toContain("script.src = trustedScriptURL(componentControllerUrl)");
});
test("a page without controller markers does not request the controller asset", () => { test("a page without controller markers does not request the controller asset", () => {
const win = mount( const win = mount(
`<main data-scope="count: 1"><span>{count}</span></main>`, `<main data-scope="count: 1"><span>{count}</span></main>`,
@@ -1081,6 +1088,38 @@ test("development runtime accepts component-local and inline wrn variables", ()
expect(warnings).toHaveLength(0); expect(warnings).toHaveLength(0);
}); });
test("development runtime ignores fallback and inactive component variables", () => {
const win = new Window() as unknown as Window & Record<string, unknown>;
win.document.head.innerHTML =
`<style>.visible { color: var(--wrn-optional, red); } ` +
`.not-rendered { color: var(--wrn-inactive); }</style>`;
win.document.body.innerHTML = `<div class="visible" data-scope=""></div>`;
(globalThis as Record<string, unknown>).window = win;
(globalThis as Record<string, unknown>).document = win.document;
(globalThis as Record<string, unknown>).location = win.location;
(globalThis as Record<string, unknown>).NodeFilter = (
win as unknown as { NodeFilter: unknown }
).NodeFilter;
(globalThis as Record<string, unknown>).MutationObserver = (
win as unknown as { MutationObserver: unknown }
).MutationObserver;
(globalThis as Record<string, unknown>).CustomEvent = (
win as unknown as { CustomEvent: unknown }
).CustomEvent;
const warnings: unknown[][] = [];
const originalWarn = console.warn;
console.warn = (...args: unknown[]) => warnings.push(args);
try {
(0, eval)(getReactiveRuntime(true));
(
win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }
).__wrnexusHydrateScopes?.(win.document);
} finally {
console.warn = originalWarn;
}
expect(warnings).toHaveLength(0);
});
test("production runtime strips development diagnostics", () => { test("production runtime strips development diagnostics", () => {
const production = getReactiveRuntime(); const production = getReactiveRuntime();
expect(production).not.toContain("WRN-DEV-"); expect(production).not.toContain("WRN-DEV-");
+10 -2
View File
@@ -15,9 +15,17 @@ function fakeDb() {
test("seed helpers parameterize object rows and removals", async () => { test("seed helpers parameterize object rows and removals", async () => {
const { db, calls } = fakeDb(); const { db, calls } = fakeDb();
await addSeedData(db, [{ code: "free", credits: 10 }, { code: "pro", credits: 20 }], "plans", { await addSeedData(
db,
[
{ code: "free", credits: 10 },
{ code: "pro", credits: 20 },
],
"plans",
{
conflict: "ignore", conflict: "ignore",
}); },
);
await removeSeedData(db, { code: "free" }, "plans"); await removeSeedData(db, { code: "free" }, "plans");
expect(calls[0]).toEqual({ expect(calls[0]).toEqual({
sql: "INSERT OR IGNORE INTO plans (code, credits) VALUES (?, ?)", sql: "INSERT OR IGNORE INTO plans (code, credits) VALUES (?, ?)",
+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/dev-server", "name": "@wrnexus/dev-server",
"version": "0.8.52", "version": "0.8.56",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
@@ -25,6 +25,7 @@
"@wrnexus/uploader": "workspace:*", "@wrnexus/uploader": "workspace:*",
"@wrnexus/plugin": "workspace:*", "@wrnexus/plugin": "workspace:*",
"@wrnexus/queue": "workspace:*", "@wrnexus/queue": "workspace:*",
"@wrnexus/react": "workspace:*",
"@wrnexus/store": "workspace:*", "@wrnexus/store": "workspace:*",
"@wrnexus/security": "workspace:*", "@wrnexus/security": "workspace:*",
"@wrnexus/observability": "workspace:*", "@wrnexus/observability": "workspace:*",
+6
View File
@@ -367,6 +367,12 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
externalRoutes: pluginContributions.routes, externalRoutes: pluginContributions.routes,
middlewareFiles: pluginContributions.middleware, middlewareFiles: pluginContributions.middleware,
}); });
// UI and plugin components are intentionally registered application-wide;
// using them without a source import is not an implicit-import diagnostic.
setCompileImportOptions(appRoot, {
...importConfig,
globalComponents: router.components.map((component) => component.name),
});
const styleEntry = opts.styleEntry ?? null; const styleEntry = opts.styleEntry ?? null;
const devToolbarConfig = resolveDevToolbarConfig(mode, opts.devToolbar); const devToolbarConfig = resolveDevToolbarConfig(mode, opts.devToolbar);
+18 -3
View File
@@ -86,6 +86,7 @@ interface CompileImportOptions {
mode: ImportMode; mode: ImportMode;
aliases: Record<string, string>; aliases: Record<string, string>;
autoImport: boolean; autoImport: boolean;
globalComponents: Set<string>;
} }
const compileImportOptions = new Map<string, CompileImportOptions>(); const compileImportOptions = new Map<string, CompileImportOptions>();
const warnedImportDiagnostics = new Set<string>(); const warnedImportDiagnostics = new Set<string>();
@@ -104,12 +105,18 @@ export function setDevCompilerPipeline(pipeline: DevCompilerPipeline | null): vo
export function setCompileImportOptions( export function setCompileImportOptions(
appRoot: string, appRoot: string,
options: { mode?: ImportMode; aliases?: Record<string, string>; autoImport?: boolean } = {}, options: {
mode?: ImportMode;
aliases?: Record<string, string>;
autoImport?: boolean;
globalComponents?: Iterable<string>;
} = {},
): void { ): void {
compileImportOptions.set(resolve(appRoot), { compileImportOptions.set(resolve(appRoot), {
mode: options.mode ?? "compatible", mode: options.mode ?? "compatible",
aliases: { "@": "./app", ...(options.aliases ?? {}) }, aliases: { "@": "./app", ...(options.aliases ?? {}) },
autoImport: options.autoImport ?? true, autoImport: options.autoImport ?? true,
globalComponents: new Set(options.globalComponents ?? []),
}); });
} }
@@ -141,11 +148,18 @@ function importOptionsHash(file: string): string {
mode: "compatible" as const, mode: "compatible" as const,
aliases: { "@": "./app" }, aliases: { "@": "./app" },
autoImport: true, autoImport: true,
globalComponents: new Set<string>(),
}; };
const aliases = Object.fromEntries( const aliases = Object.fromEntries(
Object.entries(options.aliases).sort(([left], [right]) => left.localeCompare(right)), Object.entries(options.aliases).sort(([left], [right]) => left.localeCompare(right)),
); );
return hashPath(JSON.stringify({ ...options, aliases })); return hashPath(
JSON.stringify({
...options,
aliases,
globalComponents: [...options.globalComponents].sort(),
}),
);
} }
/** Island bundles already built this dev session, keyed by resolved source. */ /** Island bundles already built this dev session, keyed by resolved source. */
@@ -493,7 +507,8 @@ function validateConfiguredImports(_source: string, ast: PageAst, file: string):
"Transition", "Transition",
]); ]);
const missing = [...usedComponents].filter( const missing = [...usedComponents].filter(
(name) => !compilerBuiltins.has(name) && !imported.has(name), (name) =>
!compilerBuiltins.has(name) && !options.globalComponents.has(name) && !imported.has(name),
); );
if (ast.layoutIsSymbol && ast.layout && !imported.has(ast.layout)) missing.push(ast.layout); if (ast.layoutIsSymbol && ast.layout && !imported.has(ast.layout)) missing.push(ast.layout);
if (!missing.length) return; if (!missing.length) return;
+19 -2
View File
@@ -1739,6 +1739,15 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// Props may carry `{t:key}` i18n markers — resolve them for the active // Props may carry `{t:key}` i18n markers — resolve them for the active
// language before handing them to the component. // language before handing them to the component.
const props = resolveTProps(parseComponentProps(attrStr!), translate); const props = resolveTProps(parseComponentProps(attrStr!), translate);
// Client control templates retain reactive expressions (for example
// `metrics="{metrics}"`) because their parent state is not available
// while we pre-render the inert branch metadata. Do not feed those
// expression strings into typed component coercion. Rendering with the
// component defaults creates the real child boundary; the bridged prop
// binding synchronizes the live value when the branch is instantiated.
for (const [key, value] of Object.entries(props)) {
if (/^\{[\s\S]*\}$/.test(value) && !/^\{t:/.test(value)) delete props[key];
}
if (normalizedName === "languageswitcher" && deps.i18n) { if (normalizedName === "languageswitcher" && deps.i18n) {
props.locales ??= JSON.stringify( props.locales ??= JSON.stringify(
deps.i18n.langs.map((locale) => ({ deps.i18n.langs.map((locale) => ({
@@ -1777,7 +1786,10 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
(match) => ` ${match[1]}`, (match) => ` ${match[1]}`,
).join(""); ).join("");
const bridged = propBindings const bridged = propBindings
? rendered.replace(/(<[A-Za-z][A-Za-z0-9-]*\b[^>]*\bdata-scope="[^"]*")/, `$1${propBindings}`) ? rendered.replace(
/(<[A-Za-z][A-Za-z0-9-]*\b[^>]*\bdata-scope="[^"]*")/,
`$1${propBindings}`,
)
: rendered; : rendered;
result += await renderComponents(bridged, translate, language, depth + 1); result += await renderComponents(bridged, translate, language, depth + 1);
} catch (err) { } catch (err) {
@@ -2679,7 +2691,12 @@ export function parseComponentProps(attrStr: string): Record<string, string> {
const props: Record<string, string> = {}; const props: Record<string, string> = {};
for (const m of attrStr.matchAll(/([A-Za-z_][\w-]*)(?:="([^"]*)")?/g)) { for (const m of attrStr.matchAll(/([A-Za-z_][\w-]*)(?:="([^"]*)")?/g)) {
const key = m[1]!; const key = m[1]!;
if (key === "data-component") continue; if (
key === "data-component" ||
key.startsWith("data-wrn-bind-") ||
key.startsWith("data-wrn-prop-bind-")
)
continue;
props[key] = decodeHtmlEntities(m[2] ?? ""); props[key] = decodeHtmlEntities(m[2] ?? "");
} }
return props; return props;
@@ -90,6 +90,58 @@ test("a component in an initially hidden client branch is server-prepared for in
expect(branches[0].body).not.toContain("data-component="); expect(branches[0].body).not.toContain("data-component=");
}); });
test("typed components in client branches use defaults until reactive props are available", async () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-component-reactive-branch-"));
roots.push(root);
const app = join(root, "app");
mkdirSync(join(app, "pages"), { recursive: true });
mkdirSync(join(app, "components"), { recursive: true });
writeFileSync(join(app, "pages/index.ts"), "export default () => '';\n");
writeFileSync(join(app, "components/Panel.wrn"), "component Panel { view { <section /> } }\n");
const marker = "[&quot;items&quot;,&quot;{items}&quot;]";
const definition = Buffer.from(
JSON.stringify([
{
cond: "open",
body: `<div data-component="Panel" items="{items}" visible="{true}" data-wrn-prop-bind-0="${marker}"></div>`,
},
]),
"utf8",
).toString("base64");
let observed: Record<string, string> | undefined;
const handlers = createHandlers({
mode: "development",
hmr: false,
router: buildRouter(app),
loadModule: async (file) =>
basename(file) === "Panel.wrn"
? {
render: (props: Record<string, string>) => {
observed = props;
return '<section data-scope="items: [], visible: false" data-wrn-scope="e30="></section>';
},
}
: {
default: () =>
`<template data-wrn-if="${definition}"></template><template data-wrn-control-end></template>`,
},
getMiddleware: async () => [],
assets: { serve: async () => null },
} satisfies RuntimeDeps);
const response = await handlers.fetch(new Request("https://example.test/"), {
upgrade: () => false,
});
const html = await response!.text();
expect(observed).toEqual({});
const encoded = /data-wrn-if="([A-Za-z0-9+/=]+)"/.exec(html)?.[1];
const branches = JSON.parse(Buffer.from(encoded!, "base64").toString("utf8"));
expect(branches[0].body).toContain("<section");
expect(branches[0].body).toContain("data-wrn-prop-bind-0");
expect(branches[0].body).not.toContain("data-component=");
});
test("SSR carries a parent prop binding onto the rendered child scope", async () => { test("SSR carries a parent prop binding onto the rendered child scope", async () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-component-prop-")); const root = mkdtempSync(join(tmpdir(), "wrnexus-component-prop-"));
roots.push(root); roots.push(root);
@@ -97,7 +149,10 @@ test("SSR carries a parent prop binding onto the rendered child scope", async ()
mkdirSync(join(app, "pages"), { recursive: true }); mkdirSync(join(app, "pages"), { recursive: true });
mkdirSync(join(app, "components"), { recursive: true }); mkdirSync(join(app, "components"), { recursive: true });
writeFileSync(join(app, "pages/index.ts"), "export default () => '';\n"); writeFileSync(join(app, "pages/index.ts"), "export default () => '';\n");
writeFileSync(join(app, "components/Child.wrn"), "component Child { props { value = 0 } view { <b>{value}</b> } }\n"); writeFileSync(
join(app, "components/Child.wrn"),
"component Child { props { value = 0 } view { <b>{value}</b> } }\n",
);
const marker = "[&quot;value&quot;,&quot;{count}&quot;]"; const marker = "[&quot;value&quot;,&quot;{count}&quot;]";
const handlers = createHandlers({ const handlers = createHandlers({
mode: "development", mode: "development",
@@ -106,11 +161,16 @@ test("SSR carries a parent prop binding onto the rendered child scope", async ()
loadModule: async (file) => loadModule: async (file) =>
basename(file) === "Child.wrn" basename(file) === "Child.wrn"
? { render: () => '<div data-scope="value: 1" data-wrn-scope="e30="><b>1</b></div>' } ? { render: () => '<div data-scope="value: 1" data-wrn-scope="e30="><b>1</b></div>' }
: { default: () => `<div data-component="Child" value="1" data-wrn-prop-bind-0="${marker}"></div>` }, : {
default: () =>
`<div data-component="Child" value="1" data-wrn-prop-bind-0="${marker}"></div>`,
},
getMiddleware: async () => [], getMiddleware: async () => [],
assets: { serve: async () => null }, assets: { serve: async () => null },
} satisfies RuntimeDeps); } satisfies RuntimeDeps);
const response = await handlers.fetch(new Request("https://example.test/"), { upgrade: () => false }); const response = await handlers.fetch(new Request("https://example.test/"), {
upgrade: () => false,
});
const html = await response!.text(); const html = await response!.text();
expect(html).toContain(`data-wrn-prop-bind-0="${marker}"`); expect(html).toContain(`data-wrn-prop-bind-0="${marker}"`);
expect(html).not.toContain('data-component="Child"'); expect(html).not.toContain('data-component="Child"');
@@ -71,6 +71,13 @@ test("compiler-native reactive elements do not require application imports", ()
expect(() => compileWrnArtifacts(page, 5)).not.toThrow(); expect(() => compileWrnArtifacts(page, 5)).not.toThrow();
}); });
test("globally registered plugin components do not require source imports", () => {
const { root, page } = fixture();
writeFileSync(page, "page Home { view { <SignIn /> } }");
setCompileImportOptions(root, { mode: "explicit", globalComponents: ["SignIn"] });
expect(() => compileWrnArtifacts(page, 6)).not.toThrow();
});
test("component examples in comments do not require imports", () => { test("component examples in comments do not require imports", () => {
const { root, page } = fixture(); const { root, page } = fixture();
writeFileSync( writeFileSync(
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/dev-toolbar", "name": "@wrnexus/dev-toolbar",
"version": "0.8.13", "version": "0.8.16",
"private": true, "private": true,
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
@@ -0,0 +1,41 @@
import { expect, test } from "bun:test";
import { createServerIssue, issueFromError } from "../../src/server/issues.ts";
test("server issues have stable fingerprints and retain actionable error context", () => {
const input = {
ruleId: "server/query",
category: "server" as const,
severity: "warning" as const,
title: "Slow query",
message: "Query exceeded budget",
pathname: "/orders",
source: { file: "app/api/orders.ts", line: 12, column: 4 },
};
const first = createServerIssue(input);
const second = createServerIssue(input);
expect(first.id).not.toBe(second.id);
expect(first.fingerprint).toBe(second.fingerprint);
expect(first.metadata?.pathname).toBe("/orders");
const error = issueFromError(new Error("database offline"), { pathname: "/orders" });
expect(error.severity).toBe("error");
expect(error.message).toBe("database offline");
expect(error.metadata?.stack).toContain("database offline");
expect(error.recommendation).toContain("stack trace");
});
test("non-Error throws are normalized without losing caller context", () => {
const issue = issueFromError(
{ reason: "offline" },
{
ruleId: "server/dependency",
title: "Dependency failed",
pathname: "/health",
source: { file: "app/api/health.ts", line: 4 },
},
);
expect(issue.ruleId).toBe("server/dependency");
expect(issue.title).toBe("Dependency failed");
expect(issue.message).toBe("[object Object]");
expect(issue.fingerprint).toContain("app/api/health.ts:4:0");
});
+1 -1
View File
@@ -117,7 +117,7 @@ export function defineSealedMailCredentials(options: SealedMailCredentialsOption
const secret = process.env[env]; const secret = process.env[env];
if (!secret) if (!secret)
throw new Error(`${env} is not set; it is required to seal mail credentials at rest.`); throw new Error(`${env} is not set; it is required to seal mail credentials at rest.`);
let length = 0; let length: number;
try { try {
length = atob(secret).length; length = atob(secret).length;
} catch { } catch {
+40
View File
@@ -0,0 +1,40 @@
# @wrnexus/metering
Framework-native entitlement catalogs, credit packs, and usage metering for WrNexus applications.
The package keeps policy separate from persistence: applications provide a `MeterStore`, while the metering facade validates unit amounts and reports insufficient balances separately from storage faults. All amounts are positive safe integers except explicit adjustments, which may be positive or negative but never zero.
## Usage
```ts
import { defineEntitlements, defineMeter, definePacks } from "@wrnexus/metering";
const entitlements = defineEntitlements({
plans: () => [
{ code: "free", name: "Free", features: [], allowance: 10 },
{ code: "pro", name: "Pro", features: ["exports"], allowance: 1_000 },
],
subscriptionFor: async (subjectId) => subscriptions.planFor(subjectId),
fallback: "free",
});
const packs = definePacks([
{ code: "starter", units: 100 },
{ code: "plus", units: 500 },
]);
const meter = defineMeter({ store });
if (await entitlements.enabled("user-1", "exports")) {
const result = await meter.reserve("user-1", 1, "export report");
if (!result.ok) console.error(result.reason);
}
```
## API
- `defineEntitlements(options)` resolves a subject's plan, features, and allowance from a server-owned catalog, with an explicit fallback plan.
- `definePacks(entries)` creates an immutable, prototype-safe lookup of purchasable unit packs. Clients select a pack code rather than supplying an amount.
- `defineMeter(options)` exposes `balance`, `reserve`, `grant`, `purchase`, `refund`, and `adjust` operations over an application-provided store.
Meter operations return `{ ok: true }` on success or `{ ok: false, reason, fault? }` on refusal. A result with `fault: true` identifies an underlying storage error; use `onFault` for operational reporting without exposing exception-based control flow to callers.
+1 -1
View File
@@ -38,7 +38,7 @@ function createQueue(options?: QueueOptions): Queue;
#### `QueueOptions` #### `QueueOptions`
| Option | Type | Default | Description | | Option | Type | Default | Description |
| ------------- | ------------------------------------ | ---------- | -------------------------------------------------------- | | ------------- | ------------------------------------ | ---------- | --------------------------------------------------------------------------------------- |
| `maxAttempts` | `number` | `3` | Default max attempts per job before it is dead-lettered. | | `maxAttempts` | `number` | `3` | Default max attempts per job before it is dead-lettered. |
| `backoffMs` | `number` | `1000` | Base retry backoff in ms; doubles per attempt. | | `backoffMs` | `number` | `1000` | Base retry backoff in ms; doubles per attempt. |
| `pollMs` | `number` | `250` | Poll interval used once `start()` is called (ms). | | `pollMs` | `number` | `250` | Poll interval used once `start()` is called (ms). |
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/store", "name": "@wrnexus/store",
"version": "0.8.9", "version": "0.8.12",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
@@ -0,0 +1,62 @@
import { expect, test } from "bun:test";
import { StoreContainer, defineStore } from "../src/index.ts";
test("memory persistence includes only approved fields and migrates old state", async () => {
const original = defineStore({
name: "preferences-migration",
kind: "global" as const,
createSharedState: () => ({ theme: "dark", secret: "initial" }),
persist: { storage: "memory" as const, version: 1, include: ["theme"] },
actions: {
setTheme: {
runtime: "client" as const,
handler: ({ state }: any, theme: string) => void (state.theme = theme),
},
},
});
const first = await new StoreContainer({ runtime: "client" }).use(original);
await first.actions.setTheme("light");
const migrated = defineStore({
...original,
persist: {
storage: "memory" as const,
version: 2,
include: ["theme"],
migrate: (state: any) => ({ ...state, theme: `${state.theme}-v2` }),
},
});
const second = await new StoreContainer({ runtime: "client" }).use(migrated);
expect(second.state.theme).toBe("light-v2");
expect(second.state.secret).toBe("initial");
});
test("invalid persisted state fails closed to the store defaults", async () => {
const name = `preferences-validation-${crypto.randomUUID()}`;
const firstDefinition = defineStore({
name,
kind: "global" as const,
createSharedState: () => ({ theme: "dark" }),
persist: { storage: "memory" as const, version: 1, include: ["theme"] },
actions: {
setTheme: {
runtime: "client" as const,
handler: ({ state }: any, theme: string) => void (state.theme = theme),
},
},
});
const first = await new StoreContainer({ runtime: "client" }).use(firstDefinition);
await first.actions.setTheme("corrupt");
const validated = defineStore({
...firstDefinition,
persist: {
storage: "memory" as const,
version: 1,
include: ["theme"],
validate: (value: any) => (value?.theme === "light" ? value : null),
},
});
const second = await new StoreContainer({ runtime: "client" }).use(validated);
expect(second.state.theme).toBe("dark");
});
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/syntax", "name": "@wrnexus/syntax",
"version": "0.8.13", "version": "0.8.18",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+34
View File
@@ -767,6 +767,28 @@ function expandStructuredStateDeclarations(lines, unit) {
}); });
} }
function formatStructuredPropDeclaration(value, unit, depth, printWidth) {
const match = /^([A-Za-z_$][\w$]*\??\s*(?::\s*[^=]+?)?\s*=\s*)([\[{][\s\S]*)$/.exec(
value,
);
if (!match) return null;
try {
const parsed = JSON.parse(match[2].trim());
const compact = JSON.stringify(parsed);
const indentation = unit.repeat(depth);
if (indentation.length + match[1].length + compact.length <= printWidth) return null;
const jsonLines = JSON.stringify(parsed, null, unit).split("\n");
return [
`${indentation}${match[1]}${jsonLines[0]}`,
...jsonLines.slice(1).map((jsonLine) => `${indentation}${jsonLine}`),
];
} catch {
return null;
}
}
function formatWrnPass(source: string, options: FormatWrnOptions = {}): string { function formatWrnPass(source: string, options: FormatWrnOptions = {}): string {
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4); const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
@@ -844,6 +866,18 @@ function formatWrnPass(source: string, options: FormatWrnOptions = {}): string {
continue; continue;
} }
const structuredProp = formatStructuredPropDeclaration(
value,
unit,
codeDepth + htmlDepth + controlDepth,
printWidth,
);
if (structuredProp) {
output.push(...structuredProp);
index += 1;
continue;
}
const leadingClosingBraces = countLeadingClosingBraces(value); const leadingClosingBraces = countLeadingClosingBraces(value);
const closesControlBlock = isControlBlockClose(value); const closesControlBlock = isControlBlockClose(value);
+2
View File
@@ -2,6 +2,8 @@ export { Lexer, LexError, skipLiteralOrComment } from "./tokenizer.ts";
export { formatWrn } from "./formatter.ts"; export { formatWrn } from "./formatter.ts";
export type { FormatWrnOptions } from "./formatter.ts"; export type { FormatWrnOptions } from "./formatter.ts";
export { parse, parseHtmlView, ParseError, VOID_ELEMENTS } from "./parser.ts"; export { parse, parseHtmlView, ParseError, VOID_ELEMENTS } from "./parser.ts";
export { extractInterpolations } from "./interpolation.ts";
export type { InterpolationSegment } from "./interpolation.ts";
export type { export type {
ActionBlock, ActionBlock,
ApiBlock, ApiBlock,
+39
View File
@@ -0,0 +1,39 @@
export interface InterpolationSegment {
expression: string;
start: number;
end: number;
}
/** Extract top-level `{...}` expressions while preserving nested object/array literals. */
export function extractInterpolations(value: string): InterpolationSegment[] {
const segments: InterpolationSegment[] = [];
let index = 0;
while (index < value.length) {
if (value[index] !== "{") {
index++;
continue;
}
const start = index++;
let depth = 1;
let quote: string | null = null;
while (index < value.length && depth > 0) {
const char = value[index]!;
if (quote) {
if (char === "\\" && index + 1 < value.length) index += 2;
else {
if (char === quote) quote = null;
index++;
}
continue;
}
if (char === '"' || char === "'" || char === "`") quote = char;
else if (char === "{") depth++;
else if (char === "}") depth--;
index++;
}
if (depth === 0) {
segments.push({ expression: value.slice(start + 1, index - 1).trim(), start, end: index });
}
}
return segments;
}
@@ -0,0 +1,35 @@
import { expect, test } from "bun:test";
import { hasRequestSection, parseApiEntries, parseApiSections } from "../src/api-sections.ts";
test("api section scanning ignores keywords in literals, comments, and nested objects", () => {
const source = `
// request { body { forged: string } }
response {
return { label: "request { not a section }", nested: { body: true } }
}
`;
expect(hasRequestSection(source)).toBe(false);
expect(parseApiSections(source)?.response).toContain("nested");
});
test("api entries normalize methods and reject malformed request fields", () => {
expect(
parseApiEntries(`lookup get /users/:id {
response { return data }
}`)[0],
).toMatchObject({
name: "lookup",
method: "GET",
path: "/users/:id",
});
expect(() =>
parseApiSections("request { body { missingType } } response { return data }"),
).toThrow('Expected "name: type"');
});
test("api entries reject truncated blocks and bare response bodies", () => {
expect(() => parseApiEntries("lookup GET /users { response { return data }")).toThrow();
expect(() => parseApiEntries("lookup GET /users { return data }")).toThrow(
'declare a "response { }" section',
);
});
+15
View File
@@ -83,3 +83,18 @@ test("preserves balanced compact sibling markup without indentation drift", () =
expect(formatted).toContain('<div class="strip"><span>Page</span><b>→</b><span>API</span></div>'); expect(formatted).toContain('<div class="strip"><span>Page</span><b>→</b><span>API</span></div>');
expect(formatWrn(formatted, { tabSize: 2, printWidth: 80 })).toBe(formatted); expect(formatWrn(formatted, { tabSize: 2, printWidth: 80 })).toBe(formatted);
}); });
test("formats large typed JSON prop defaults and remains idempotent", () => {
const source = `component Catalog {
props {
categories: unknown[] = [{"label":"Platform & Core","value":"platform","apps":[{"label":"WRNexus Home / Business Command Center","href":"/products/home","number":"01"},{"label":"SSO & Account","href":"/products/sso","number":"02"}]},{"label":"Sales & Customer","value":"sales","apps":[{"label":"CRM","href":"/products/crm","number":"11"}]}]
}
view { <div></div> }
}`;
const formatted = formatWrn(source, options);
expect(formatted).toContain(`categories: unknown[] = [\n {\n "label": "Platform & Core"`);
expect(formatted).toContain(`"apps": [\n {`);
expect(formatWrn(formatted, options)).toBe(formatted);
expect(() => parse(formatted)).not.toThrow();
});
+12 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test"; import { expect, test } from "bun:test";
import { diagnose, formatDiagnostic, parse } from "../src/index.ts"; import { diagnose, extractInterpolations, formatDiagnostic, parse } from "../src/index.ts";
test("parses explicit rendering modes and the never hydration alias", () => { test("parses explicit rendering modes and the never hydration alias", () => {
const ast = parse(`page Marketing { const ast = parse(`page Marketing {
@@ -337,3 +337,14 @@ test("a block comment inside props is still refused with its own message", () =>
`; `;
expect(() => parse(source)).toThrow("Block comments are not allowed inside props"); expect(() => parse(source)).toThrow("Block comments are not allowed inside props");
}); });
test("extracts nested object and array interpolation as one expression", () => {
const value = `prefix {[{ label: 'One', meta: { count: 1 } }, { label: "Two" }]} suffix`;
expect(extractInterpolations(value)).toEqual([
{
expression: `[{ label: 'One', meta: { count: 1 } }, { label: "Two" }]`,
start: 7,
end: value.length - 7,
},
]);
});
+1 -5
View File
@@ -206,8 +206,4 @@ export type {
export { withDatabaseRollback, createFactory, captureBrowserArtifacts } from "./platform.ts"; export { withDatabaseRollback, createFactory, captureBrowserArtifacts } from "./platform.ts";
export type { TransactionalDatabase, BrowserArtifactPage } from "./platform.ts"; export type { TransactionalDatabase, BrowserArtifactPage } from "./platform.ts";
export { detectMutations } from "./mutation.ts"; export { detectMutations } from "./mutation.ts";
export type { export type { MutationCase, MutationReport, DetectMutationsOptions } from "./mutation.ts";
MutationCase,
MutationReport,
DetectMutationsOptions,
} from "./mutation.ts";
+3 -4
View File
@@ -43,10 +43,9 @@ export async function detectMutations<T, O>(
(same ? survived : killed).push(mutation.name); (same ? survived : killed).push(mutation.name);
} }
if (survived.length) { if (survived.length) {
throw Object.assign( throw Object.assign(new Error(`WRN-MUTATION-SURVIVED: ${survived.join(", ")}`), {
new Error(`WRN-MUTATION-SURVIVED: ${survived.join(", ")}`), report: { baseline, killed, survived },
{ report: { baseline, killed, survived } }, });
);
} }
return { baseline, killed, survived }; return { baseline, killed, survived };
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/typecheck", "name": "@wrnexus/typecheck",
"version": "0.8.13", "version": "0.8.16",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+46 -6
View File
@@ -3,6 +3,7 @@ import { dirname, join, normalize, resolve } from "node:path";
import ts from "typescript"; import ts from "typescript";
import { import {
containsReadonlyPropMutation, containsReadonlyPropMutation,
extractInterpolations,
inferredRuntimeType, inferredRuntimeType,
parse, parse,
runtimeTypeOf, runtimeTypeOf,
@@ -139,7 +140,7 @@ function functionDeclaration(fn: RuntimeFunctionDecl): string {
const params = fn.parameters const params = fn.parameters
.map( .map(
(param) => (param) =>
`${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : param.name === "event" ? ": CustomEvent<any> & { target: HTMLElement }" : ": any"}${param.default ? ` = ${param.default}` : ""}`, `${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : param.name === "event" ? ": Event & { target: HTMLElement }" : ": any"}${param.default ? ` = ${param.default}` : ""}`,
) )
.join(", "); .join(", ");
// An omitted WRN parameter type follows JavaScript semantics. Emitting it as // An omitted WRN parameter type follows JavaScript semantics. Emitting it as
@@ -214,6 +215,31 @@ function safeBindingName(name: string): boolean {
return /^[A-Za-z_$][\w$]*$/.test(name) && !RESERVED_BINDING_NAMES.has(name); return /^[A-Za-z_$][\w$]*$/.test(name) && !RESERVED_BINDING_NAMES.has(name);
} }
function safeViewExpression(expression: string, reservedProps: ReadonlySet<string>): string {
if (!reservedProps.size) return expression;
const scanner = ts.createScanner(
ts.ScriptTarget.Latest,
false,
ts.LanguageVariant.Standard,
expression,
);
let result = "";
let copied = 0;
for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
const start = scanner.getTokenPos();
const end = scanner.getTextPos();
const text = scanner.getTokenText();
if (!reservedProps.has(text)) continue;
const previous = expression.slice(0, start).trimEnd().at(-1);
const following = expression.slice(end).trimStart().at(0);
// Property access and object-literal keys are already valid TypeScript.
if (previous === "." || following === ":") continue;
result += expression.slice(copied, start) + `props.${text}`;
copied = end;
}
return result + expression.slice(copied);
}
export function virtualTypeScriptModule( export function virtualTypeScriptModule(
source: string, source: string,
filePath = "component.wrn", filePath = "component.wrn",
@@ -307,6 +333,9 @@ export function virtualTypeScriptModule(
if (ast.dataApis.length) append(`declare const api: { ${apiType} };`); if (ast.dataApis.length) append(`declare const api: { ${apiType} };`);
append(`declare const props: Readonly<${ast.name}Props>;`); append(`declare const props: Readonly<${ast.name}Props>;`);
append("declare const refs: Record<string, Element | null>;"); append("declare const refs: Record<string, Element | null>;");
append(
"declare const toast: ((message: string, options?: Record<string, unknown>) => unknown) & { success(message: string, options?: Record<string, unknown>): unknown; error(message: string, options?: Record<string, unknown>): unknown; danger(message: string, options?: Record<string, unknown>): unknown; warning(message: string, options?: Record<string, unknown>): unknown; info(message: string, options?: Record<string, unknown>): unknown; dismiss(id: string | number): void; clear(): void };",
);
append( append(
"declare function useFetch(path: string, method?: string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown }, input?: unknown): Promise<any>;", "declare function useFetch(path: string, method?: string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown }, input?: unknown): Promise<any>;",
); );
@@ -348,21 +377,29 @@ export function virtualTypeScriptModule(
// misspelled state/function name cannot silently become `undefined` at // misspelled state/function name cannot silently become `undefined` at
// runtime. Each/handler locals mirror the browser evaluator's bindings. // runtime. Each/handler locals mirror the browser evaluator's bindings.
let bindingIndex = 0; let bindingIndex = 0;
const reservedProps = new Set(
ast.props.map((prop) => prop.name).filter((name) => !safeBindingName(name)),
);
const appendViewBindings = (nodes: ViewNode[], locals: Set<string>): void => { const appendViewBindings = (nodes: ViewNode[], locals: Set<string>): void => {
for (const node of nodes) { for (const node of nodes) {
if (node.type === "element") { if (node.type === "element") {
for (const attr of node.attrs) { for (const attr of node.attrs) {
const expressions = attr.event const expressions = attr.event
? [attr.value] ? [attr.value]
: [...attr.value.matchAll(/\{([^{}]+)\}/g)].map((match) => match[1]!.trim()); : extractInterpolations(attr.value).map((segment) => segment.expression);
for (const expression of expressions) { for (const expression of expressions) {
if (!expression.trim()) continue; if (!expression.trim()) continue;
const declarations = [ const declarations = [
...[...locals].map((name) => `declare const ${name}: any;`), ...[...locals].map((name) => `declare const ${name}: any;`),
...(attr.event ? ["declare const payload: any;", "declare const event: Event;"] : []), ...(attr.event
? [
"declare const payload: any;",
"declare const event: Event & { target: HTMLElement };",
]
: []),
].join(" "); ].join(" ");
append( append(
`namespace __wrn_view_${bindingIndex++} { ${declarations} void (${expression}); }`, `namespace __wrn_view_${bindingIndex++} { ${declarations} void (${safeViewExpression(expression, reservedProps)}); }`,
attr.value, attr.value,
); );
} }
@@ -372,13 +409,16 @@ export function virtualTypeScriptModule(
for (const branch of node.branches) { for (const branch of node.branches) {
if (branch.cond) if (branch.cond)
append( append(
`namespace __wrn_view_${bindingIndex++} { void (${branch.cond}); }`, `namespace __wrn_view_${bindingIndex++} { void (${safeViewExpression(branch.cond, reservedProps)}); }`,
branch.cond, branch.cond,
); );
appendViewBindings(branch.body, locals); appendViewBindings(branch.body, locals);
} }
} else if (node.type === "each") { } else if (node.type === "each") {
append(`namespace __wrn_view_${bindingIndex++} { void (${node.list}); }`, node.list); append(
`namespace __wrn_view_${bindingIndex++} { void (${safeViewExpression(node.list, reservedProps)}); }`,
node.list,
);
const nested = new Set(locals); const nested = new Set(locals);
nested.add(node.item); nested.add(node.item);
if (node.index) nested.add(node.index); if (node.index) nested.add(node.index);
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test"; import { expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { mkdirSync, mkdtempSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { checkWrnSource } from "../src/index.ts"; import { checkWrnSource } from "../src/index.ts";
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test"; import { expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { checkWrnSource } from "../src/index.ts"; import { checkWrnSource } from "../src/index.ts";
@@ -82,6 +82,23 @@ page Home {
expect(diagnostics.some((item) => item.code === "WRN-COMPONENT-PROP-TYPE")).toBe(false); expect(diagnostics.some((item) => item.code === "WRN-COMPONENT-PROP-TYPE")).toBe(false);
expect(diagnostics.some((item) => item.code === "WRN-TYPE-1389")).toBe(false); expect(diagnostics.some((item) => item.code === "WRN-TYPE-1389")).toBe(false);
expect(diagnostics.some((item) => item.code === "WRN-TYPE-1005")).toBe(false); expect(diagnostics.some((item) => item.code === "WRN-TYPE-1005")).toBe(false);
expect(
checkWrnSource(readFileSync(componentPath, "utf8"), { appRoot: root, filePath: componentPath }),
).toEqual([]);
}, 15_000);
test("implicit DOM events satisfy untyped event handler parameters", () => {
const root = fixture();
const filePath = join(root, "app", "components", "Submit.wrn");
const diagnostics = checkWrnSource(
`component Submit {
outputs { submit(payload: { event: Event }) }
functions { client function submitForm(event) { output.submit({ event }) } }
view { <form @submit="submitForm(event)"></form> }
}`,
{ appRoot: root, filePath },
);
expect(diagnostics).toEqual([]);
}, 15_000); }, 15_000);
test("does not treat UI component imports as JavaScript exports", () => { test("does not treat UI component imports as JavaScript exports", () => {
+29
View File
@@ -112,3 +112,32 @@ test("accepts handler payload, loop locals, and the public useFetch helper", ()
); );
expect(diagnostics.filter((diagnostic) => diagnostic.code === "WRN-TYPE-2304")).toEqual([]); expect(diagnostics.filter((diagnostic) => diagnostic.code === "WRN-TYPE-2304")).toEqual([]);
}); });
test("accepts multiline array props containing nested object literals", () => {
const root = app();
const diagnostics = checkWrnSource(
`page Contact {
view {
<div data-component="StatsBar" items="{[
{ label: 'Product catalog', value: '81', suffix: ' apps' },
{ label: 'Starting point', value: 'One workflow' }
]}"></div>
}
}`,
{ appRoot: root, filePath: join(root, "app", "pages", "contact.wrn") },
);
expect(diagnostics.filter((diagnostic) => diagnostic.code.startsWith("WRN-TYPE-"))).toEqual([]);
});
test("provides the toast API to view event expressions", () => {
const root = app();
const diagnostics = checkWrnSource(
`page Contact {
view {
<form @wrn:success="toast.success('Sent', { title: 'Request sent' })"></form>
}
}`,
{ appRoot: root, filePath: join(root, "app", "pages", "contact.wrn") },
);
expect(diagnostics.filter((diagnostic) => diagnostic.code.startsWith("WRN-TYPE-"))).toEqual([]);
});
+15 -15
View File
@@ -45,7 +45,7 @@ Secure multi-cell PIN and verification-code input with regex and paste support.
Theme-aware, responsive strong password component. Theme-aware, responsive strong password component.
- Mount: `data-component="StrongPassword"` - Mount: `data-component="StrongPassword"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `class: string = ""` - Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `showLabel: string = "Show password"`, `hideLabel: string = "Hide password"`, `class: string = ""`
- Slots: None - Slots: None
- Outputs: `input({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `change({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `strength({ value: string; score: number; maximumScore: number; percent: number; level: string })` - Outputs: `input({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `change({ value: string; score: number; maximumScore: number; percent: number; level: string })`, `strength({ value: string; score: number; maximumScore: number; percent: number; level: string })`
@@ -146,7 +146,7 @@ Theme-aware, responsive button group component.
Group related content in a responsive themed surface with title, description, content, and supporting slots. Group related content in a responsive themed surface with title, description, content, and supporting slots.
- Mount: `data-component="Card"` - Mount: `data-component="Card"`
- Props: `title: string = "Card title"`, `subtitle: string = ""`, `description: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""` - Props: `title: string = ""`, `icon: string = ""`, `titleKey: string = ""`, `subtitle: string = ""`, `subtitleKey: string = ""`, `description: string = ""`, `descriptionKey: string = ""`, `header: string = ""`, `footer: string = ""`, `imageSrc: string = ""`, `imageAlt: string = ""`, `imagePosition: string = "top"`, `actionLabel: string = ""`, `actionLabelKey: string = ""`, `actionHref: string = ""`, `headerActions: unknown[] = []`, `navigation: unknown[] = []`, `activeNav: string = ""`, `mobileNavigation: boolean = false`, `alertTitle: string = ""`, `alertDescription: string = ""`, `empty: boolean = false`, `emptyTitle: string = "No data to show"`, `emptyIcon: string = "icon-[lucide--inbox]"`, `items: unknown[] = []`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `layout: string = "vertical"`, `align: string = "left"`, `hover: string = "none"`, `radius: string = "default"`, `shadow: string = "default"`, `equalHeight: boolean = false`, `scrollable: boolean = false`, `maxHeight: string = "18rem"`, `dismissible: boolean = false`, `ariaLabel: string = ""`, `class: string = ""`
- Slots: `default` - Slots: `default`
- Outputs: `click({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `navigate({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `load({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)` - Outputs: `click({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `navigate({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `load({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)`
@@ -337,7 +337,7 @@ Reusable portal dashboard component.
Reusable preference switcher component. Reusable preference switcher component.
- Mount: `data-component="PreferenceSwitcher"` - Mount: `data-component="PreferenceSwitcher"`
- Props: `size: string = "default"`, `color: string = "primary"`, `themeLabel: string = "Theme"`, `colorLabel: string = "Accent color"`, `languageLabel: string = "Language"`, `languages: string = [`, `colors: string = [`, `compact: boolean = true`, `class: string = ""` - Props: `size: string = "default"`, `color: string = "primary"`, `themeLabel: string = "Theme"`, `colorLabel: string = "Accent color"`, `languageLabel: string = "Language"`, `languages: string = [`, `colors: string = [`, `compact: boolean = true`, `iconOnly: boolean = true`, `themeIcon: string = "icon-[lucide--sun-moon]"`, `colorIcon: string = "icon-[lucide--palette]"`, `languageIcon: string = "icon-[lucide--languages]"`, `panelPlacement: string = "bottom-end"`, `mobilePanel: string = "dropdown"`, `showCurrentLanguage: boolean = true`, `class: string = ""`
- Slots: None - Slots: None
- Outputs: `theme({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `color({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `language({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])` - Outputs: `theme({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `color({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `language({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
@@ -375,7 +375,7 @@ Arrange operational metrics, KPIs, public statistics, or service indicators in a
Present a compact responsive strip of key facts, counts, performance indicators, or trust signals. Present a compact responsive strip of key facts, counts, performance indicators, or trust signals.
- Mount: `data-component="StatsBar"` - Mount: `data-component="StatsBar"`
- Props: `items: unknown[] = []`, `columns: number = 4`, `compact: boolean = true`, `dividers: boolean = true`, `icons: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "raised"`, `maxWidth: string = "xl"`, `class: string = ""` - Props: `items: unknown[] = []`, `columns: number = 4`, `compact: boolean = true`, `dividers: boolean = true`, `icons: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "raised"`, `maxWidth: string = "xl"`, `radius: string = "default"`, `shadow: string = "default"`, `gutter: string = "md"`, `outerSpacing: string = "none"`, `mobileColumns: number = 1`, `class: string = ""`
- Slots: None - Slots: None
- Outputs: None - Outputs: None
@@ -606,7 +606,7 @@ Arrange feature cards, services, solutions, or benefits in a responsive equal-he
Render structured responsive footer navigation, pre and post content, copyright content, links, and public events. Render structured responsive footer navigation, pre and post content, copyright content, links, and public events.
- Mount: `data-component="Footer"` - Mount: `data-component="Footer"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `copyright: string = ""`, `class: string = ""` - Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Footer navigation"`, `items: unknown[] = []`, `columns: number = 3`, `maxWidth: string = "compact"`, `mobileColumns: number = 2`, `mobileCopyrightAlign: string = "center"`, `copyright: string = ""`, `copyrightKey: string = ""`, `translationPrefix: string = ""`, `class: string = ""`
- Slots: `pre-footer`, `post-footer`, `copyright-left`, `copyright-right` - Slots: `pre-footer`, `post-footer`, `copyright-left`, `copyright-right`
- Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })` - Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `action({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
@@ -698,7 +698,7 @@ Apply consistent readable responsive typography, widths, columns, spacing, and e
Publish a responsive notice with badge, icon, supporting copy, action, dismiss behavior, and width controls. Publish a responsive notice with badge, icon, supporting copy, action, dismiss behavior, and width controls.
- Mount: `data-component="AnnouncementBar"` - Mount: `data-component="AnnouncementBar"`
- Props: `badge: string = ""`, `badgeIcon: string = ""`, `message: string = "Announcement"`, `description: string = ""`, `icon: string = "icon-[lucide--megaphone]"`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = ""`, `dismissible: boolean = false`, `dismissLabel: string = "Dismiss announcement"`, `sticky: boolean = false`, `compact: boolean = false`, `size: string = "default"`, `width: string = "default"`, `color: string = "primary"`, `variant: string = "soft"`, `role: string = "status"`, `live: string = "polite"`, `class: string = ""` - Props: `badge: string = ""`, `badgeIcon: string = ""`, `message: string = "Announcement"`, `description: string = ""`, `icon: string = "icon-[lucide--megaphone]"`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = ""`, `dismissible: boolean = false`, `dismissLabel: string = "Dismiss announcement"`, `sticky: boolean = false`, `compact: boolean = false`, `flush: boolean = false`, `mobileCompact: boolean = false`, `mobileMessageLines: number = 2`, `hideBadgeOnMobile: boolean = false`, `hideDescriptionOnMobile: boolean = false`, `mobileActionSize: string = "default"`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `size: string = "default"`, `width: string = "default"`, `color: string = "primary"`, `variant: string = "soft"`, `role: string = "status"`, `live: string = "polite"`, `class: string = ""`
- Slots: None - Slots: None
- Outputs: `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })` - Outputs: `dismiss({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
@@ -707,7 +707,7 @@ Publish a responsive notice with badge, icon, supporting copy, action, dismiss b
Close a page or major section with conversion-focused copy, actions, and optional supporting visual content. Close a page or major section with conversion-focused copy, actions, and optional supporting visual content.
- Mount: `data-component="CTASection"` - Mount: `data-component="CTASection"`
- Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""` - Props: `eyebrow: string = ""`, `title: string = "Ready to get started?"`, `description: string = ""`, `icon: string = ""`, `align: string = "center"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "solid"`, `spacing: string = "none"`, `radius: string = "default"`, `titleMaxWidth: string = ""`, `contentPadding: string = "default"`, `mobilePadding: string = "default"`, `mobileActions: string = "stack"`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualPosition: string = "right"`, `maxWidth: string = "xl"`, `fullBleed: boolean = false`, `class: string = ""`
- Slots: `default`, `actions`, `visual`, `footer` - Slots: `default`, `actions`, `visual`, `footer`
- Outputs: None - Outputs: None
@@ -725,7 +725,7 @@ Present one linked feature or service with media, icon, badge, description, and
Present a compact feature or benefit with a styled icon, title, description, badge, and optional link. Present a compact feature or benefit with a styled icon, title, description, badge, and optional link.
- Mount: `data-component="FeatureIconCard"` - Mount: `data-component="FeatureIconCard"`
- Props: `icon: string = "icon-[lucide--sparkles]"`, `iconSize: string = "md"`, `iconVariant: string = "soft"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Explore"`, `badge: string = ""`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `align: string = "left"`, `hover: string = "lift"`, `class: string = ""` - Props: `icon: string = "icon-[lucide--sparkles]"`, `iconSize: string = "md"`, `iconVariant: string = "soft"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Explore"`, `badge: string = ""`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `align: string = "left"`, `hover: string = "lift"`, `radius: string = "default"`, `shadow: string = "default"`, `clickableSurface: boolean = false`, `descriptionTone: string = "default"`, `class: string = ""`
- Slots: `default`, `footer` - Slots: `default`, `footer`
- Outputs: None - Outputs: None
@@ -770,7 +770,7 @@ Introduce an internal or public page with breadcrumbs, icon, title, description,
Build a responsive two-column introduction balancing descriptive content with a visual, image, or structured data panel. Build a responsive two-column introduction balancing descriptive content with a visual, image, or structured data panel.
- Mount: `data-component="SplitHero"` - Mount: `data-component="SplitHero"`
- Props: `eyebrow: string = ""`, `eyebrowIcon: string = "icon-[lucide--sparkles]"`, `title: string = "A better digital experience"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `visualPosition: string = "right"`, `reverse: boolean = false`, `ratio: string = "balanced"`, `align: string = "left"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `maxWidth: string = "xl"`, `fullBleed: boolean = true`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualEyebrow: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualStyle: string = "panel"`, `trustItems: unknown[] = []`, `class: string = ""` - Props: `eyebrow: string = ""`, `eyebrowIcon: string = "icon-[lucide--sparkles]"`, `title: string = "A better digital experience"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `primaryIcon: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `secondaryIcon: string = ""`, `visualPosition: string = "right"`, `reverse: boolean = false`, `ratio: string = "balanced"`, `align: string = "left"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "default"`, `maxWidth: string = "xl"`, `fullBleed: boolean = true`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `titleSize: string = "default"`, `titleMaxWidth: string = ""`, `descriptionWeight: string = "default"`, `mobileTitleSize: string = "default"`, `mobileStack: boolean = true`, `backgroundImage: string = ""`, `visualImage: string = ""`, `visualAlt: string = ""`, `visualIcon: string = ""`, `visualEyebrow: string = ""`, `visualTitle: string = ""`, `visualDescription: string = ""`, `visualItems: unknown[] = []`, `visualStyle: string = "panel"`, `trustItems: unknown[] = []`, `class: string = ""`
- Slots: `default`, `actions`, `trust`, `visual` - Slots: `default`, `actions`, `trust`, `visual`
- Outputs: None - Outputs: None
@@ -799,7 +799,7 @@ Provide a responsive floating control that returns long pages to the top and can
Show responsive hierarchical navigation with home support, separators, current-page state, sizes, and selection events. Show responsive hierarchical navigation with home support, separators, current-page state, sizes, and selection events.
- Mount: `data-component="Breadcrumb"` - Mount: `data-component="Breadcrumb"`
- Props: `label: string = "Breadcrumb"`, `items: unknown[] = []`, `active: string = ""`, `separator: string = "chevron"`, `showHome: boolean = false`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `homeIcon: string = "icon-[lucide--house]"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "minimal"`, `class: string = ""` - Props: `label: string = "Breadcrumb"`, `items: unknown[] = []`, `active: string = ""`, `activeIcon: string = "icon-[lucide--file-text]"`, `separator: string = "chevron"`, `showHome: boolean = false`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `homeIcon: string = "icon-[lucide--house]"`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "minimal"`, `class: string = ""`
- Slots: None - Slots: None
- Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)` - Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
@@ -808,9 +808,9 @@ Show responsive hierarchical navigation with home support, separators, current-p
Theme-aware, responsive mega menu component. Theme-aware, responsive mega menu component.
- Mount: `data-component="MegaMenu"` - Mount: `data-component="MegaMenu"`
- Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Menu"`, `icon: string = ""`, `columns: unknown[] = []`, `footer: string = ""`, `defaultOpen: boolean = false`, `class: string = ""` - Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Menu"`, `icon: string = ""`, `columns: unknown[] = []`, `rail: unknown[] = []`, `featured: Record<string, unknown> = {}`, `variant: string = "default"`, `density: string = "default"`, `panelWidth: string = "xl"`, `panelAlign: string = "start"`, `columnCount: number = 0`, `fullWidth: boolean = false`, `openOnHover: boolean = true`, `activeCategory: string = ""`, `actionLabel: string = ""`, `actionHref: string = ""`, `actionIcon: string = "icon-[lucide--arrow-right]"`, `footer: string = ""`, `defaultOpen: boolean = false`, `class: string = ""`
- Slots: `default` - Slots: `default`
- Outputs: `open({ sourceEvent: Event })`, `close({ reason: string })`, `select({ item: object; value: string })` - Outputs: `open({ sourceEvent: Event })`, `close({ reason: string })`, `select({ item: object; value: string })`, `category({ item: object; value: string; index: number })`
### Nav ### Nav
@@ -826,8 +826,8 @@ Theme-aware, responsive nav component.
Theme-aware, responsive navbar component. Theme-aware, responsive navbar component.
- Mount: `data-component="Navbar"` - Mount: `data-component="Navbar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record<string, unknown> = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `class: string = ""` - Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Primary navigation"`, `topbarLabel: string = "Utility navigation"`, `brand: Record<string, unknown> = {}`, `items: unknown[] = []`, `actions: unknown[] = []`, `active: string = ""`, `sticky: boolean = false`, `floating: boolean = false`, `startFloating: string = "0px"`, `openOnHover: boolean = false`, `maxWidth: string = "full"`, `mobileLabel: string = "Toggle navigation"`, `collapseAt: string = "900px"`, `mobilePanel: string = "content"`, `mobileUtilitiesPosition: string = "header"`, `gutter: string = "md"`, `contentGutter: string = "md"`, `flushMobile: boolean = false`, `translationPrefix: string = ""`, `class: string = ""`
- Slots: `topbar`, `actions` - Slots: `topbar`, `actions`, `utilities`
- Outputs: `toggle({ open: boolean })`, `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ item: string | number | boolean | null | object; value: string | number | boolean; level: string })`, `action({ item: string | number | boolean | null | object; value: string | number | boolean })` - Outputs: `toggle({ open: boolean })`, `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ item: string | number | boolean | null | object; value: string | number | boolean; level: string })`, `action({ item: string | number | boolean | null | object; value: string | number | boolean })`
### Pagination ### Pagination
@@ -844,7 +844,7 @@ Theme-aware, responsive pagination component.
Theme-aware, responsive scrollspy component. Theme-aware, responsive scrollspy component.
- Mount: `data-component="Scrollspy"` - Mount: `data-component="Scrollspy"`
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `label: string = "On this page"`, `heading: string = ""`, `class: string = ""` - Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `label: string = "On this page"`, `heading: string = ""`, `sticky: boolean = false`, `topOffset: string = "6rem"`, `sectionOffset: string = "7rem"`, `collapseAt: string = "800px"`, `class: string = ""`
- Slots: `default` - Slots: `default`
- Outputs: `change({ href: string; label: string })` - Outputs: `change({ href: string; label: string })`
+56
View File
@@ -90,6 +90,62 @@ the component directory at runtime.
For the full catalog, see [`COMPONENTS.md`](./COMPONENTS.md). The machine-readable For the full catalog, see [`COMPONENTS.md`](./COMPONENTS.md). The machine-readable
equivalent is exported as `@wrnexus/ui/component-reference.json`. equivalent is exported as `@wrnexus/ui/component-reference.json`.
### Typed Navbar items
Keep navigation data in a shared TypeScript module and validate it with the
package contract. `NavbarItem` is a discriminated union of `link`, `dropdown`,
and `mega`; a mega item accepts every `MegaMenu` layout option.
```ts
import type { NavbarItem } from "@wrnexus/ui";
export const navigation = [
{ type: "link", label: "Pricing", href: "/pricing" },
{
type: "dropdown",
label: "Company",
items: [
{ label: "About", href: "/about" },
{ label: "Contact", href: "/contact" },
],
},
{
type: "mega",
label: "Products",
mega: {
variant: "icon-grid",
panelWidth: "2xl",
columnCount: 3,
rail: [{ label: "All products", href: "/products" }],
columns: [
{
heading: "Work",
items: [
{
label: "Projects",
href: "/products/projects",
icon: "icon-[lucide--folders]",
description: "Plan and deliver work",
badge: "New",
},
],
},
],
featured: {
title: "What's new",
actionLabel: "Explore",
actionHref: "/new",
},
actionLabel: "View all products",
actionHref: "/products",
},
},
] satisfies NavbarItem[];
```
Importing from `@wrnexus/ui/navigation` is also supported. Existing untyped
Navbar data using `children` remains accepted at runtime for migration.
## API ## API
The JS module (`@wrnexus/ui`) exposes five helpers used by the build tooling to The JS module (`@wrnexus/ui`) exposes five helpers used by the build tooling to
+560 -3
View File
@@ -806,6 +806,69 @@
"default": "false", "default": "false",
"options": [] "options": []
}, },
{
"name": "flush",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "mobileCompact",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "mobileMessageLines",
"type": "number",
"required": false,
"default": "2",
"options": []
},
{
"name": "hideBadgeOnMobile",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "hideDescriptionOnMobile",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "mobileActionSize",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "gutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "contentGutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "flushMobile",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{ {
"name": "size", "name": "size",
"type": "string", "type": "string",
@@ -2180,6 +2243,48 @@
"default": "\"solid\"", "default": "\"solid\"",
"options": [] "options": []
}, },
{
"name": "spacing",
"type": "string",
"required": false,
"default": "\"none\"",
"options": []
},
{
"name": "radius",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "titleMaxWidth",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "contentPadding",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "mobilePadding",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "mobileActions",
"type": "string",
"required": false,
"default": "\"stack\"",
"options": []
},
{ {
"name": "primaryLabel", "name": "primaryLabel",
"type": "string", "type": "string",
@@ -2315,7 +2420,21 @@
"name": "title", "name": "title",
"type": "string", "type": "string",
"required": false, "required": false,
"default": "\"Card title\"", "default": "\"\"",
"options": []
},
{
"name": "icon",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "titleKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": [] "options": []
}, },
{ {
@@ -2325,6 +2444,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "subtitleKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "description", "name": "description",
"type": "string", "type": "string",
@@ -2332,6 +2458,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "descriptionKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "header", "name": "header",
"type": "string", "type": "string",
@@ -2374,6 +2507,13 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "actionLabelKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "actionHref", "name": "actionHref",
"type": "string", "type": "string",
@@ -2493,6 +2633,27 @@
"default": "\"none\"", "default": "\"none\"",
"options": [] "options": []
}, },
{
"name": "radius",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "shadow",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "equalHeight",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{ {
"name": "scrollable", "name": "scrollable",
"type": "boolean", "type": "boolean",
@@ -5833,6 +5994,34 @@
"default": "\"lift\"", "default": "\"lift\"",
"options": [] "options": []
}, },
{
"name": "radius",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "shadow",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "clickableSurface",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "descriptionTone",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -6198,6 +6387,20 @@
"default": "\"compact\"", "default": "\"compact\"",
"options": [] "options": []
}, },
{
"name": "mobileColumns",
"type": "number",
"required": false,
"default": "2",
"options": []
},
{
"name": "mobileCopyrightAlign",
"type": "string",
"required": false,
"default": "\"center\"",
"options": []
},
{ {
"name": "copyright", "name": "copyright",
"type": "string", "type": "string",
@@ -6205,6 +6408,20 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "copyrightKey",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "translationPrefix",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -8128,6 +8345,97 @@
"default": "[]", "default": "[]",
"options": [] "options": []
}, },
{
"name": "rail",
"type": "unknown[]",
"required": false,
"default": "[]",
"options": []
},
{
"name": "featured",
"type": "Record<string, unknown>",
"required": false,
"default": "{}",
"options": []
},
{
"name": "variant",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "density",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "panelWidth",
"type": "string",
"required": false,
"default": "\"xl\"",
"options": []
},
{
"name": "panelAlign",
"type": "string",
"required": false,
"default": "\"start\"",
"options": []
},
{
"name": "columnCount",
"type": "number",
"required": false,
"default": "0",
"options": []
},
{
"name": "fullWidth",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "openOnHover",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "activeCategory",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "actionLabel",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "actionHref",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "actionIcon",
"type": "string",
"required": false,
"default": "\"icon-[lucide--arrow-right]\"",
"options": []
},
{ {
"name": "footer", "name": "footer",
"type": "string", "type": "string",
@@ -8163,9 +8471,13 @@
{ {
"name": "select", "name": "select",
"payloadType": "{ item: object; value: string }" "payloadType": "{ item: object; value: string }"
},
{
"name": "category",
"payloadType": "{ item: object; value: string; index: number }"
} }
], ],
"events": ["open", "close", "select"], "events": ["open", "close", "select", "category"],
"source": "components/MegaMenu.wrn" "source": "components/MegaMenu.wrn"
}, },
{ {
@@ -8878,6 +9190,20 @@
"default": "false", "default": "false",
"options": [] "options": []
}, },
{
"name": "floating",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "startFloating",
"type": "string",
"required": false,
"default": "\"0px\"",
"options": []
},
{ {
"name": "openOnHover", "name": "openOnHover",
"type": "boolean", "type": "boolean",
@@ -8899,6 +9225,55 @@
"default": "\"Toggle navigation\"", "default": "\"Toggle navigation\"",
"options": [] "options": []
}, },
{
"name": "collapseAt",
"type": "string",
"required": false,
"default": "\"900px\"",
"options": []
},
{
"name": "mobilePanel",
"type": "string",
"required": false,
"default": "\"content\"",
"options": []
},
{
"name": "mobileUtilitiesPosition",
"type": "string",
"required": false,
"default": "\"header\"",
"options": []
},
{
"name": "gutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "contentGutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "flushMobile",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "translationPrefix",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -8907,7 +9282,7 @@
"options": [] "options": []
} }
], ],
"slots": ["topbar", "actions"], "slots": ["topbar", "actions", "utilities"],
"outputs": [ "outputs": [
{ {
"name": "toggle", "name": "toggle",
@@ -9826,6 +10201,55 @@
"default": "true", "default": "true",
"options": [] "options": []
}, },
{
"name": "iconOnly",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "themeIcon",
"type": "string",
"required": false,
"default": "\"icon-[lucide--sun-moon]\"",
"options": []
},
{
"name": "colorIcon",
"type": "string",
"required": false,
"default": "\"icon-[lucide--palette]\"",
"options": []
},
{
"name": "languageIcon",
"type": "string",
"required": false,
"default": "\"icon-[lucide--languages]\"",
"options": []
},
{
"name": "panelPlacement",
"type": "string",
"required": false,
"default": "\"bottom-end\"",
"options": []
},
{
"name": "mobilePanel",
"type": "string",
"required": false,
"default": "\"dropdown\"",
"options": []
},
{
"name": "showCurrentLanguage",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -10532,6 +10956,34 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "sticky",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "topOffset",
"type": "string",
"required": false,
"default": "\"6rem\"",
"options": []
},
{
"name": "sectionOffset",
"type": "string",
"required": false,
"default": "\"7rem\"",
"options": []
},
{
"name": "collapseAt",
"type": "string",
"required": false,
"default": "\"800px\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -11346,6 +11798,62 @@
"default": "true", "default": "true",
"options": [] "options": []
}, },
{
"name": "gutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "contentGutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "flushMobile",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "titleSize",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "titleMaxWidth",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "descriptionWeight",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "mobileTitleSize",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "mobileStack",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{ {
"name": "backgroundImage", "name": "backgroundImage",
"type": "string", "type": "string",
@@ -11498,6 +12006,41 @@
"default": "\"xl\"", "default": "\"xl\"",
"options": [] "options": []
}, },
{
"name": "radius",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "shadow",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "gutter",
"type": "string",
"required": false,
"default": "\"md\"",
"options": []
},
{
"name": "outerSpacing",
"type": "string",
"required": false,
"default": "\"none\"",
"options": []
},
{
"name": "mobileColumns",
"type": "number",
"required": false,
"default": "1",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -11844,6 +12387,20 @@
"default": "\"\"", "default": "\"\"",
"options": [] "options": []
}, },
{
"name": "showLabel",
"type": "string",
"required": false,
"default": "\"Show password\"",
"options": []
},
{
"name": "hideLabel",
"type": "string",
"required": false,
"default": "\"Hide password\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
+24 -3
View File
@@ -16,6 +16,15 @@ badge: string = ""
dismissLabel: string = "Dismiss announcement" dismissLabel: string = "Dismiss announcement"
sticky: boolean = false sticky: boolean = false
compact: boolean = false compact: boolean = false
flush: boolean = false
mobileCompact: boolean = false
mobileMessageLines: number = 2
hideBadgeOnMobile: boolean = false
hideDescriptionOnMobile: boolean = false
mobileActionSize: string = "default"
gutter: string = "md"
contentGutter: string = "md"
flushMobile: boolean = false
size: string = "default" size: string = "default"
width: string = "default" width: string = "default"
color: string = "primary" color: string = "primary"
@@ -36,6 +45,7 @@ badge: string = ""
data-color='{color}' data-color='{color}'
data-variant='{variant}' data-variant='{variant}'
data-compact='{compact}' data-compact='{compact}'
data-flush='{flush ? "true" : "false"}' data-mobile-compact='{mobileCompact ? "true" : "false"}' data-mobile-message-lines='{mobileMessageLines}' data-hide-badge-mobile='{hideBadgeOnMobile ? "true" : "false"}' data-hide-description-mobile='{hideDescriptionOnMobile ? "true" : "false"}' data-mobile-action-size='{mobileActionSize}' data-gutter='{gutter}' data-content-gutter='{contentGutter}' data-flush-mobile='{flushMobile ? "true" : "false"}'
data-sticky='{sticky}' data-sticky='{sticky}'
data-show='{!dismissed}' data-show='{!dismissed}'
aria-hidden='{dismissed ? "true" : "false"}' aria-hidden='{dismissed ? "true" : "false"}'
@@ -152,9 +162,9 @@ badge: string = ""
position: relative; position: relative;
z-index: 40; z-index: 40;
display: block; display: block;
width: 100vw; width: 100%;
max-width: 100vw; max-width: 100%;
margin-inline: calc(50% - 50vw); margin-inline: 0;
color: var(--wrn-color-text); color: var(--wrn-color-text);
} }
@@ -163,6 +173,8 @@ badge: string = ""
margin-inline: auto; margin-inline: auto;
} }
.wrn-announcement[data-flush="true"] .wrn-announcement__surface { border-radius: 0; box-shadow: none; }
.wrn-announcement[data-width="wide"] .wrn-announcement__surface { .wrn-announcement[data-width="wide"] .wrn-announcement__surface {
width: min(calc(100% - 2rem), 90rem); width: min(calc(100% - 2rem), 90rem);
} }
@@ -573,6 +585,15 @@ badge: string = ""
} }
@media (max-width: 639px) { @media (max-width: 639px) {
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__inner,
.wrn-announcement[data-compact="true"] .wrn-announcement__inner { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.65rem; }
.wrn-announcement[data-hide-badge-mobile="true"] .wrn-announcement__badge,
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__badge { display: none; }
.wrn-announcement[data-hide-description-mobile="true"] .wrn-announcement__description,
.wrn-announcement[data-mobile-compact="true"] .wrn-announcement__description { display: none; }
.wrn-announcement[data-mobile-message-lines="2"] .wrn-announcement__message { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.wrn-announcement[data-mobile-action-size="sm"] .wrn-announcement__action { min-height: 2.25rem; padding-inline: 0.7rem; }
.wrn-announcement[data-flush-mobile="false"] .wrn-announcement__inner { padding-inline: 1rem; }
.wrn-announcement__surface, .wrn-announcement__surface,
.wrn-announcement[data-width="wide"] .wrn-announcement__surface { .wrn-announcement[data-width="wide"] .wrn-announcement__surface {
width: calc(100% - 1rem); width: calc(100% - 1rem);
+2 -2
View File
@@ -120,11 +120,11 @@ component BackToTop {
} }
.wrn-back-to-top[data-position="right"] { .wrn-back-to-top[data-position="right"] {
right: 1.5rem; right: max(1.5rem, env(safe-area-inset-right));
} }
.wrn-back-to-top[data-position="left"] { .wrn-back-to-top[data-position="left"] {
left: 1.5rem; left: max(1.5rem, env(safe-area-inset-left));
} }
.wrn-back-to-top[data-position="center"] { .wrn-back-to-top[data-position="center"] {
+20
View File
@@ -12,6 +12,7 @@ component Blockquote {
variant: string = "default" variant: string = "default"
quoteMark: boolean = true quoteMark: boolean = true
italic: boolean = true italic: boolean = true
maxWidth: string = "xl"
class: string = "" class: string = ""
} }
@@ -24,6 +25,7 @@ component Blockquote {
data-align="{align}" data-align="{align}"
data-variant="{variant}" data-variant="{variant}"
data-italic="{italic}" data-italic="{italic}"
data-max-width="{maxWidth}"
> >
<blockquote cite="{citationUrl}"> <blockquote cite="{citationUrl}">
{#if quoteMark} {#if quoteMark}
@@ -73,10 +75,17 @@ component Blockquote {
display: grid; display: grid;
gap: 1.25rem; gap: 1.25rem;
width: 100%; width: 100%;
max-width: var(--wrn-blockquote-max-width, 48rem);
color: var(--wrn-color-text); color: var(--wrn-color-text);
text-align: left; text-align: left;
} }
.wrn-next--blockquote[data-max-width="sm"] { --wrn-blockquote-max-width: 32rem; }
.wrn-next--blockquote[data-max-width="md"] { --wrn-blockquote-max-width: 40rem; }
.wrn-next--blockquote[data-max-width="lg"] { --wrn-blockquote-max-width: 48rem; }
.wrn-next--blockquote[data-max-width="xl"] { --wrn-blockquote-max-width: 64rem; }
.wrn-next--blockquote[data-max-width="full"] { --wrn-blockquote-max-width: 100%; }
.wrn-next--blockquote[data-size="xs"] { .wrn-next--blockquote[data-size="xs"] {
--wrn-blockquote-font-size: 0.875rem; --wrn-blockquote-font-size: 0.875rem;
--wrn-blockquote-mark-size: 3rem; --wrn-blockquote-mark-size: 3rem;
@@ -189,8 +198,19 @@ component Blockquote {
.wrn-next--blockquote[data-align="center"] { .wrn-next--blockquote[data-align="center"] {
text-align: center; text-align: center;
margin-inline: auto;
} }
.wrn-next--blockquote:is([data-variant="contained"], [data-variant="spotlight"], [data-variant="card"]) {
padding: clamp(1.5rem, 4vw, 3rem);
border: 1px solid color-mix(in srgb, var(--wrn-blockquote-color) 24%, var(--wrn-color-border));
border-radius: calc(var(--wrn-radius) * 2);
background: linear-gradient(145deg, color-mix(in srgb, var(--wrn-blockquote-color) 8%, var(--wrn-color-surface-raised)), var(--wrn-color-surface-raised));
box-shadow: var(--wrn-shadow-1);
}
.wrn-next--blockquote[data-variant="spotlight"] > blockquote { padding-top: calc(var(--wrn-blockquote-mark-size) * 0.55); }
.wrn-next--blockquote[data-align="right"], .wrn-next--blockquote[data-align="right"],
.wrn-next--blockquote[data-align="end"] { .wrn-next--blockquote[data-align="end"] {
text-align: right; text-align: right;
+32 -1
View File
@@ -7,6 +7,7 @@ component Breadcrumb {
label: string = "Breadcrumb" label: string = "Breadcrumb"
items: unknown[] = [] items: unknown[] = []
active: string = "" active: string = ""
activeIcon: string = "icon-[lucide--file-text]"
separator: string = "chevron" separator: string = "chevron"
showHome: boolean = false showHome: boolean = false
homeLabel: string = "Home" homeLabel: string = "Home"
@@ -61,7 +62,7 @@ label: string = "Breadcrumb"
{:else if separator === "arrow"} {:else if separator === "arrow"}
<span>→</span> <span>→</span>
{:else} {:else}
<span class="icon-[lucide--chevron-right] wrn-breadcrumb__separator-icon"></span> <span class="wrn-breadcrumb__separator-icon"></span>
{/if} {/if}
</span> </span>
{/if} {/if}
@@ -99,6 +100,30 @@ label: string = "Breadcrumb"
{/if} {/if}
</li> </li>
{/each} {/each}
{#if active && !items.some((item) => active === (item.value || item.label || item.title))}
<li class="wrn-breadcrumb__item" data-current="true">
{#if showHome || items.length > 0}
<span class="wrn-breadcrumb__separator" aria-hidden="true">
{#if separator === "slash"}
<span>/</span>
{:else if separator === "dot"}
<span>•</span>
{:else if separator === "arrow"}
<span>→</span>
{:else}
<span class="wrn-breadcrumb__separator-icon"></span>
{/if}
</span>
{/if}
<span class="wrn-breadcrumb__current" aria-current="page">
{#if activeIcon}
<span class='{activeIcon} wrn-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wrn-breadcrumb__label">{active}</span>
</span>
</li>
{/if}
</ol> </ol>
</nav> </nav>
} }
@@ -207,8 +232,14 @@ label: string = "Breadcrumb"
} }
.wrn-breadcrumb__separator-icon { .wrn-breadcrumb__separator-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 0.9rem; width: 0.9rem;
height: 0.9rem; height: 0.9rem;
font-size: 1rem;
font-weight: 700;
line-height: 1;
} }
.wrn-breadcrumb__link, .wrn-breadcrumb__link,
+42 -3
View File
@@ -8,6 +8,12 @@ component CTASection {
size: string = "default" size: string = "default"
color: string = "primary" color: string = "primary"
variant: string = "solid" variant: string = "solid"
spacing: string = "none"
radius: string = "default"
titleMaxWidth: string = ""
contentPadding: string = "default"
mobilePadding: string = "default"
mobileActions: string = "stack"
primaryLabel: string = "Get started" primaryLabel: string = "Get started"
primaryHref: string = "#" primaryHref: string = "#"
primaryIcon: string = "" primaryIcon: string = ""
@@ -35,6 +41,9 @@ component CTASection {
data-size='{size}' data-size='{size}'
data-color='{color}' data-color='{color}'
data-variant='{variant}' data-variant='{variant}'
data-spacing='{spacing}'
data-radius='{radius}' data-content-padding='{contentPadding}' data-mobile-padding='{mobilePadding}' data-mobile-actions='{mobileActions}'
style='--wrn-cta-title-max-width: {titleMaxWidth || "18ch"}'
data-max-width='{maxWidth}' data-max-width='{maxWidth}'
data-full-bleed='{fullBleed ? "true" : "false"}' data-full-bleed='{fullBleed ? "true" : "false"}'
data-visual-position='{visualPosition}' data-visual-position='{visualPosition}'
@@ -186,6 +195,18 @@ component CTASection {
--cta-contrast: var(--wrn-color-secondary-contrast); --cta-contrast: var(--wrn-color-secondary-contrast);
} }
.wrn-cta-section[data-spacing="sm"] {
padding-block: 2rem;
}
.wrn-cta-section[data-spacing="md"] {
padding-block: 3rem;
}
.wrn-cta-section[data-spacing="lg"] {
padding-block: 4rem;
}
.wrn-cta-section[data-color="info"] { .wrn-cta-section[data-color="info"] {
--cta-accent: var(--wrn-color-info); --cta-accent: var(--wrn-color-info);
--cta-accent-hover: var(--wrn-color-info-hover); --cta-accent-hover: var(--wrn-color-info-hover);
@@ -223,6 +244,20 @@ component CTASection {
margin-inline: auto; margin-inline: auto;
} }
@media (max-width: 640px) {
.wrn-cta-section[data-spacing="sm"] {
padding-block: 1.5rem;
}
.wrn-cta-section[data-spacing="md"] {
padding-block: 2rem;
}
.wrn-cta-section[data-spacing="lg"] {
padding-block: 2.5rem;
}
}
.wrn-cta-section[data-max-width="compact"] .wrn-cta-section__outer { .wrn-cta-section[data-max-width="compact"] .wrn-cta-section__outer {
width: min(calc(100% - 2rem), 64rem); width: min(calc(100% - 2rem), 64rem);
} }
@@ -260,6 +295,8 @@ component CTASection {
0 1px 0 color-mix(in srgb, white 5%, transparent) inset, 0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 24px 70px color-mix(in srgb, black 15%, transparent); 0 24px 70px color-mix(in srgb, black 15%, transparent);
} }
.wrn-cta-section[data-radius="xl"] .wrn-cta-section__surface { border-radius: 1.5rem; }
.wrn-cta-section[data-content-padding="lg"] .wrn-cta-section__surface { padding: 3.5rem clamp(1.5rem, 5vw, 4rem); }
.wrn-cta-section[data-size="compact"] .wrn-cta-section__surface, .wrn-cta-section[data-size="compact"] .wrn-cta-section__surface,
.wrn-cta-section[data-size="sm"] .wrn-cta-section__surface { .wrn-cta-section[data-size="sm"] .wrn-cta-section__surface {
@@ -401,7 +438,7 @@ component CTASection {
} }
.wrn-cta-section__title { .wrn-cta-section__title {
max-width: 18ch; max-width: var(--wrn-cta-title-max-width);
margin: 0.75rem 0 0; margin: 0.75rem 0 0;
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: clamp(2rem, 5vw, 4rem); font-size: clamp(2rem, 5vw, 4rem);
@@ -637,11 +674,11 @@ component CTASection {
line-height: 1.5; line-height: 1.5;
} }
.wrn-cta-section__footer:empty { .wrn-cta-section__footer:not(:has(> *)) {
display: none; display: none;
} }
.wrn-cta-section__footer:not(:empty) { .wrn-cta-section__footer:has(> *) {
margin-top: 2rem; margin-top: 2rem;
padding-top: 1.25rem; padding-top: 1.25rem;
border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent); border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
@@ -671,6 +708,8 @@ component CTASection {
} }
@media (max-width: 639px) { @media (max-width: 639px) {
.wrn-cta-section[data-mobile-padding="md"] .wrn-cta-section__surface { padding: 2.5rem 1.25rem; }
.wrn-cta-section[data-mobile-actions="inline"] .wrn-cta-section__action { width: auto; }
.wrn-cta-section__surface { .wrn-cta-section__surface {
padding: 1.6rem; padding: 1.6rem;
border-radius: 1.2rem; border-radius: 1.2rem;
+13 -1
View File
@@ -9,7 +9,8 @@ component Card {
} }
props { props {
title: string = "Card title" title: string = ""
icon: string = ""
titleKey: string = "" titleKey: string = ""
subtitle: string = "" subtitle: string = ""
subtitleKey: string = "" subtitleKey: string = ""
@@ -39,6 +40,9 @@ component Card {
layout: string = "vertical" layout: string = "vertical"
align: string = "left" align: string = "left"
hover: string = "none" hover: string = "none"
radius: string = "default"
shadow: string = "default"
equalHeight: boolean = false
scrollable: boolean = false scrollable: boolean = false
maxHeight: string = "18rem" maxHeight: string = "18rem"
dismissible: boolean = false dismissible: boolean = false
@@ -84,6 +88,7 @@ component Card {
data-layout='{layout}' data-layout='{layout}'
data-hover='{hover}' data-hover='{hover}'
data-align='{align}' data-align='{align}'
data-radius='{radius}' data-shadow='{shadow}' data-equal-height='{equalHeight ? "true" : "false"}'
class='wrn-component wrn-next--card wrn-component--color-{color} wrn-component--size-{size} wrn-next--variant-{variant} {class}' class='wrn-component wrn-next--card wrn-component--color-{color} wrn-component--size-{size} wrn-next--variant-{variant} {class}'
> >
{#if items.length > 0} {#if items.length > 0}
@@ -271,6 +276,7 @@ component Card {
<p>{emptyTitle}</p> <p>{emptyTitle}</p>
</div> </div>
{:else} {:else}
{#if icon}<span class='wrn-next__card-icon {icon}' aria-hidden="true"></span>{/if}
{#if description} {#if description}
<p class="wrn-next__card-description" data-t='{descriptionKey}'>{description}</p> <p class="wrn-next__card-description" data-t='{descriptionKey}'>{description}</p>
{/if} {/if}
@@ -343,6 +349,12 @@ component Card {
.wrn-next--card[data-size="sm"] { .wrn-next--card[data-size="sm"] {
--wrn-card-padding: 0.85rem; --wrn-card-padding: 0.85rem;
} }
.wrn-next--card[data-size="compact"] .wrn-next__card-body { padding: 1rem; }
.wrn-next--card[data-radius="lg"] > .wrn-next__card-panel { border-radius: 1rem; }
.wrn-next--card[data-shadow="none"] > .wrn-next__card-panel { box-shadow: none; }
.wrn-next--card[data-equal-height="true"], .wrn-next--card[data-equal-height="true"] > .wrn-next__card-panel { height: 100%; }
.wrn-next--card[data-layout="horizontal"] .wrn-next__card-body { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 0.8rem; }
.wrn-next__card-icon { display: grid; width: 2.35rem; height: 2.35rem; place-items: center; border-radius: 0.7rem; color: var(--wrn-color-primary); background: color-mix(in srgb, var(--wrn-color-primary) 12%, transparent); font-size: 1.15rem; }
.wrn-next--card[data-size="lg"] { .wrn-next--card[data-size="lg"] {
--wrn-card-padding: 1.75rem; --wrn-card-padding: 1.75rem;
+2
View File
@@ -205,7 +205,9 @@ component ColorPicker {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -276,7 +276,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+10 -1
View File
@@ -15,13 +15,17 @@ component FeatureIconCard {
variant: string = "default" variant: string = "default"
align: string = "left" align: string = "left"
hover: string = "lift" hover: string = "lift"
radius: string = "default"
shadow: string = "default"
clickableSurface: boolean = false
descriptionTone: string = "default"
class: string = "" class: string = ""
} }
view { view {
<article <article
data-ui-component="FeatureIconCard" data-ui-component="FeatureIconCard"
data-size='{size}' data-variant='{variant}' data-hover='{hover}' data-align='{align}' data-size='{size}' data-variant='{variant}' data-hover='{hover}' data-align='{align}' data-radius='{radius}' data-shadow='{shadow}' data-clickable-surface='{clickableSurface ? "true" : "false"}' data-description-tone='{descriptionTone}'
class='wrn-feature-icon-card {class}' class='wrn-feature-icon-card {class}'
> >
<div <div
@@ -62,6 +66,11 @@ component FeatureIconCard {
style { style {
.wrn-feature-icon-card { position: relative; display: flex; height: 100%; flex-direction: column; padding: 1.5rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 2); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: transform var(--wrn-motion-base), border-color var(--wrn-motion-base), box-shadow var(--wrn-motion-base); } .wrn-feature-icon-card { position: relative; display: flex; height: 100%; flex-direction: column; padding: 1.5rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 2); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: transform var(--wrn-motion-base), border-color var(--wrn-motion-base), box-shadow var(--wrn-motion-base); }
.wrn-feature-icon-card[data-size="sm"] { padding: 1.25rem; } .wrn-feature-icon-card[data-size="lg"] { padding: 2rem; } .wrn-feature-icon-card[data-size="sm"] { padding: 1.25rem; } .wrn-feature-icon-card[data-size="lg"] { padding: 2rem; }
.wrn-feature-icon-card[data-size="compact"] { min-height: 0; padding: 1.25rem; }
.wrn-feature-icon-card[data-radius="lg"] { border-radius: 1rem; }
.wrn-feature-icon-card[data-shadow="sm"] { box-shadow: 0 5px 18px rgb(15 23 42 / 0.06); }
.wrn-feature-icon-card[data-description-tone="muted"] .wrn-feature-icon-card__description { color: var(--wrn-color-text-muted); font-weight: 500; }
.wrn-feature-icon-card[data-clickable-surface="true"] .wrn-feature-icon-card__action::after { position: absolute; inset: 0; content: ""; }
.wrn-feature-icon-card[data-variant="soft"] { background: var(--wrn-color-surface-soft); } .wrn-feature-icon-card[data-variant="soft"] { background: var(--wrn-color-surface-soft); }
.wrn-feature-icon-card[data-variant="ghost"] { border-color: transparent; background: transparent; box-shadow: none; } .wrn-feature-icon-card[data-variant="ghost"] { border-color: transparent; background: transparent; box-shadow: none; }
.wrn-feature-icon-card[data-hover="lift"]:hover { transform: translateY(-0.25rem); } .wrn-feature-icon-card[data-hover="lift"]:hover { transform: translateY(-0.25rem); }
+2
View File
@@ -230,7 +230,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
@@ -244,7 +244,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+6
View File
@@ -12,6 +12,8 @@ component Footer {
items: unknown[] = [] items: unknown[] = []
columns: number = 3 columns: number = 3
maxWidth: string = "compact" maxWidth: string = "compact"
mobileColumns: number = 2
mobileCopyrightAlign: string = "center"
copyright: string = "" copyright: string = ""
copyrightKey: string = "" copyrightKey: string = ""
translationPrefix: string = "" translationPrefix: string = ""
@@ -36,6 +38,7 @@ component Footer {
data-ui-component="Footer" data-ui-component="Footer"
data-size='{size}' data-size='{size}'
data-color='{color}' data-color='{color}'
data-mobile-columns='{mobileColumns}' data-mobile-copyright-align='{mobileCopyrightAlign}'
class='wrn-footer {maxWidth === "full" ? "wrn-footer--width-full" : ""} {class}' class='wrn-footer {maxWidth === "full" ? "wrn-footer--width-full" : ""} {class}'
> >
<div <div
@@ -531,6 +534,9 @@ component Footer {
.wrn-footer__links { .wrn-footer__links {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
.wrn-footer[data-mobile-columns="1"] .wrn-footer__links { grid-template-columns: 1fr; }
.wrn-footer[data-mobile-columns="2"] .wrn-footer__links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wrn-footer[data-mobile-copyright-align="start"] :is(.wrn-footer__copyright-left, .wrn-footer__copyright-right) { justify-content: flex-start; text-align: left; }
.wrn-footer__bottom, .wrn-footer__bottom,
.wrn-footer__bottom--slots { .wrn-footer__bottom--slots {
grid-template-columns: 1fr; grid-template-columns: 1fr;
+3 -11
View File
@@ -280,22 +280,14 @@ component Hero {
} }
.wrn-hero[data-full-bleed="true"] { .wrn-hero[data-full-bleed="true"] {
width: 100vw; width: 100%;
max-width: 100vw; max-width: 100%;
margin-inline: calc(50% - 50vw); margin-inline: 0;
border-right-width: 0; border-right-width: 0;
border-left-width: 0; border-left-width: 0;
border-radius: 0; border-radius: 0;
} }
@supports (width: 100dvw) {
.wrn-hero[data-full-bleed="true"] {
width: 100dvw;
max-width: 100dvw;
margin-inline: calc(50% - 50dvw);
}
}
.wrn-hero[data-color="secondary"] { .wrn-hero[data-color="secondary"] {
--wrn-hero-accent: var(--wrn-color-secondary); --wrn-hero-accent: var(--wrn-color-secondary);
--wrn-hero-accent-soft: var(--wrn-color-secondary-soft, var(--wrn-color-primary-soft)); --wrn-hero-accent-soft: var(--wrn-color-secondary-soft, var(--wrn-color-primary-soft));
+2
View File
@@ -243,7 +243,9 @@ component InputGroup {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+283 -26
View File
@@ -18,6 +18,7 @@ component MegaMenu {
open(payload: { sourceEvent: Event }) open(payload: { sourceEvent: Event })
close(payload: { reason: string }) close(payload: { reason: string })
select(payload: { item: object; value: string }) select(payload: { item: object; value: string })
category(payload: { item: object; value: string; index: number })
} }
props { props {
@@ -26,12 +27,27 @@ component MegaMenu {
label: string = "Menu" label: string = "Menu"
icon: string = "" icon: string = ""
columns: unknown[] = [] columns: unknown[] = []
rail: unknown[] = []
featured: Record<string, unknown> = {}
variant: string = "default"
density: string = "default"
panelWidth: string = "xl"
panelAlign: string = "start"
columnCount: number = 0
fullWidth: boolean = false
openOnHover: boolean = true
activeCategory: string = ""
actionLabel: string = ""
actionHref: string = ""
actionIcon: string = "icon-[lucide--arrow-right]"
footer: string = "" footer: string = ""
defaultOpen: boolean = false defaultOpen: boolean = false
class: string = "" class: string = ""
} }
state visible = defaultOpen state visible = defaultOpen
state closeTimer = null
state selectedCategory = activeCategory
functions { functions {
shared function columnList() { shared function columnList() {
@@ -42,16 +58,66 @@ component MegaMenu {
return column && Array.isArray(column.items) ? column.items : [] return column && Array.isArray(column.items) ? column.items : []
} }
shared function railList() {
return Array.isArray(rail) ? rail : []
}
shared function categoryValue(item) {
return item ? String(item.value || item.href || item.label || "") : ""
}
shared function activeCategoryIndex() {
const entries = railList()
if (!entries.length) {
return 0
}
if (!selectedCategory) {
return 0
}
for (let index = 0; index < entries.length; index += 1) {
if (categoryValue(entries[index]) === selectedCategory) {
return index
}
}
return 0
}
shared function visibleColumns() {
const entries = columnList()
if (variant !== "catalog") {
return entries
}
const selected = entries[activeCategoryIndex()]
return selected ? [selected] : []
}
client function showPanel(sourceEvent) { client function showPanel(sourceEvent) {
cancelScheduledHide()
visible = true visible = true
output.open({ sourceEvent: sourceEvent }) output.open({ sourceEvent: sourceEvent })
} }
client function hidePanel(reason) { client function hidePanel(reason) {
cancelScheduledHide()
visible = false visible = false
output.close({ reason: reason }) output.close({ reason: reason })
} }
client function cancelScheduledHide() {
if (closeTimer) {
window.clearTimeout(closeTimer)
closeTimer = null
}
}
client function scheduleHide() {
cancelScheduledHide()
closeTimer = window.setTimeout(function () {
closeTimer = null
hidePanel("pointer-leave")
}, 220)
}
client function togglePanel(sourceEvent) { client function togglePanel(sourceEvent) {
if (visible) { if (visible) {
hidePanel("toggle") hidePanel("toggle")
@@ -60,6 +126,34 @@ component MegaMenu {
} }
} }
client function activatePanel(sourceEvent) {
if (openOnHover) {
showPanel(sourceEvent)
} else {
togglePanel(sourceEvent)
}
}
client function hoverPanel(sourceEvent) {
if (openOnHover) {
showPanel(sourceEvent)
}
}
client function chooseCategory(item, index, sourceEvent) {
const root = sourceEvent.currentTarget.closest(".wrn-mega")
const rail = sourceEvent.currentTarget.closest(".wrn-mega__rail")
const scrollTop = rail ? rail.scrollTop : 0
selectedCategory = categoryValue(item)
output.category({ item: item, value: selectedCategory, index: index })
window.requestAnimationFrame(function () {
const nextRail = root ? root.querySelector(".wrn-mega__rail") : null
if (nextRail) {
nextRail.scrollTop = scrollTop
}
})
}
client function handleKeydown(sourceEvent) { client function handleKeydown(sourceEvent) {
if (sourceEvent.key === "Escape" && visible) { if (sourceEvent.key === "Escape" && visible) {
sourceEvent.preventDefault() sourceEvent.preventDefault()
@@ -84,20 +178,27 @@ component MegaMenu {
data-color='{color}' data-color='{color}'
data-size='{size}' data-size='{size}'
data-open='{visible}' data-open='{visible}'
data-variant='{variant}'
data-density='{density}'
data-panel-width='{panelWidth}'
data-panel-align='{panelAlign}'
data-full-width='{fullWidth ? "true" : "false"}'
data-column-count='{columnCount}'
@keydown='handleKeydown(event)' @keydown='handleKeydown(event)'
@mouseleave='hidePanel("pointer-leave")' @mouseenter='cancelScheduledHide()'
@mouseleave='scheduleHide()'
> >
<button <button
type="button" type="button"
class="wrn-mega__trigger" class="wrn-mega__trigger"
aria-haspopup="true" aria-haspopup="true"
aria-expanded='{visible}' aria-expanded='{visible}'
@click='togglePanel(event)' aria-label='{label}'
@mouseenter='showPanel(event)' @click='activatePanel(event)'
@focus='showPanel(event)' @mouseenter='hoverPanel(event)'
> >
<span class='wrn-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span> <span class='wrn-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span>
<span class="wrn-mega__trigger-label">{label}</span> <span class="wrn-mega__trigger-label" data-label='{label}' aria-hidden="true"></span>
<span class="wrn-mega__arrow" aria-hidden="true">&#8250;</span> <span class="wrn-mega__arrow" aria-hidden="true">&#8250;</span>
</button> </button>
@@ -105,42 +206,76 @@ component MegaMenu {
class="wrn-mega__panel" class="wrn-mega__panel"
data-wrn-anchored="true" data-wrn-anchored="true"
data-show="visible" data-show="visible"
style='{visible ? "" : "display:none"}'
data-wrn-roving="both" data-wrn-roving="both"
@mouseenter='cancelScheduledHide()'
> >
<div class="wrn-mega__layout">
{#if railList().length > 0}
<aside class="wrn-mega__rail">
{#each railList() as item, index}
{#if variant === "catalog"}
<button type="button" class="wrn-mega__rail-link" data-active='{activeCategoryIndex() === index ? "true" : "false"}' aria-pressed='{activeCategoryIndex() === index ? "true" : "false"}' @click='chooseCategory(item, index, event)'>
{#if item.icon}<span class="wrn-mega__rail-icon" aria-hidden="true"><span class='{item.icon}'></span></span>{/if}
<span><strong>{item.label}</strong>{#if item.description}<small>{item.description}</small>{/if}</span>
</button>
{:else}
<a class="wrn-mega__rail-link" href='{item.href || "#"}' @click='choose(item)'>
{#if item.icon}<span class="wrn-mega__rail-icon" aria-hidden="true"><span class='{item.icon}'></span></span>{/if}
<span><strong>{item.label}</strong>{#if item.description}<small>{item.description}</small>{/if}</span>
</a>
{/if}
{/each}
</aside>
{/if}
<div class="wrn-mega__columns"> <div class="wrn-mega__columns">
{#each columnList() as column} {#each visibleColumns() as column}
<section class="wrn-mega__column"> <section class="wrn-mega__column">
<header class="wrn-mega__column-header">
{#if column.eyebrow}<p class="wrn-mega__eyebrow">{column.eyebrow}</p>{/if}
<h3 class="wrn-mega__heading" data-show="column.heading">{column.heading}</h3> <h3 class="wrn-mega__heading" data-show="column.heading">{column.heading}</h3>
{#if column.description}<p class="wrn-mega__column-description">{column.description}</p>{/if}
</header>
{#if column.image}<img class="wrn-mega__column-image" src='{column.image}' alt='{column.imageAlt || ""}' loading="lazy" />{/if}
<ul class="wrn-mega__list"> <ul class="wrn-mega__list">
{#each itemsOf(column) as item} {#each itemsOf(column) as item}
<li> <li>
<a <a class="wrn-mega__link" href='{item.href || "#"}' data-wrn-roving-item="true" aria-disabled='{item.disabled ? "true" : "false"}' @click='choose(item)'>
class="wrn-mega__link" {#if item.image}<img class="wrn-mega__link-image" src='{item.image}' alt="" loading="lazy" />{/if}
href='{item.href || "#"}' {#if item.icon}
data-wrn-roving-item="true" <span class='wrn-mega__link-icon {item.icon}' aria-hidden="true"></span>
aria-disabled='{item.disabled ? "true" : "false"}' {:else if variant === "catalog"}
@click='choose(item)' <span class="wrn-mega__link-icon wrn-mega__link-icon--fallback" aria-hidden="true">
> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M3 9h18"></path><path d="M9 21V9"></path></svg>
<span
class='wrn-mega__link-icon {item.icon}'
data-show="item.icon"
aria-hidden="true"
></span>
<span class="wrn-mega__link-body">
<span class="wrn-mega__link-label">{item.label}</span>
<span class="wrn-mega__link-description" data-show="item.description">
{item.description}
</span> </span>
{/if}
<span class="wrn-mega__link-body">
{#if item.eyebrow}<span class="wrn-mega__link-eyebrow">{item.eyebrow}</span>{/if}
<span class="wrn-mega__link-label">{item.label}{#if item.badge}<span class="wrn-mega__badge">{item.badge}</span>{/if}</span>
{#if item.description}<span class="wrn-mega__link-description">{item.description}</span>{/if}
</span> </span>
</a> </a>
</li> </li>
{/each} {/each}
</ul> </ul>
{#if column.actionLabel}<a class="wrn-mega__column-action" href='{column.actionHref || "#"}'>{column.actionLabel}<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span></a>{/if}
</section> </section>
{/each} {/each}
</div> </div>
<p class="wrn-mega__footer" data-show="footer">{footer}</p> {#if featured.title || featured.image}
<aside class="wrn-mega__featured">
{#if featured.eyebrow}<p class="wrn-mega__eyebrow">{featured.eyebrow}</p>{/if}
{#if featured.image}<img src='{featured.image}' alt='{featured.imageAlt || ""}' loading="lazy" />{/if}
{#if featured.title}<strong>{featured.title}</strong>{/if}
{#if featured.description}<p>{featured.description}</p>{/if}
{#if featured.actionLabel}<a href='{featured.actionHref || "#"}'>{featured.actionLabel}<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span></a>{/if}
</aside>
{/if}
</div>
{#if footer || actionLabel}<div class="wrn-mega__footer"><span>{footer}</span>{#if actionLabel}<a href='{actionHref || "#"}'>{actionLabel}<span class='{actionIcon}' aria-hidden="true"></span></a>{/if}</div>{/if}
<slot /> <slot />
</div> </div>
</div> </div>
@@ -187,7 +322,7 @@ component MegaMenu {
border: 1px solid transparent; border: 1px solid transparent;
border-radius: var(--wrn-radius-sm); border-radius: var(--wrn-radius-sm);
background: transparent; background: transparent;
color: var(--wrn-color-text-muted); color: var(--wrn-color-text);
font: inherit; font: inherit;
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 600; font-weight: 600;
@@ -200,6 +335,10 @@ component MegaMenu {
color: var(--wrn-color-text); color: var(--wrn-color-text);
} }
.wrn-mega__trigger-label::before {
content: attr(data-label);
}
.wrn-mega__trigger:focus-visible { .wrn-mega__trigger:focus-visible {
outline: 2px solid var(--mega-accent); outline: 2px solid var(--mega-accent);
outline-offset: 2px; outline-offset: 2px;
@@ -226,29 +365,78 @@ component MegaMenu {
left: 0; left: 0;
right: 0; right: 0;
bottom: 100%; bottom: 100%;
height: 0.6rem; height: 0.85rem;
} }
.wrn-mega__panel { .wrn-mega__panel {
box-sizing: border-box;
position: absolute; position: absolute;
z-index: 40; z-index: 1200;
top: calc(100% + 0.4rem); top: calc(100% + 0.4rem);
left: 0; left: 0;
width: max-content; width: max-content;
max-width: min(60rem, calc(100vw - 2rem)); max-width: min(60rem, calc(100vw - 2rem));
max-height: calc(100dvh - 5.5rem);
padding: 1rem; padding: 1rem;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid var(--wrn-color-border); border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-lg); border-radius: var(--wrn-radius-lg);
background: var(--wrn-color-surface); background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1); box-shadow: var(--wrn-shadow-1);
} }
/* Keep SSR output closed before the reactive runtime hydrates it. */
.wrn-mega__panel[data-show="false"] { display: none; }
.wrn-mega__panel[data-show="true"] { display: flex; }
.wrn-mega[data-panel-width="lg"] .wrn-mega__panel { width: min(48rem, calc(100vw - 2rem)); }
.wrn-mega[data-panel-width="xl"] .wrn-mega__panel { width: min(72rem, calc(100vw - 2rem)); }
.wrn-mega[data-panel-width="2xl"] .wrn-mega__panel { width: min(88rem, calc(100vw - 2rem)); }
.wrn-mega[data-full-width="true"] .wrn-mega__panel { width: calc(100vw - 2rem); max-width: none; }
.wrn-mega[data-panel-align="center"] .wrn-mega__panel { left: 50%; transform: translateX(-50%); }
.wrn-mega[data-panel-align="end"] .wrn-mega__panel { right: 0; left: auto; }
.wrn-mega[data-variant="default"] .wrn-mega__panel {
width: max-content;
min-width: 15rem;
max-width: min(24rem, calc(100dvw - 2rem));
height: auto;
}
.wrn-mega[data-variant="default"] .wrn-mega__layout,
.wrn-mega[data-variant="default"] .wrn-mega__columns,
.wrn-mega[data-variant="default"][data-column-count] .wrn-mega__columns {
grid-template-columns: minmax(0, 1fr);
}
.wrn-mega[data-variant="default"] .wrn-mega__columns { overflow: visible; }
.wrn-mega[data-variant="default"] .wrn-mega__featured { width: auto; padding: 1rem 0 0; border-top: 1px solid var(--wrn-color-border); border-left: 0; }
.wrn-mega__layout { display: grid; min-height: 0; max-height: calc(100dvh - 8rem); flex: 1; grid-template-columns: auto minmax(0, 1fr) auto; gap: 1.25rem; overflow: hidden; }
.wrn-mega__columns { .wrn-mega__columns {
display: grid; display: grid;
min-height: 0;
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
gap: 1.25rem; gap: 1.25rem;
overflow-y: auto;
overscroll-behavior: contain;
} }
.wrn-mega[data-column-count="2"] .wrn-mega__columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wrn-mega[data-column-count="3"] .wrn-mega__columns { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.wrn-mega[data-column-count="4"] .wrn-mega__columns { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.wrn-mega[data-column-count="5"] .wrn-mega__columns { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.wrn-mega__rail { display: grid; min-width: 13rem; min-height: 0; padding: 0.75rem; align-content: start; gap: 0.25rem; overflow-y: auto; overscroll-behavior: contain; border-radius: var(--wrn-radius-lg); background: var(--wrn-color-surface-soft); }
.wrn-mega__rail-link { appearance: none; display: flex; width: 100%; padding: 0.65rem; align-items: flex-start; gap: 0.65rem; color: var(--wrn-color-text); border: 0; border-radius: var(--wrn-radius-sm); background: transparent; font: inherit; text-align: left; text-decoration: none; cursor: pointer; }
.wrn-mega__rail-link:hover { background: var(--wrn-color-surface); }
.wrn-mega__rail-link[data-active="true"] { color: var(--mega-accent); background: var(--wrn-color-surface); box-shadow: inset 3px 0 0 var(--mega-accent); }
.wrn-mega__rail-link > span:last-child { display: grid; gap: 0.2rem; }
.wrn-mega__rail-link strong { font-size: 0.82rem; }
.wrn-mega__rail-link small { max-width: 18ch; color: var(--wrn-color-text-muted); font-size: 0.72rem; line-height: 1.45; }
.wrn-mega__rail-icon { display: grid; width: 2.4rem; height: 2.4rem; flex: none; place-items: center; color: var(--mega-accent); border-radius: 0.7rem; background: color-mix(in srgb, var(--mega-accent) 12%, transparent); font-size: 1.15rem; }
.wrn-mega__column { .wrn-mega__column {
min-width: 0; min-width: 0;
} }
@@ -262,6 +450,10 @@ component MegaMenu {
text-transform: uppercase; text-transform: uppercase;
} }
.wrn-mega__eyebrow, .wrn-mega__link-eyebrow { margin: 0 0 0.35rem; color: var(--wrn-color-text-muted); font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.wrn-mega__column-description { margin: -0.25rem 0 0.75rem; color: var(--wrn-color-text-muted); font-size: 0.75rem; line-height: 1.5; }
.wrn-mega__column-image { width: 100%; aspect-ratio: 16 / 7; margin-bottom: 0.65rem; object-fit: cover; border-radius: var(--wrn-radius-sm); }
.wrn-mega__list { .wrn-mega__list {
display: grid; display: grid;
gap: 0.15rem; gap: 0.15rem;
@@ -270,8 +462,14 @@ component MegaMenu {
list-style: none; list-style: none;
} }
.wrn-mega__list > li {
min-width: 0;
}
.wrn-mega__link { .wrn-mega__link {
display: flex; display: flex;
width: 100%;
min-width: 0;
align-items: flex-start; align-items: flex-start;
gap: 0.5rem; gap: 0.5rem;
padding: 0.45rem 0.5rem; padding: 0.45rem 0.5rem;
@@ -280,6 +478,43 @@ component MegaMenu {
text-decoration: none; text-decoration: none;
} }
.wrn-mega__link-icon { display: grid; width: 2rem; height: 2rem; flex: none; place-items: center; color: var(--mega-accent); border-radius: 999px; background: color-mix(in srgb, var(--mega-accent) 10%, var(--wrn-color-surface-soft)); }
.wrn-mega__link-image { width: 2.4rem; height: 2.4rem; flex: none; object-fit: cover; border-radius: 0.65rem; }
.wrn-mega__badge { display: inline-flex; margin-left: 0.4rem; padding: 0.12rem 0.35rem; align-items: center; color: var(--mega-accent); border: 1px solid color-mix(in srgb, var(--mega-accent) 35%, transparent); border-radius: 999px; font-size: 0.6rem; font-weight: 700; }
.wrn-mega__column-action, .wrn-mega__featured a, .wrn-mega__footer a { display: inline-flex; margin-top: 0.7rem; align-items: center; gap: 0.35rem; color: var(--mega-accent); font-size: 0.78rem; font-weight: 700; text-decoration: none; }
.wrn-mega__featured { display: grid; width: 14rem; padding-left: 1.25rem; align-content: start; gap: 0.55rem; border-left: 1px solid var(--wrn-color-border); }
.wrn-mega__featured img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--wrn-radius-lg); }
.wrn-mega__featured strong { font-size: 0.85rem; line-height: 1.35; }
.wrn-mega__featured p { margin: 0; color: var(--wrn-color-text-muted); font-size: 0.75rem; line-height: 1.5; }
.wrn-mega[data-density="compact"] :is(.wrn-mega__panel, .wrn-mega__rail) { padding: 0.7rem; }
.wrn-mega[data-density="compact"] .wrn-mega__columns { gap: 0.65rem; }
.wrn-mega[data-variant="icon-grid"] .wrn-mega__list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.35rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__panel { height: min(36rem, calc(100dvh - 6.5rem)); }
.wrn-mega[data-variant="catalog"] .wrn-mega__layout { grid-template-columns: minmax(14rem, 17rem) minmax(0, 1fr); gap: 0; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail { min-width: 0; padding: 0.55rem; gap: 0.3rem; border-radius: calc(var(--wrn-radius-lg) - 0.25rem); }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link { min-height: 2.5rem; padding: 0.45rem 0.55rem; align-items: center; gap: 0.7rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link strong { font-size: 0.76rem; font-weight: 400; line-height: 1.25; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link[data-active="true"] strong { font-weight: 600; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-icon { display: grid; width: 1.65rem; height: 1.65rem; flex: 0 0 1.65rem; place-items: center; color: currentColor; border: 1px solid color-mix(in srgb, currentColor 35%, transparent); border-radius: 0.45rem; background: color-mix(in srgb, currentColor 8%, transparent); opacity: 0.9; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-icon > span { display: block; width: 0.95rem; height: 0.95rem; color: currentColor !important; font-size: 0.95rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link:hover .wrn-mega__rail-icon,
.wrn-mega[data-variant="catalog"] .wrn-mega__rail-link[data-active="true"] .wrn-mega__rail-icon { opacity: 1; }
.wrn-mega[data-variant="catalog"] .wrn-mega__columns { padding: 0.35rem 0.5rem 0.75rem 1.25rem; grid-template-columns: minmax(0, 1fr); }
.wrn-mega[data-variant="catalog"] .wrn-mega__column { display: flex; min-height: 100%; flex-direction: column; }
.wrn-mega[data-variant="catalog"] .wrn-mega__column-header { width: 100%; min-width: 0; margin: 0 0 0.85rem; padding: 0 0 0.65rem; border: 0; border-bottom: 1px solid var(--wrn-color-border); border-radius: 0; background: transparent; }
.wrn-mega[data-variant="catalog"] .wrn-mega__heading { margin: 0; color: var(--wrn-color-text-muted); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
.wrn-mega[data-variant="catalog"] .wrn-mega__column-description { margin: 0.3rem 0 0; }
.wrn-mega[data-variant="catalog"] .wrn-mega__list { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0.3rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__link { min-height: 2.7rem; padding: 0.45rem 0.55rem; align-items: center; gap: 0.55rem; border: 1px solid transparent; background: transparent; }
.wrn-mega[data-variant="catalog"] .wrn-mega__link:hover { border-color: color-mix(in srgb, var(--mega-accent) 28%, var(--wrn-color-border)); background: color-mix(in srgb, var(--mega-accent) 7%, var(--wrn-color-surface)); transform: translateY(-1px); }
.wrn-mega[data-variant="catalog"] .wrn-mega__column-action { margin-top: auto; padding-top: 1rem; }
.wrn-mega[data-variant="dark"] .wrn-mega__panel { color: #f8fafc; border-color: #2b2b2b; background: #151515; }
.wrn-mega[data-variant="dark"] :is(.wrn-mega__heading, .wrn-mega__link, .wrn-mega__featured strong) { color: #f8fafc; }
.wrn-mega[data-variant="dark"] :is(.wrn-mega__link-description, .wrn-mega__column-description, .wrn-mega__eyebrow, .wrn-mega__featured p) { color: #a3a3a3; }
.wrn-mega[data-variant="dark"] .wrn-mega__link:hover { background: #242424; }
.wrn-mega__link:hover { .wrn-mega__link:hover {
background: var(--wrn-color-surface-soft); background: var(--wrn-color-surface-soft);
} }
@@ -300,9 +535,15 @@ component MegaMenu {
min-width: 0; min-width: 0;
} }
.wrn-mega[data-variant="catalog"] .wrn-mega__link-icon { width: 1.65rem; height: 1.65rem; flex: 0 0 1.65rem; border: 1px solid color-mix(in srgb, var(--mega-accent) 22%, var(--wrn-color-border)); border-radius: 0.45rem; font-size: 0.9rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__link-icon--fallback svg { width: 0.9rem; height: 0.9rem; }
.wrn-mega[data-variant="catalog"] .wrn-mega__link-label { font-size: 0.78rem; line-height: 1.25; }
.wrn-mega[data-variant="catalog"] .wrn-mega__link-description { font-size: 0.7rem; line-height: 1.35; }
.wrn-mega__link-label { .wrn-mega__link-label {
font-size: 0.88rem; font-size: 0.88rem;
font-weight: 600; font-weight: 600;
overflow-wrap: anywhere;
} }
.wrn-mega__link-description { .wrn-mega__link-description {
@@ -312,8 +553,12 @@ component MegaMenu {
} }
.wrn-mega__footer { .wrn-mega__footer {
display: flex;
margin: 1rem 0 0; margin: 1rem 0 0;
padding-top: 0.75rem; padding-top: 0.75rem;
align-items: center;
justify-content: space-between;
gap: 1rem;
border-top: 1px solid var(--wrn-color-border); border-top: 1px solid var(--wrn-color-border);
color: var(--wrn-color-text-muted); color: var(--wrn-color-text-muted);
font-size: 0.8rem; font-size: 0.8rem;
@@ -333,12 +578,24 @@ component MegaMenu {
position: static; position: static;
width: auto; width: auto;
max-width: 100%; max-width: 100%;
max-height: none;
overflow: visible;
box-shadow: none; box-shadow: none;
} }
.wrn-mega__panel[data-show="false"] { display: none; }
.wrn-mega__columns { .wrn-mega__columns {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.wrn-mega__layout { max-height: none; grid-template-columns: 1fr; overflow: visible; }
.wrn-mega__rail, .wrn-mega__columns { overflow: visible; }
.wrn-mega__featured { width: auto; padding: 1rem 0 0; border-top: 1px solid var(--wrn-color-border); border-left: 0; }
.wrn-mega[data-variant="icon-grid"] .wrn-mega__list { grid-template-columns: 1fr; }
.wrn-mega[data-variant="catalog"] .wrn-mega__panel { height: auto; }
.wrn-mega[data-variant="catalog"] .wrn-mega__layout { grid-template-columns: 1fr; }
.wrn-mega[data-variant="catalog"] .wrn-mega__columns { padding: 1rem 0 0; }
.wrn-mega[data-variant="catalog"] .wrn-mega__list { grid-template-columns: 1fr; }
} }
} }
} }
+12 -1
View File
@@ -92,7 +92,9 @@ component MetricGrid {
@action='forwardAction(item, itemIndex, event)' @action='forwardAction(item, itemIndex, event)'
/> />
{:empty} {:empty}
<slot></slot> <div class="wrn-metric-grid__empty" role="status">
<slot>No metrics are available.</slot>
</div>
{/each} {/each}
</div> </div>
</div> </div>
@@ -100,6 +102,15 @@ component MetricGrid {
} }
style { style {
.wrn-metric-grid__empty {
grid-column: 1 / -1;
padding: 1.25rem;
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px dashed var(--wrn-color-border);
border-radius: 0.9rem;
text-align: center;
}
.wrn-metric-grid__frame { .wrn-metric-grid__frame {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
+290 -35
View File
@@ -1,3 +1,5 @@
import MegaMenu from "./MegaMenu.wrn"
component Navbar { component Navbar {
outputs { outputs {
toggle(payload: { open: boolean }) toggle(payload: { open: boolean })
@@ -17,9 +19,17 @@ size: string = "default"
actions: unknown[] = [] actions: unknown[] = []
active: string = "" active: string = ""
sticky: boolean = false sticky: boolean = false
floating: boolean = false
startFloating: string = "0px"
openOnHover: boolean = false openOnHover: boolean = false
maxWidth: string = "full" maxWidth: string = "full"
mobileLabel: string = "Toggle navigation" mobileLabel: string = "Toggle navigation"
collapseAt: string = "900px"
mobilePanel: string = "content"
mobileUtilitiesPosition: string = "header"
gutter: string = "md"
contentGutter: string = "md"
flushMobile: boolean = false
translationPrefix: string = "" translationPrefix: string = ""
class: string = "" class: string = ""
} }
@@ -36,27 +46,112 @@ size: string = "default"
const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()
return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : "" return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : ""
} }
client function toggleNavigation() { shared function itemType(item) {
mobileOpen = !mobileOpen if (item && item.type) {
output.toggle({ open: mobileOpen }) return item.type
output[mobileOpen ? "open" : "close"]({ source: "mobile" }) }
return item && ((item.items && item.items.length) || (item.children && item.children.length)) ? "dropdown" : "link"
} }
client function selectItem(item, level) { shared function dropdownItems(item) {
mobileOpen = false if (!item) {
return []
}
if (Array.isArray(item.items)) {
return item.items
}
return Array.isArray(item.children) ? item.children : []
}
shared function dropdownGroupItems(item) {
if (!item) {
return []
}
if (Array.isArray(item.items)) {
return item.items
}
return Array.isArray(item.children) ? item.children : []
}
shared function megaData(item) {
return item && item.mega && typeof item.mega === "object" ? item.mega : item || {}
}
shared function megaColumns(item) {
const data = megaData(item)
if (Array.isArray(data.columns)) {
return data.columns
}
const legacy = dropdownItems(item)
if (!legacy.length) {
return []
}
const grouped = legacy.some(function (entry) {
return dropdownGroupItems(entry).length > 0
})
if (grouped) {
return legacy.map(function (entry) {
return {
heading: entry.label || "",
description: entry.description || "",
items: dropdownGroupItems(entry)
}
})
}
return [{ heading: item.description || "", items: legacy }]
}
client function setMobileNavigation(open, trigger) {
const root = trigger && trigger.closest ? trigger.closest("[data-wrn-navbar]") : null
if (!root) return
const collapse = root.querySelector(".wrn-navbar__collapse")
const toggle = root.querySelector(".wrn-navbar__toggle")
if (collapse) collapse.classList.toggle("is-open", open)
if (toggle) toggle.setAttribute("aria-expanded", open ? "true" : "false")
}
client function toggleNavigation(event) {
const open = event.currentTarget.getAttribute("aria-expanded") !== "true"
setMobileNavigation(open, event.currentTarget)
output.toggle({ open: open })
output[open ? "open" : "close"]({ source: "mobile" })
}
client function selectItem(item, level, event) {
setMobileNavigation(false, event.currentTarget)
output.select({ item: item, value: item.value || "", level: level }) output.select({ item: item, value: item.value || "", level: level })
} }
client function selectAction(item) { client function selectAction(item, event) {
mobileOpen = false setMobileNavigation(false, event.currentTarget)
output.action({ item: item, value: item.value || "" }) output.action({ item: item, value: item.value || "" })
} }
shared function isItemActive(item) { shared function isItemActive(item) {
if (!item) return false if (!item) {
if (item.value && item.value === active) return true return false
if (!item.children || !item.children.length) return false }
return item.children.some(function (child) { if (item.value && item.value === active) {
return true
}
if (itemType(item) === "mega") {
const data = megaData(item)
const links = Array.isArray(data.rail) ? data.rail.slice() : []
megaColumns(item).forEach(function (column) {
if (column && Array.isArray(column.items)) {
column.items.forEach(function (child) {
links.push(child)
})
}
})
return links.some(function (child) {
return child.value && child.value === active
})
}
const entries = dropdownItems(item)
if (!entries.length) {
return false
}
return entries.some(function (child) {
return isItemActive(child) return isItemActive(child)
}) })
} }
@@ -70,12 +165,13 @@ size: string = "default"
} }
view { view {
<header class="wrn-navbar wrn-navbar--width-{maxWidth} wrn-component--color-{color} wrn-component--size-{size} {sticky ? 'wrn-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}"> <header class="wrn-navbar wrn-navbar--width-{maxWidth} wrn-component--color-{color} wrn-component--size-{size} {sticky ? 'wrn-navbar--sticky' : ''} {floating ? 'wrn-navbar--floating' : ''} {class}" style="--wrn-navbar-floating-start: {startFloating};" data-wrn-navbar data-open-on-hover="{openOnHover}" data-floating="{floating ? 'true' : 'false'}" data-collapse-at="{collapseAt}" data-mobile-panel="{mobilePanel}" data-mobile-utilities-position="{mobileUtilitiesPosition}" data-gutter="{gutter}" data-content-gutter="{contentGutter}" data-flush-mobile="{flushMobile ? 'true' : 'false'}">
<div class="wrn-navbar__topbar" aria-label="{topbarLabel}"> <div class="wrn-navbar__topbar" aria-label="{topbarLabel}">
<slot name="topbar" /> <slot name="topbar" />
</div> </div>
<div class="wrn-navbar__main"> <div class="wrn-navbar__main">
{#if brand.logo || brand.icon || brand.label || brand.description}
<a class="wrn-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}"> <a class="wrn-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}">
{#if brand.logo} {#if brand.logo}
<img class="wrn-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" /> <img class="wrn-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" />
@@ -90,15 +186,39 @@ size: string = "default"
</span> </span>
{/if} {/if}
</a> </a>
{/if}
<button type="button" class="wrn-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation()"> <slot name="brand" />
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
</button>
<div class="wrn-navbar__collapse {mobileOpen ? 'is-open' : ''}"> <div class="wrn-navbar__collapse {mobileOpen ? 'is-open' : ''}">
<nav class="wrn-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal"> <nav class="wrn-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
{#each items as item} {#each items as item}
{#if item.children && item.children.length} {#if itemType(item) === "mega"}
<MegaMenu
class="wrn-navbar__mega"
data-active="{isItemActive(item) ? 'true' : 'false'}"
label="{item.label}"
icon="{item.icon || ''}"
columns={megaColumns(item)}
rail={megaData(item).rail || []}
featured={megaData(item).featured || {}}
variant="{megaData(item).variant || 'default'}"
density="{megaData(item).density || 'default'}"
panelWidth="{megaData(item).panelWidth || 'xl'}"
panelAlign="{megaData(item).panelAlign || 'start'}"
columnCount={megaData(item).columnCount || 0}
fullWidth={megaData(item).fullWidth || false}
openOnHover={openOnHover}
activeCategory="{megaData(item).activeCategory || ''}"
actionLabel="{megaData(item).actionLabel || ''}"
actionHref="{megaData(item).actionHref || ''}"
actionIcon="{megaData(item).actionIcon || 'icon-[lucide--arrow-right]'}"
footer="{megaData(item).footer || ''}"
/>
<a class="wrn-navbar__menu-link wrn-navbar__mobile-mega-link" data-wrn-roving-item="true" href="{item.href || megaData(item).actionHref || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{isItemActive(item) ? 'page' : 'false'}" @click="selectItem(item, 1, event)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
</a>
{:else if itemType(item) === "dropdown"}
<details class="wrn-navbar__dropdown wrn-navbar__dropdown--{item.type || 'dropdown'}" name="wrn-navbar-menu" @toggle="toggleDropdown(event, item)"> <details class="wrn-navbar__dropdown wrn-navbar__dropdown--{item.type || 'dropdown'}" name="wrn-navbar-menu" @toggle="toggleDropdown(event, item)">
<summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}"> <summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if} {#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
@@ -107,19 +227,19 @@ size: string = "default"
</summary> </summary>
<div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}"> <div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}">
{#if item.description}<p class="wrn-navbar__panel-intro" data-t="{translationKey(item, 'description')}">{item.description}</p>{/if} {#if item.description}<p class="wrn-navbar__panel-intro" data-t="{translationKey(item, 'description')}">{item.description}</p>{/if}
{#each item.children as child} {#each dropdownItems(item) as child}
<div class="wrn-navbar__group"> <div class="wrn-navbar__group">
{#if child.children && child.children.length} {#if dropdownGroupItems(child).length}
{#if child.label}<strong class="wrn-navbar__group-title" data-t="{translationKey(child, 'label')}">{child.label}</strong>{/if} {#if child.label}<strong class="wrn-navbar__group-title" data-t="{translationKey(child, 'label')}">{child.label}</strong>{/if}
{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if} {#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}
{#each child.children as nested} {#each dropdownGroupItems(child) as nested}
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3)"> <a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3, event)">
{#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if} {#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if}
<span><strong data-t="{translationKey(nested, 'label')}">{nested.label}</strong>{#if nested.description}<small data-t="{translationKey(nested, 'description')}">{nested.description}</small>{/if}</span> <span><strong data-t="{translationKey(nested, 'label')}">{nested.label}</strong>{#if nested.description}<small data-t="{translationKey(nested, 'description')}">{nested.description}</small>{/if}</span>
</a> </a>
{/each} {/each}
{:else} {:else}
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @click="selectItem(child, 2)"> <a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @click="selectItem(child, 2, event)">
{#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if} {#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if}
<span><strong data-t="{translationKey(child, 'label')}">{child.label}</strong>{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}</span> <span><strong data-t="{translationKey(child, 'label')}">{child.label}</strong>{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}</span>
</a> </a>
@@ -129,7 +249,7 @@ size: string = "default"
</div> </div>
</details> </details>
{:else} {:else}
<a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)"> <a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1, event)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if} {#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span data-t="{translationKey(item, 'label')}">{item.label}</span> <span data-t="{translationKey(item, 'label')}">{item.label}</span>
</a> </a>
@@ -138,15 +258,21 @@ size: string = "default"
</nav> </nav>
<div class="wrn-navbar__actions"> <div class="wrn-navbar__actions">
<slot name="actions" />
{#each actions as item} {#each actions as item}
<a class="wrn-navbar__action wrn-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item)"> <a class="wrn-navbar__action wrn-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item, event)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if} {#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span data-t="{translationKey(item, 'label')}">{item.label}</span> <span data-t="{translationKey(item, 'label')}">{item.label}</span>
</a> </a>
{/each} {/each}
<slot name="actions" />
</div> </div>
</div> </div>
<div class="wrn-navbar__utilities"><slot name="utilities" /></div>
<button type="button" class="wrn-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation(event)">
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
</button>
</div> </div>
</header> </header>
} }
@@ -155,7 +281,7 @@ size: string = "default"
/* Full application navigation */ /* Full application navigation */
.wrn-navbar { .wrn-navbar {
position: relative; position: relative;
z-index: 40; z-index: 1000;
width: 100%; width: 100%;
color: var(--wrn-color-text); color: var(--wrn-color-text);
background: color-mix(in srgb, var(--wrn-color-surface) 96%, transparent); background: color-mix(in srgb, var(--wrn-color-surface) 96%, transparent);
@@ -168,12 +294,19 @@ size: string = "default"
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
} }
.wrn-navbar__topbar:empty, .wrn-navbar--floating {
.wrn-navbar__topbar:not(:has(*)) { position: sticky;
z-index: 1000;
top: var(--wrn-navbar-floating-start, 0px);
box-shadow: 0 10px 30px rgb(15 23 42 / 0.08);
backdrop-filter: blur(16px);
}
.wrn-navbar__topbar:not(:has(> *)) {
display: none; display: none;
} }
.wrn-navbar__topbar:not(:empty) { .wrn-navbar__topbar:has(> *) {
display: flex; display: flex;
min-height: 2.25rem; min-height: 2.25rem;
align-items: center; align-items: center;
@@ -188,6 +321,7 @@ size: string = "default"
} }
.wrn-navbar__main { .wrn-navbar__main {
position: relative;
display: flex; display: flex;
min-height: 4.25rem; min-height: 4.25rem;
width: min(100%, 90rem); width: min(100%, 90rem);
@@ -197,6 +331,17 @@ size: string = "default"
gap: 1.5rem; gap: 1.5rem;
} }
.wrn-navbar[data-gutter="sm"] .wrn-navbar__main { padding-inline: 1rem; }
.wrn-navbar[data-gutter="lg"] .wrn-navbar__main { padding-inline: clamp(1.5rem, 5vw, 4rem); }
.wrn-navbar__utilities {
display: inline-flex;
align-items: center;
flex: none;
}
.wrn-navbar__utilities:not(:has(> *)) { display: none; }
.wrn-navbar--width-full .wrn-navbar__main { .wrn-navbar--width-full .wrn-navbar__main {
width: 100%; width: 100%;
max-width: none; max-width: none;
@@ -265,20 +410,59 @@ size: string = "default"
.wrn-navbar__collapse { .wrn-navbar__collapse {
min-width: 0; min-width: 0;
flex: 1; flex: 1;
justify-content: flex-end;
gap: 1rem; gap: 1rem;
} }
.wrn-navbar__menus { .wrn-navbar__menus {
position: absolute;
right: 0;
left: 0;
justify-content: center; justify-content: center;
flex: 1; pointer-events: none;
gap: 0.25rem; gap: 0.25rem;
} }
.wrn-navbar__menus > * {
pointer-events: auto;
}
.wrn-navbar__actions { .wrn-navbar__actions {
justify-content: flex-end; justify-content: flex-end;
gap: 0.5rem; gap: 0.5rem;
} }
.wrn-navbar__mega {
flex: 0 0 auto;
}
.wrn-navbar__mega .wrn-mega__trigger {
min-height: 2.75rem;
padding: 0.65rem 0.8rem;
border-radius: 0.65rem;
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel {
position: fixed;
top: calc(var(--wrn-navbar-floating-start, 0px) + 4.25rem);
right: 1rem;
left: 1rem;
width: auto;
max-width: none;
}
.wrn-navbar__mega[data-variant="default"][data-full-width="true"] .wrn-mega__panel {
position: absolute;
top: calc(100% + 0.4rem);
right: auto;
left: 0;
width: max-content;
min-width: 15rem;
max-width: min(24rem, calc(100dvw - 2rem));
}
.wrn-navbar__menu-link, .wrn-navbar__menu-link,
.wrn-navbar__dropdown > summary, .wrn-navbar__dropdown > summary,
.wrn-navbar__action { .wrn-navbar__action {
@@ -295,13 +479,15 @@ size: string = "default"
} }
.wrn-navbar__menu-link:hover, .wrn-navbar__menu-link:hover,
.wrn-navbar__dropdown > summary:hover { .wrn-navbar__dropdown > summary:hover,
.wrn-navbar__mega .wrn-mega__trigger:hover {
color: var(--wrn-color-text); color: var(--wrn-color-text);
background: var(--wrn-color-surface-2); background: var(--wrn-color-surface-2);
} }
.wrn-navbar__menu-link[aria-current="page"], .wrn-navbar__menu-link[aria-current="page"],
.wrn-navbar__dropdown > summary[aria-current="page"] { .wrn-navbar__dropdown > summary[aria-current="page"],
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger {
color: var(--wrn-component-color, var(--wrn-color-primary)); color: var(--wrn-component-color, var(--wrn-color-primary));
font-weight: 600; font-weight: 600;
background: color-mix( background: color-mix(
@@ -313,7 +499,8 @@ size: string = "default"
} }
.wrn-navbar__menu-link[aria-current="page"]:hover, .wrn-navbar__menu-link[aria-current="page"]:hover,
.wrn-navbar__dropdown > summary[aria-current="page"]:hover { .wrn-navbar__dropdown > summary[aria-current="page"]:hover,
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger:hover {
color: var(--wrn-component-color, var(--wrn-color-primary)); color: var(--wrn-component-color, var(--wrn-color-primary));
background: color-mix( background: color-mix(
in srgb, in srgb,
@@ -349,6 +536,7 @@ size: string = "default"
.wrn-navbar__panel { .wrn-navbar__panel {
position: absolute; position: absolute;
z-index: 1100;
top: calc(100% + 0.55rem); top: calc(100% + 0.55rem);
left: 50%; left: 50%;
display: grid; display: grid;
@@ -508,7 +696,13 @@ size: string = "default"
background: currentColor; background: currentColor;
} }
.wrn-navbar__mobile-mega-link { display: none; }
@media (max-width: 900px) { @media (max-width: 900px) {
.wrn-navbar__main {
min-height: 3.5rem;
padding-block: 0.4rem;
}
.wrn-navbar--width-compact .wrn-navbar__main { .wrn-navbar--width-compact .wrn-navbar__main {
width: 100%; width: 100%;
} }
@@ -527,20 +721,39 @@ size: string = "default"
background: var(--wrn-color-surface); background: var(--wrn-color-surface);
border-bottom: 1px solid var(--wrn-color-border); border-bottom: 1px solid var(--wrn-color-border);
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12); box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
max-height: calc(100dvh - 4rem); height: auto;
max-height: calc(100dvh - 3.5rem);
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
overscroll-behavior: contain; overscroll-behavior: contain;
} }
.wrn-navbar[data-mobile-panel="sheet"] .wrn-navbar__collapse {
height: calc(100dvh - 3.5rem);
}
.wrn-navbar__collapse.is-open { .wrn-navbar__collapse.is-open {
display: flex; display: flex;
} }
.wrn-navbar:has(.wrn-navbar__collapse.is-open)::after {
position: fixed;
z-index: -1;
inset: 0;
background: rgb(15 23 42 / 0.42);
content: "";
}
.wrn-navbar__menus, .wrn-navbar__menus,
.wrn-navbar__actions { .wrn-navbar__actions {
position: static;
transform: none;
width: 100%; width: 100%;
align-items: stretch; align-items: stretch;
flex-direction: column; flex-direction: column;
} }
.wrn-navbar__mega,
.wrn-navbar__mega .wrn-mega__trigger {
width: 100%;
}
.wrn-navbar__mega { display: none; }
.wrn-navbar__mobile-mega-link { display: flex; }
.wrn-navbar__menu-link, .wrn-navbar__menu-link,
.wrn-navbar__dropdown > summary, .wrn-navbar__dropdown > summary,
.wrn-navbar__action { .wrn-navbar__action {
@@ -557,6 +770,12 @@ size: string = "default"
overflow: hidden; overflow: hidden;
animation: wrn-navbar-mobile-panel 180ms ease both; animation: wrn-navbar-mobile-panel 180ms ease both;
} }
.wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel {
position: static;
width: 100%;
max-width: 100%;
margin-top: 0.25rem;
}
.wrn-navbar__dropdown:not([open]) > .wrn-navbar__panel, .wrn-navbar__dropdown:not([open]) > .wrn-navbar__panel,
.wrn-navbar__dropdown[open] > .wrn-navbar__panel { .wrn-navbar__dropdown[open] > .wrn-navbar__panel {
transform: none; transform: none;
@@ -569,7 +788,43 @@ size: string = "default"
} }
} }
@media (max-width: 1100px) and (min-width: 901px) {
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__toggle { display: block; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__collapse {
position: absolute;
top: 100%;
right: 0;
left: 0;
display: none;
max-height: calc(100dvh - 3.5rem);
padding: 0.75rem 1rem 1rem;
align-items: stretch;
flex-direction: column;
overflow-y: auto;
background: var(--wrn-color-surface);
border-bottom: 1px solid var(--wrn-color-border);
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
}
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__collapse.is-open { display: flex; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__menus,
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__actions {
position: static;
transform: none;
width: 100%;
align-items: stretch;
flex: none;
flex-direction: column;
}
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__menu-link, .wrn-navbar__dropdown > summary, .wrn-navbar__action) { width: 100%; }
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__mega, .wrn-navbar__mega .wrn-mega__trigger) { width: 100%; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mega { display: none; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mobile-mega-link { display: flex; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__panel { position: static; width: 100%; max-width: none; transform: none; }
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__mega[data-full-width="true"] .wrn-mega__panel { position: static; width: 100%; max-width: 100%; margin-top: 0.25rem; }
}
@media (max-width: 600px) { @media (max-width: 600px) {
.wrn-navbar[data-flush-mobile="false"] .wrn-navbar__main { padding-inline: 1rem; }
.wrn-navbar__brand-copy small { .wrn-navbar__brand-copy small {
display: none; display: none;
} }
+8
View File
@@ -576,6 +576,14 @@ current: true
display: none; display: none;
} }
/* Compiled empty slots retain inert template markers, so :empty alone
cannot collapse their spacing in the rendered document. */
.wrn-page-header__meta:not(:has(> :not(template))),
.wrn-page-header__body:not(:has(> :not(template))),
.wrn-page-header__actions--slot-only:not(:has(> :not(template))) {
display: none;
}
.wrn-page-header__meta { .wrn-page-header__meta {
margin-top: 1.1rem; margin-top: 1.1rem;
} }
+28 -9
View File
@@ -25,6 +25,13 @@ size: string = "default"
{"label": "Amber", "value": "amber", "hex": "#d97706"} {"label": "Amber", "value": "amber", "hex": "#d97706"}
] ]
compact: boolean = true compact: boolean = true
iconOnly: boolean = true
themeIcon: string = "icon-[lucide--sun-moon]"
colorIcon: string = "icon-[lucide--palette]"
languageIcon: string = "icon-[lucide--languages]"
panelPlacement: string = "bottom-end"
mobilePanel: string = "dropdown"
showCurrentLanguage: boolean = true
class: string = "" class: string = ""
} }
@@ -37,11 +44,11 @@ size: string = "default"
} }
view { view {
<div class="wrn-preferences wrn-component--color-{color} wrn-component--size-{size} {compact ? 'wrn-preferences--compact' : ''} {class}" data-wrn-preferences aria-label="Display and language preferences"> <div class="wrn-preferences wrn-component--color-{color} wrn-component--size-{size} {compact ? 'wrn-preferences--compact' : ''} {class}" data-wrn-preferences data-icon-only="{iconOnly ? 'true' : 'false'}" data-panel-placement="{panelPlacement}" data-mobile-panel="{mobilePanel}" aria-label="Display and language preferences">
<details class="wrn-preferences__menu" name="wrn-preference-menu"> <details class="wrn-preferences__menu" name="wrn-preference-menu">
<summary aria-label="{themeLabel}" title="{themeLabel}"> <summary aria-label="{themeLabel}" title="{themeLabel}">
<span class="icon-[lucide--sun-moon]" aria-hidden="true"></span> <span class="{themeIcon}" aria-hidden="true"></span>
{#if !compact}<span>{themeLabel}</span>{/if} {#if !iconOnly && !compact}<span>{themeLabel}</span>{/if}
</summary> </summary>
<div class="wrn-preferences__panel"> <div class="wrn-preferences__panel">
<strong>{themeLabel}</strong> <strong>{themeLabel}</strong>
@@ -54,8 +61,8 @@ size: string = "default"
<details class="wrn-preferences__menu" name="wrn-preference-menu"> <details class="wrn-preferences__menu" name="wrn-preference-menu">
<summary aria-label="{colorLabel}" title="{colorLabel}"> <summary aria-label="{colorLabel}" title="{colorLabel}">
<span class="icon-[lucide--palette]" aria-hidden="true"></span> <span class="{colorIcon}" aria-hidden="true"></span>
{#if !compact}<span>{colorLabel}</span>{/if} {#if !iconOnly && !compact}<span>{colorLabel}</span>{/if}
</summary> </summary>
<div class="wrn-preferences__panel wrn-preferences__panel--colors"> <div class="wrn-preferences__panel wrn-preferences__panel--colors">
<strong>{colorLabel}</strong> <strong>{colorLabel}</strong>
@@ -69,9 +76,9 @@ size: string = "default"
<details class="wrn-preferences__menu wrn-preferences__menu--language" name="wrn-preference-menu"> <details class="wrn-preferences__menu wrn-preferences__menu--language" name="wrn-preference-menu">
<summary aria-label="{languageLabel}" title="{languageLabel}"> <summary aria-label="{languageLabel}" title="{languageLabel}">
<span class="icon-[lucide--languages]" aria-hidden="true"></span> <span class="{languageIcon}" aria-hidden="true"></span>
<span class="wrn-preferences__current-language" aria-hidden="true">EN</span> {#if showCurrentLanguage}<span class="wrn-preferences__current-language" aria-hidden="true">EN</span>{/if}
{#if !compact}<span>{languageLabel}</span>{/if} {#if !iconOnly && !compact}<span>{languageLabel}</span>{/if}
</summary> </summary>
<div class="wrn-preferences__panel wrn-preferences__panel--language"> <div class="wrn-preferences__panel wrn-preferences__panel--language">
<strong>{languageLabel}</strong> <strong>{languageLabel}</strong>
@@ -157,6 +164,9 @@ size: string = "default"
animation: wrn-component-enter 160ms ease both; animation: wrn-component-enter 160ms ease both;
} }
.wrn-preferences[data-panel-placement="bottom-start"] .wrn-preferences__panel { right: auto; left: 0; }
.wrn-preferences[data-panel-placement="bottom-center"] .wrn-preferences__panel { right: auto; left: 50%; transform: translateX(-50%); }
.wrn-preferences__panel > strong { .wrn-preferences__panel > strong {
padding-inline: 0.25rem; padding-inline: 0.25rem;
font-size: 0.7rem; font-size: 0.7rem;
@@ -242,7 +252,7 @@ size: string = "default"
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.wrn-preferences__panel { .wrn-preferences[data-mobile-panel="sheet"] .wrn-preferences__panel {
position: fixed; position: fixed;
top: auto; top: auto;
right: 1rem; right: 1rem;
@@ -250,6 +260,15 @@ size: string = "default"
left: 1rem; left: 1rem;
width: auto; width: auto;
} }
.wrn-preferences[data-mobile-panel="dropdown"] .wrn-preferences__panel {
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
bottom: auto;
left: auto;
width: max-content;
max-width: min(22rem, calc(100vw - 2rem));
}
} }
/* Shared component foundation. */ /* Shared component foundation. */
+2
View File
@@ -365,7 +365,9 @@ component RangeSlider {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+12
View File
@@ -24,6 +24,10 @@ component Scrollspy {
active: string = "" active: string = ""
label: string = "On this page" label: string = "On this page"
heading: string = "" heading: string = ""
sticky: boolean = false
topOffset: string = "6rem"
sectionOffset: string = "7rem"
collapseAt: string = "800px"
class: string = "" class: string = ""
} }
@@ -44,6 +48,7 @@ component Scrollspy {
class='wrn-scrollspy {class}' class='wrn-scrollspy {class}'
data-color='{color}' data-color='{color}'
data-size='{size}' data-size='{size}'
data-sticky='{sticky ? "true" : "false"}' data-collapse-at='{collapseAt}' style='--wrn-scrollspy-top: {topOffset}; --wrn-scrollspy-section-offset: {sectionOffset};'
data-wrn-scrollspy="true" data-wrn-scrollspy="true"
role="navigation" role="navigation"
aria-label='{label}' aria-label='{label}'
@@ -74,6 +79,12 @@ component Scrollspy {
--scrollspy-accent: var(--wrn-color-primary); --scrollspy-accent: var(--wrn-color-primary);
max-width: 100%; max-width: 100%;
} }
.wrn-scrollspy[data-sticky="true"] { position: sticky; top: var(--wrn-scrollspy-top); height: max-content; }
.wrn-scrollspy__link { scroll-margin-top: var(--wrn-scrollspy-section-offset); }
.wrn-scrollspy:not(:has(.wrn-scrollspy__item)):not(:has(slot > *)) {
display: none;
}
.wrn-scrollspy[data-color="secondary"] { .wrn-scrollspy[data-color="secondary"] {
--scrollspy-accent: var(--wrn-color-secondary); --scrollspy-accent: var(--wrn-color-secondary);
@@ -148,6 +159,7 @@ component Scrollspy {
* line rather than a screenful. * line rather than a screenful.
*/ */
@media (max-width: 767px) { @media (max-width: 767px) {
.wrn-scrollspy[data-collapse-at="800px"] { position: static; }
.wrn-scrollspy__list { .wrn-scrollspy__list {
grid-auto-flow: column; grid-auto-flow: column;
grid-auto-columns: max-content; grid-auto-columns: max-content;
+1
View File
@@ -54,6 +54,7 @@ component Section {
padding-block: 3rem; padding-block: 3rem;
background: var(--section-tint); background: var(--section-tint);
color: var(--section-ink); color: var(--section-ink);
scroll-margin-top: var(--wrn-sticky-header-offset, 5rem);
} }
.wrn-section[data-spacing="sm"] { .wrn-section[data-spacing="sm"] {
+16
View File
@@ -20,6 +20,14 @@ component SplitHero {
variant: string = "default" variant: string = "default"
maxWidth: string = "xl" maxWidth: string = "xl"
fullBleed: boolean = true fullBleed: boolean = true
gutter: string = "md"
contentGutter: string = "md"
flushMobile: boolean = false
titleSize: string = "default"
titleMaxWidth: string = ""
descriptionWeight: string = "default"
mobileTitleSize: string = "default"
mobileStack: boolean = true
backgroundImage: string = "" backgroundImage: string = ""
visualImage: string = "" visualImage: string = ""
visualAlt: string = "" visualAlt: string = ""
@@ -45,6 +53,8 @@ component SplitHero {
data-variant='{variant}' data-variant='{variant}'
data-max-width='{maxWidth}' data-max-width='{maxWidth}'
data-full-bleed='{fullBleed ? "true" : "false"}' data-full-bleed='{fullBleed ? "true" : "false"}'
data-gutter='{gutter}' data-content-gutter='{contentGutter}' data-flush-mobile='{flushMobile ? "true" : "false"}' data-title-size='{titleSize}' data-description-weight='{descriptionWeight}' data-mobile-title-size='{mobileTitleSize}' data-mobile-stack='{mobileStack ? "true" : "false"}'
style='--wrn-split-title-max-width: {titleMaxWidth || "18ch"}'
data-visual-style='{visualStyle}' data-visual-style='{visualStyle}'
class='wrn-split-hero {class}' class='wrn-split-hero {class}'
> >
@@ -413,6 +423,7 @@ component SplitHero {
} }
.wrn-split-hero__title { .wrn-split-hero__title {
max-width: var(--wrn-split-title-max-width);
max-width: 15ch; max-width: 15ch;
margin: 1.45rem 0 0; margin: 1.45rem 0 0;
color: var(--wrn-color-text); color: var(--wrn-color-text);
@@ -421,6 +432,8 @@ component SplitHero {
line-height: 1.02; line-height: 1.02;
letter-spacing: -0.05em; letter-spacing: -0.05em;
} }
.wrn-split-hero[data-title-size="xl"] .wrn-split-hero__title { font-size: clamp(3rem, 5.5vw, 5rem); }
.wrn-split-hero[data-description-weight="medium"] .wrn-split-hero__description { font-weight: 500; }
.wrn-split-hero[data-size="compact"] .wrn-split-hero__title, .wrn-split-hero[data-size="compact"] .wrn-split-hero__title,
.wrn-split-hero[data-size="sm"] .wrn-split-hero__title { .wrn-split-hero[data-size="sm"] .wrn-split-hero__title {
@@ -771,6 +784,7 @@ component SplitHero {
.wrn-split-hero[data-ratio="content"] .wrn-split-hero__layout { .wrn-split-hero[data-ratio="content"] .wrn-split-hero__layout {
grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
} }
.wrn-split-hero[data-ratio="wide-copy"] .wrn-split-hero__layout { grid-template-columns: minmax(0, 1.12fr) minmax(20rem, 0.88fr); }
.wrn-split-hero[data-ratio="visual"] .wrn-split-hero__layout { .wrn-split-hero[data-ratio="visual"] .wrn-split-hero__layout {
grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
@@ -786,6 +800,8 @@ component SplitHero {
} }
@media (max-width: 639px) { @media (max-width: 639px) {
.wrn-split-hero[data-flush-mobile="false"] .wrn-split-hero__inner { width: calc(100% - 2rem); }
.wrn-split-hero[data-mobile-title-size="lg"] .wrn-split-hero__title { font-size: clamp(2.5rem, 12vw, 3.5rem); }
.wrn-split-hero__inner { .wrn-split-hero__inner {
padding-block: 3.25rem; padding-block: 3.25rem;
} }
+47 -4
View File
@@ -9,6 +9,11 @@ component StatsBar {
color: string = "primary" color: string = "primary"
variant: string = "raised" variant: string = "raised"
maxWidth: string = "xl" maxWidth: string = "xl"
radius: string = "default"
shadow: string = "default"
gutter: string = "md"
outerSpacing: string = "none"
mobileColumns: number = 1
class: string = "" class: string = ""
} }
@@ -24,6 +29,7 @@ component StatsBar {
data-color='{color}' data-color='{color}'
data-variant='{variant}' data-variant='{variant}'
data-max-width='{maxWidth}' data-max-width='{maxWidth}'
data-radius='{radius}' data-shadow='{shadow}' data-gutter='{gutter}' data-outer-spacing='{outerSpacing}' data-mobile-columns='{mobileColumns}'
class='wrn-stats-bar {class}' class='wrn-stats-bar {class}'
aria-label="Key statistics" aria-label="Key statistics"
> >
@@ -91,6 +97,8 @@ component StatsBar {
</div> </div>
{/if} {/if}
</article> </article>
{:empty}
<div class="wrn-stats-bar__empty" role="status">No statistics are available.</div>
{/each} {/each}
</div> </div>
</div> </div>
@@ -98,6 +106,15 @@ component StatsBar {
} }
style { style {
.wrn-stats-bar__empty {
grid-column: 1 / -1;
padding: 1.25rem;
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px dashed var(--wrn-color-border);
border-radius: 0.9rem;
text-align: center;
}
.wrn-stats-bar { .wrn-stats-bar {
--stats-accent: var(--wrn-color-primary); --stats-accent: var(--wrn-color-primary);
--stats-contrast: var(--wrn-color-primary-contrast); --stats-contrast: var(--wrn-color-primary-contrast);
@@ -135,21 +152,43 @@ component StatsBar {
} }
.wrn-stats-bar__inner { .wrn-stats-bar__inner {
width: min(calc(100% - 2rem), 80rem); --stats-max-width: 80rem;
--stats-gutter-width: 0rem;
width: min(
calc(100% - var(--stats-gutter-width)),
calc(var(--stats-max-width) - var(--stats-gutter-width))
);
margin-inline: auto; margin-inline: auto;
} }
.wrn-stats-bar[data-gutter="sm"] .wrn-stats-bar__inner {
--stats-gutter-width: 2rem;
}
.wrn-stats-bar[data-gutter="md"] .wrn-stats-bar__inner {
--stats-gutter-width: 4rem;
}
.wrn-stats-bar[data-gutter="lg"] .wrn-stats-bar__inner {
--stats-gutter-width: 6rem;
}
.wrn-stats-bar[data-outer-spacing="md"] { margin-block: 2rem; }
.wrn-stats-bar[data-radius="xl"] .wrn-stats-bar__surface { overflow: hidden; border-radius: 1.25rem; }
.wrn-stats-bar[data-shadow="sm"] .wrn-stats-bar__surface { box-shadow: 0 10px 30px rgb(15 23 42 / 0.05); }
.wrn-stats-bar[data-variant="card"] .wrn-stats-bar__surface { border: 1px solid var(--wrn-color-border); background: var(--wrn-color-surface); }
.wrn-stats-bar[data-max-width="compact"] .wrn-stats-bar__inner { .wrn-stats-bar[data-max-width="compact"] .wrn-stats-bar__inner {
width: min(calc(100% - 2rem), 64rem); --stats-max-width: 64rem;
} }
.wrn-stats-bar[data-max-width="lg"] .wrn-stats-bar__inner { .wrn-stats-bar[data-max-width="lg"] .wrn-stats-bar__inner {
width: min(calc(100% - 2rem), 72rem); --stats-max-width: 72rem;
} }
.wrn-stats-bar[data-max-width="wide"] .wrn-stats-bar__inner, .wrn-stats-bar[data-max-width="wide"] .wrn-stats-bar__inner,
.wrn-stats-bar[data-max-width="2xl"] .wrn-stats-bar__inner { .wrn-stats-bar[data-max-width="2xl"] .wrn-stats-bar__inner {
width: min(calc(100% - 2rem), 90rem); --stats-max-width: 90rem;
} }
.wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__inner { .wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__inner {
@@ -469,6 +508,10 @@ component StatsBar {
} }
@media (max-width: 639px) { @media (max-width: 639px) {
.wrn-stats-bar[data-mobile-columns="1"] .wrn-stats-bar__surface { grid-template-columns: 1fr; }
.wrn-stats-bar[data-gutter="sm"] .wrn-stats-bar__inner,
.wrn-stats-bar[data-gutter="md"] .wrn-stats-bar__inner { --stats-gutter-width: 2rem; }
.wrn-stats-bar[data-gutter="lg"] .wrn-stats-bar__inner { --stats-gutter-width: 3rem; }
.wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__surface { .wrn-stats-bar[data-max-width="full"] .wrn-stats-bar__surface {
border-inline: 0; border-inline: 0;
border-radius: 0; border-radius: 0;
@@ -649,7 +649,9 @@ component StrongPassword {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+17 -5
View File
@@ -41,6 +41,18 @@ component Tabs {
return String(item.value || item.id || index) return String(item.value || item.id || index)
} }
shared function tabId(item, index) {
return "wrn-tab-" + valueOf(item, index)
}
shared function panelId(item, index) {
return "wrn-panel-" + valueOf(item, index)
}
shared function panelSlot(item, index) {
return "panel-" + valueOf(item, index)
}
/* /*
* In url mode the query parameter is the source of truth, not local state. * In url mode the query parameter is the source of truth, not local state.
* *
@@ -130,8 +142,8 @@ component Tabs {
role="tab" role="tab"
data-value='{valueOf(item, index)}' data-value='{valueOf(item, index)}'
data-wrn-roving-item="true" data-wrn-roving-item="true"
id='wrn-tab-{valueOf(item, index)}' id='{tabId(item, index)}'
aria-controls='wrn-panel-{valueOf(item, index)}' aria-controls='{panelId(item, index)}'
aria-selected='{isSelected(item, index) ? "true" : "false"}' aria-selected='{isSelected(item, index) ? "true" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}' aria-disabled='{item.disabled ? "true" : "false"}'
@click='selectTab(item, index, event)' @click='selectTab(item, index, event)'
@@ -148,8 +160,8 @@ component Tabs {
<div <div
class="wrn-tabs__panel" class="wrn-tabs__panel"
role="tabpanel" role="tabpanel"
id='wrn-panel-{valueOf(item, index)}' id='{panelId(item, index)}'
aria-labelledby='wrn-tab-{valueOf(item, index)}' aria-labelledby='{tabId(item, index)}'
tabindex="0" tabindex="0"
data-show='isSelected(item, index)' data-show='isSelected(item, index)'
> >
@@ -158,7 +170,7 @@ component Tabs {
</h3> </h3>
<p class="wrn-tabs__description" data-show="item.description">{item.description}</p> <p class="wrn-tabs__description" data-show="item.description">{item.description}</p>
<div class="wrn-tabs__content" data-show="item.content">{item.content}</div> <div class="wrn-tabs__content" data-show="item.content">{item.content}</div>
<slot name="panel-{valueOf(item, index)}"></slot> <slot name='{panelSlot(item, index)}'></slot>
</div> </div>
{/each} {/each}
<slot /> <slot />
+2
View File
@@ -249,7 +249,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+28 -7
View File
@@ -10,6 +10,7 @@ component Timeline {
description: string = "" description: string = ""
items: unknown[] = [] items: unknown[] = []
variant: string = "default" variant: string = "default"
numbered: boolean = false
class: string = "" class: string = ""
} }
@@ -18,6 +19,9 @@ component Timeline {
data-ui-component="Timeline" data-ui-component="Timeline"
aria-label='{title}' aria-label='{title}'
class='wrn-timeline {class}' class='wrn-timeline {class}'
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
> >
{#if title || description} {#if title || description}
<header class="wrn-timeline__header"> <header class="wrn-timeline__header">
@@ -34,13 +38,18 @@ component Timeline {
{#each items as item, index} {#each items as item, index}
<li <li
class="wrn-timeline__item" class="wrn-timeline__item"
data-interactive='{item.href ? "true" : "false"}'
@click='output.select({ item: item, index: index })' @click='output.select({ item: item, index: index })'
> >
<span <span
class="wrn-timeline__marker" class="wrn-timeline__marker"
data-status='{item.status || "default"}' data-status='{item.status || "default"}'
> >
<span class='{item.icon || "icon-[lucide--circle]"}' aria-hidden="true"></span> {#if numbered}
<span class="wrn-timeline__number" aria-hidden="true">{index + 1}</span>
{:else}
<span class='{item.icon || "icon-[lucide--circle-check]"}' aria-hidden="true"></span>
{/if}
</span> </span>
<article <article
@@ -81,22 +90,28 @@ component Timeline {
} }
style { style {
.wrn-timeline { width: 100%; } .wrn-timeline { --wrn-timeline-accent: var(--wrn-color-primary); width: 100%; min-width: 0; color: var(--wrn-color-text); }
.wrn-timeline[data-color="success"] { --wrn-timeline-accent: var(--wrn-color-success); }
.wrn-timeline[data-color="info"] { --wrn-timeline-accent: var(--wrn-color-info); }
.wrn-timeline__header { margin-bottom: 1.5rem; } .wrn-timeline__header { margin-bottom: 1.5rem; }
.wrn-timeline__title { color: var(--wrn-color-text); font-size: 1.25rem; font-weight: 700; } .wrn-timeline__title { color: var(--wrn-color-text); font-size: 1.25rem; font-weight: 700; }
.wrn-timeline__description { max-width: 42rem; margin-top: 0.5rem; color: var(--wrn-color-text-muted); line-height: 1.75rem; } .wrn-timeline__description { max-width: 42rem; margin-top: 0.5rem; color: var(--wrn-color-text-muted); line-height: 1.75rem; }
.wrn-timeline__list { position: relative; margin-left: 1rem; border-left: 1px solid var(--wrn-color-border); } .wrn-timeline__list { position: relative; display: grid; gap: 1rem; margin: 0; padding: 0 0 0 3rem; list-style: none; }
.wrn-timeline__item { position: relative; padding: 0 0 2rem 2rem; cursor: pointer; } .wrn-timeline__list::before { position: absolute; inset-block: 1rem; inset-inline-start: 1rem; width: 2px; border-radius: 999px; background: color-mix(in srgb, var(--wrn-timeline-accent) 30%, var(--wrn-color-border)); content: ""; }
.wrn-timeline__item { position: relative; min-width: 0; }
.wrn-timeline__item:last-child { padding-bottom: 0; } .wrn-timeline__item:last-child { padding-bottom: 0; }
.wrn-timeline__marker { .wrn-timeline__marker {
position: absolute; top: 0; left: -1.05rem; display: flex; width: 2rem; height: 2rem; position: absolute; z-index: 1; top: 1rem; inset-inline-start: -3rem; display: flex; width: 2rem; height: 2rem;
align-items: center; justify-content: center; border: 4px solid var(--wrn-color-background); align-items: center; justify-content: center; border: 4px solid var(--wrn-color-background);
border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); box-shadow: var(--wrn-shadow-1); border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-timeline-accent); box-shadow: var(--wrn-shadow-1);
} }
.wrn-timeline__marker:is([data-status="complete"], [data-status="success"]) { background: var(--wrn-color-success); } .wrn-timeline__marker:is([data-status="complete"], [data-status="success"]) { background: var(--wrn-color-success); }
.wrn-timeline__marker:is([data-status="warning"], [data-status="pending"]) { background: var(--wrn-color-warning); } .wrn-timeline__marker:is([data-status="warning"], [data-status="pending"]) { background: var(--wrn-color-warning); }
.wrn-timeline__marker:is([data-status="danger"], [data-status="failed"]) { background: var(--wrn-color-danger); } .wrn-timeline__marker:is([data-status="danger"], [data-status="failed"]) { background: var(--wrn-color-danger); }
.wrn-timeline__card { padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); } .wrn-timeline__number { font-size: 0.75rem; font-weight: 800; }
.wrn-timeline__card { min-width: 0; padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease; }
.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }
.wrn-timeline__item[data-interactive="true"]:hover .wrn-timeline__card { border-color: color-mix(in srgb, var(--wrn-timeline-accent) 45%, var(--wrn-color-border)); transform: translateY(-2px); box-shadow: var(--wrn-shadow-2); }
.wrn-timeline__card[data-variant="minimal"] { padding: 0; border-color: transparent; background: transparent; box-shadow: none; } .wrn-timeline__card[data-variant="minimal"] { padding: 0; border-color: transparent; background: transparent; box-shadow: none; }
.wrn-timeline__card-header { display: flex; flex-direction: column; gap: 0.5rem; } .wrn-timeline__card-header { display: flex; flex-direction: column; gap: 0.5rem; }
.wrn-timeline__item-title { color: var(--wrn-color-text); font-weight: 700; } .wrn-timeline__item-title { color: var(--wrn-color-text); font-weight: 700; }
@@ -110,5 +125,11 @@ component Timeline {
@media (min-width: 40rem) { @media (min-width: 40rem) {
.wrn-timeline__card-header { flex-direction: row; align-items: flex-start; justify-content: space-between; } .wrn-timeline__card-header { flex-direction: row; align-items: flex-start; justify-content: space-between; }
} }
@media (max-width: 39.99rem) {
.wrn-timeline__list { padding-inline-start: 2.5rem; }
.wrn-timeline__list::before { inset-inline-start: 0.75rem; }
.wrn-timeline__marker { inset-inline-start: -2.5rem; }
.wrn-timeline__card { padding: 1rem; }
}
} }
} }
@@ -492,7 +492,9 @@ component TogglePassword {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -360,7 +360,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -315,7 +315,9 @@ component Input {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -221,7 +221,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -244,7 +244,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -243,7 +243,9 @@ component Switch {
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2
View File
@@ -223,7 +223,9 @@ size: string = "default"
} }
.wrn-next__field-heading label { .wrn-next__field-heading label {
color: var(--wrn-color-text); color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.35;
} }
.wrn-next__field-hint, .wrn-next__field-hint,
.wrn-next__field-help { .wrn-next__field-help {
+2 -1
View File
@@ -1,11 +1,12 @@
{ {
"name": "@wrnexus/ui", "name": "@wrnexus/ui",
"version": "0.8.23", "version": "0.8.56",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
".": "./src/index.ts", ".": "./src/index.ts",
"./navigation": "./src/navigation.ts",
"./registry": "./src/registry.ts", "./registry": "./src/registry.ts",
"./components/*": "./components/*", "./components/*": "./components/*",
"./component-catalog.json": "./component-catalog.json", "./component-catalog.json": "./component-catalog.json",
+19 -1
View File
@@ -24,4 +24,22 @@
* server-only subpath. Import from there instead if you need them. * server-only subpath. Import from there instead if you need them.
*/ */
export {}; export type {
MegaMenuColumn,
MegaMenuDensity,
MegaMenuFeatured,
MegaMenuPanelAlign,
MegaMenuPanelWidth,
MegaMenuRailItem,
MegaMenuVariant,
NavbarAction,
NavbarBrand,
NavbarDropdownGroup,
NavbarDropdownItem,
NavbarItem,
NavbarItemType,
NavbarLinkItem,
NavbarMegaMenuData,
NavbarMegaMenuItem,
NavigationLink,
} from "./navigation";
+121
View File
@@ -0,0 +1,121 @@
/** Shared, serializable navigation contracts for Navbar and application configs. */
export type NavbarItemType = "link" | "dropdown" | "mega";
export type MegaMenuVariant = "default" | "icon-grid" | "catalog" | "dark";
export type MegaMenuDensity = "compact" | "default" | "comfortable";
export type MegaMenuPanelWidth = "sm" | "md" | "lg" | "xl" | "2xl" | "full";
export type MegaMenuPanelAlign = "start" | "center" | "end";
export interface NavigationLink {
label: string;
href: string;
value?: string;
labelKey?: string;
description?: string;
descriptionKey?: string;
icon?: string;
image?: string;
imageAlt?: string;
eyebrow?: string;
badge?: string;
target?: string;
rel?: string;
disabled?: boolean;
}
export interface NavbarLinkItem extends NavigationLink {
type?: "link";
}
export interface NavbarDropdownGroup {
label?: string;
labelKey?: string;
description?: string;
descriptionKey?: string;
items: NavigationLink[];
/** Compatibility alias accepted by Navbar. Prefer `items`. */
children?: NavigationLink[];
}
export interface NavbarDropdownItem {
type: "dropdown";
label: string;
value?: string;
labelKey?: string;
description?: string;
descriptionKey?: string;
icon?: string;
columns?: number;
items: Array<NavigationLink | NavbarDropdownGroup>;
/** Compatibility alias accepted by Navbar. Prefer `items`. */
children?: Array<NavigationLink | NavbarDropdownGroup>;
}
export interface MegaMenuColumn {
heading?: string;
eyebrow?: string;
description?: string;
image?: string;
imageAlt?: string;
items: NavigationLink[];
actionLabel?: string;
actionHref?: string;
}
export interface MegaMenuRailItem extends NavigationLink {}
export interface MegaMenuFeatured {
eyebrow?: string;
image?: string;
imageAlt?: string;
title?: string;
description?: string;
actionLabel?: string;
actionHref?: string;
}
export interface NavbarMegaMenuData {
variant?: MegaMenuVariant;
density?: MegaMenuDensity;
panelWidth?: MegaMenuPanelWidth;
panelAlign?: MegaMenuPanelAlign;
fullWidth?: boolean;
activeCategory?: string;
columnCount?: number;
columns: MegaMenuColumn[];
rail?: MegaMenuRailItem[];
featured?: MegaMenuFeatured;
actionLabel?: string;
actionHref?: string;
actionIcon?: string;
footer?: string;
}
export interface NavbarMegaMenuItem {
type: "mega";
label: string;
value?: string;
labelKey?: string;
icon?: string;
mega: NavbarMegaMenuData;
}
export type NavbarItem = NavbarLinkItem | NavbarDropdownItem | NavbarMegaMenuItem;
export interface NavbarAction extends NavigationLink {
variant?: "link" | "outline" | "primary";
}
export interface NavbarBrand {
label?: string;
labelKey?: string;
description?: string;
descriptionKey?: string;
href?: string;
ariaLabel?: string;
icon?: string;
logo?: string;
logoAlt?: string;
logoWidth?: number | string;
logoHeight?: number | string;
}
+51
View File
@@ -0,0 +1,51 @@
import { expect, test } from "bun:test";
import type { NavbarItem } from "../src/navigation";
const items = [
{ type: "link", label: "Pricing", href: "/pricing" },
{
type: "dropdown",
label: "Company",
items: [{ label: "About", href: "/about" }],
},
{
type: "mega",
label: "Products",
mega: {
variant: "icon-grid",
density: "compact",
panelWidth: "2xl",
panelAlign: "center",
columnCount: 3,
fullWidth: true,
rail: [{ label: "All products", href: "/products", icon: "icon-products" }],
columns: [
{
heading: "Work",
items: [
{
label: "Projects",
href: "/projects",
icon: "icon-projects",
description: "Plan and deliver work",
badge: "New",
},
],
},
],
featured: {
title: "What's new",
image: "/new.png",
actionLabel: "Explore",
actionHref: "/new",
},
footer: "One connected platform",
actionLabel: "View all products",
actionHref: "/products",
},
},
] satisfies NavbarItem[];
test("navbar item contract covers links, dropdowns, and complete mega menus", () => {
expect(items.map((item) => item.type)).toEqual(["link", "dropdown", "mega"]);
});
@@ -78,6 +78,28 @@ function openingTags(source: string): string[] {
} }
describe("@wrnexus/ui redesign component pack", () => { describe("@wrnexus/ui redesign component pack", () => {
test("Hero full bleed does not use scrollbar-sensitive viewport units", async () => {
const source = await readFile(join(componentsDir, "Hero.wrn"), "utf8");
expect(source).not.toContain("width: 100vw");
expect(source).not.toContain("width: 100dvw");
expect(source).toContain('.wrn-hero[data-full-bleed="true"]');
});
test("Timeline supports numbered data items without styling every row as interactive", async () => {
const source = await readFile(join(componentsDir, "Timeline.wrn"), "utf8");
expect(source).toContain("numbered: boolean = false");
expect(source).toContain("data-interactive");
expect(source).toContain('data-interactive="true"');
expect(source).toContain('.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }');
});
test("Blockquote provides constrained contained presentation", async () => {
const source = await readFile(join(componentsDir, "Blockquote.wrn"), "utf8");
expect(source).toContain('maxWidth: string = "xl"');
expect(source).toContain('data-variant="spotlight"');
expect(source).toContain('data-max-width="full"');
});
test("contains every replacement and new component", async () => { test("contains every replacement and new component", async () => {
const files = new Set(await readdir(componentsDir)); const files = new Set(await readdir(componentsDir));
@@ -118,6 +140,77 @@ describe("@wrnexus/ui redesign component pack", () => {
} }
}); });
test("Navbar only displays its utility bar when the topbar slot has rendered content", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
expect(source).toContain(".wrn-navbar__topbar:not(:has(> *))");
expect(source).toContain(".wrn-navbar__topbar:has(> *)");
expect(source).not.toContain(".wrn-navbar__topbar:not(:empty)");
});
test("Navbar renders its brand prop independently from the optional brand slot", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
expect(source).toContain('<slot name="brand" />');
expect(source).toContain("brand.logo || brand.icon || brand.label || brand.description");
expect(source.indexOf("brand.logo || brand.icon || brand.label || brand.description")).toBeLessThan(
source.indexOf('<slot name="brand" />'),
);
});
test("Navbar renders slotted utilities before its built-in actions", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
const actionsStart = source.indexOf('<div class="wrn-navbar__actions">');
const slotIndex = source.indexOf('<slot name="actions" />', actionsStart);
const actionsLoop = source.indexOf("{#each actions as item}", actionsStart);
expect(slotIndex).toBeGreaterThan(actionsStart);
expect(slotIndex).toBeLessThan(actionsLoop);
});
test("Navbar passes structured MegaMenu props as expressions", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
expect(source).toContain("columns={megaColumns(item)}");
expect(source).toContain("rail={megaData(item).rail || []}");
expect(source).toContain("featured={megaData(item).featured || {}}");
expect(source).toContain("fullWidth={megaData(item).fullWidth || false}");
expect(source).not.toContain('columns="{megaColumns(item)}"');
});
test("Navbar replaces mega panels with direct links in collapsed navigation", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
expect(source).toContain("wrn-navbar__mobile-mega-link");
expect(source).toContain("item.href || megaData(item).actionHref");
expect(source).toContain('.wrn-navbar__mega { display: none; }');
expect(source).toContain('.wrn-navbar__mobile-mega-link { display: flex; }');
});
test("Navbar supports in-flow floating and elevated menu panels", async () => {
const source = await readFile(join(componentsDir, "Navbar.wrn"), "utf8");
const ast = parse(source);
const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default]));
expect(defaults.get("floating")).toBe("false");
expect(defaults.get("startFloating")).toBe('"0px"');
expect(source).toContain("wrn-navbar--floating");
expect(source).toContain("--wrn-navbar-floating-start");
expect(source).toContain("z-index: 1100");
});
test("CTASection exposes responsive section spacing", async () => {
const source = await readFile(join(componentsDir, "CTASection.wrn"), "utf8");
const ast = parse(source);
const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default]));
expect(defaults.get("spacing")).toBe('"none"');
expect(source).toContain("data-spacing='{spacing}'");
expect(source).toContain('.wrn-cta-section[data-spacing="sm"]');
expect(source).toContain('.wrn-cta-section[data-spacing="md"]');
expect(source).toContain('.wrn-cta-section[data-spacing="lg"]');
});
test("existing public defaults remain release compatible", async () => { test("existing public defaults remain release compatible", async () => {
const expectations: Record<string, string[]> = { const expectations: Record<string, string[]> = {
Container: [ Container: [
+191 -7
View File
@@ -32,6 +32,19 @@ test("Textarea binds its value without rendering hydration markup as user conten
expect(source).not.toContain(">{value}</textarea>"); expect(source).not.toContain(">{value}</textarea>");
}); });
test("form field labels use a stable control-label size instead of inherited heading scale", () => {
const fieldComponents = uiComponentNames().filter((name) =>
readFileSync(uiComponentPath(name), "utf8").includes(".wrn-next__field-heading label"),
);
expect(fieldComponents.length).toBeGreaterThan(0);
for (const name of fieldComponents) {
const source = readFileSync(uiComponentPath(name), "utf8");
expect(source).toMatch(
/\.wrn-next__field-heading label\s*\{[^}]*font-size: 0\.8125rem;[^}]*line-height: 1\.35;/s,
);
}
});
test("global UI CSS stays below its migration ratchet", () => { test("global UI CSS stays below its migration ratchet", () => {
const css = uiCss(); const css = uiCss();
// Component selectors belong to their owning .wrn files. Keep this asset to // Component selectors belong to their owning .wrn files. Keep this asset to
@@ -239,7 +252,9 @@ test("navbar supports brands, nested menus, actions, utility slots, and public e
expect(defaults.get("openOnHover")).toBe("false"); expect(defaults.get("openOnHover")).toBe("false");
expect(defaults.get("maxWidth")).toBe('"full"'); expect(defaults.get("maxWidth")).toBe('"full"');
expect(source).toContain("item.children"); expect(source).toContain("item.children");
expect(source).toContain("child.children"); expect(source).toContain("dropdownGroupItems(child)");
expect(source).toContain('itemType(item) === "mega"');
expect(source).toContain("megaData(item).featured");
expect(source).toContain("wrn-navbar__dropdown--{item.type || 'dropdown'}"); expect(source).toContain("wrn-navbar__dropdown--{item.type || 'dropdown'}");
const outputs = new Set(contract.outputs.map((output) => output.name)); const outputs = new Set(contract.outputs.map((output) => output.name));
for (const event of ["toggle", "open", "close", "select", "action"]) { for (const event of ["toggle", "open", "close", "select", "action"]) {
@@ -304,13 +319,19 @@ test("navbar renders a brand, nested dropdowns, mega groups, and actions", async
{ {
label: "Safety", label: "Safety",
type: "mega", type: "mega",
columns: 2, mega: {
children: [ variant: "icon-grid",
columnCount: 2,
rail: [{ label: "Overview", href: "/safety" }],
columns: [
{ {
label: "Resources", heading: "Resources",
children: [{ label: "Guidance", href: "/guidance" }], items: [{ label: "Guidance", href: "/guidance", badge: "New" }],
}, },
], ],
featured: { title: "Safety report", actionLabel: "Read report" },
actionLabel: "All safety resources",
},
}, },
], ],
actions: [{ label: "Sign in", href: "/login", variant: "primary" }], actions: [{ label: "Sign in", href: "/login", variant: "primary" }],
@@ -320,7 +341,9 @@ test("navbar renders a brand, nested dropdowns, mega groups, and actions", async
expect(html).toContain("Public portal"); expect(html).toContain("Public portal");
expect(html).toContain("Reports"); expect(html).toContain("Reports");
expect(html).toContain("Guidance"); expect(html).toContain("Guidance");
expect(html).toContain("wrn-navbar__dropdown--mega"); expect(html).toContain("wrn-navbar__mega");
expect(html).toContain("Safety report");
expect(html).toContain("All safety resources");
expect(html).toContain("data-wrn-navbar"); expect(html).toContain("data-wrn-navbar");
expect(html).toContain('name="wrn-navbar-menu"'); expect(html).toContain('name="wrn-navbar-menu"');
expect(html).toContain("Sign in"); expect(html).toContain("Sign in");
@@ -2856,8 +2879,13 @@ test("tabs use wrn classes and declare real outputs instead of raw events", asyn
expect(tabs).toHaveLength(2); expect(tabs).toHaveLength(2);
expect(tabs[0]!.getAttribute("aria-selected")).toBe("true"); expect(tabs[0]!.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]!.getAttribute("aria-selected")).toBe("false"); expect(tabs[1]!.getAttribute("aria-selected")).toBe("false");
expect(tabs[0]!.id).toBe("wrn-tab-overview");
expect(tabs[0]!.getAttribute("aria-controls")).toBe("wrn-panel-overview");
expect(dom.querySelector(".wrn-tabs")).not.toBeNull(); expect(dom.querySelector(".wrn-tabs")).not.toBeNull();
expect(dom.querySelectorAll('[role="tabpanel"]')).toHaveLength(2); const panels = dom.querySelectorAll('[role="tabpanel"]');
expect(panels).toHaveLength(2);
expect(panels[0]!.id).toBe("wrn-panel-overview");
expect(panels[0]!.getAttribute("aria-labelledby")).toBe("wrn-tab-overview");
}); });
test("tabs in url mode declare the query parameter they sync to", async () => { test("tabs in url mode declare the query parameter they sync to", async () => {
@@ -2918,6 +2946,10 @@ test("mega menu renders column groups in an anchored panel", async () => {
const trigger = dom.querySelector(".wrn-mega__trigger") as HTMLElement; const trigger = dom.querySelector(".wrn-mega__trigger") as HTMLElement;
expect(trigger.getAttribute("aria-expanded")).toBe("false"); expect(trigger.getAttribute("aria-expanded")).toBe("false");
expect(trigger.getAttribute("aria-haspopup")).toBe("true"); expect(trigger.getAttribute("aria-haspopup")).toBe("true");
expect(trigger.getAttribute("aria-label")).toBe("Products");
expect(dom.querySelector(".wrn-mega__trigger-label")?.getAttribute("data-label")).toBe(
"Products",
);
const panel = dom.querySelector(".wrn-mega__panel") as HTMLElement; const panel = dom.querySelector(".wrn-mega__panel") as HTMLElement;
expect(panel.getAttribute("data-wrn-anchored")).toBe("true"); expect(panel.getAttribute("data-wrn-anchored")).toBe("true");
@@ -2927,6 +2959,14 @@ test("mega menu renders column groups in an anchored panel", async () => {
// Single level by design: a mega panel shows breadth flat, so there is no // Single level by design: a mega panel shows breadth flat, so there is no
// nested submenu markup to find. // nested submenu markup to find.
expect(source).not.toContain("wrn-mega__submenu"); expect(source).not.toContain("wrn-mega__submenu");
expect(source).toContain('.wrn-mega[data-variant="default"] .wrn-mega__panel');
expect(source).toContain("box-sizing: border-box");
});
test("announcement full-width surface does not create viewport scrollbar overflow", () => {
const source = readFileSync(uiComponentPath("AnnouncementBar"), "utf8");
expect(source).not.toContain("width: 100vw");
expect(source).not.toContain("calc(50% - 50vw)");
}); });
test("mega menu columns take roving focus and survive an empty column list", async () => { test("mega menu columns take roving focus and survive an empty column list", async () => {
@@ -2938,6 +2978,76 @@ test("mega menu columns take roving focus and survive an empty column list", asy
expect(dom.querySelector('[data-wrn-roving="both"]')).not.toBeNull(); expect(dom.querySelector('[data-wrn-roving="both"]')).not.toBeNull();
}); });
test("catalog mega menu renders only the active rail category", async () => {
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
expect(source).toContain(".wrn-mega__list > li");
expect(source).toContain("overflow-wrap: anywhere;");
const html = await renderComponent(source, {
label: "Products",
variant: "catalog",
activeCategory: "sales",
rail: [
{ label: "Platform", value: "platform" },
{ label: "Sales", value: "sales" },
],
columns: [
{ heading: "Platform", items: [{ label: "Home", href: "/home" }] },
{ heading: "Sales", items: [{ label: "CRM", href: "/crm" }] },
],
});
const dom = mountHtml(html);
expect(dom.querySelectorAll(".wrn-mega__rail-link")).toHaveLength(2);
expect(dom.querySelectorAll(".wrn-mega__column")).toHaveLength(1);
expect(dom.querySelector(".wrn-mega__heading")?.textContent).toContain("Sales");
expect(dom.querySelector('[aria-pressed="true"]')?.textContent).toContain("Sales");
expect(source).toContain("overflow-y: auto");
expect(source).not.toContain("@mouseenter='chooseCategory(item, index)'");
expect(dom.querySelector(".wrn-mega__column-header")?.textContent).toContain("Sales");
expect(dom.querySelectorAll(".wrn-mega__link-icon")).toHaveLength(1);
expect(dom.querySelector(".wrn-mega__link-icon--fallback svg")).not.toBeNull();
expect(dom.querySelectorAll(".wrn-mega__link-description")).toHaveLength(0);
expect(source).toContain("border-bottom: 1px solid var(--wrn-color-border)");
expect(source).toContain(".wrn-mega__rail-icon > span");
expect(source).toContain("color: currentColor !important");
});
test("mega menu supports rails, icons, badges, media, featured content and footer actions", async () => {
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
const html = await renderComponent(source, {
label: "Explore",
variant: "icon-grid",
panelWidth: "2xl",
columnCount: 3,
rail: [{ label: "Enterprise", description: "For larger teams", icon: "icon-enterprise" }],
columns: [
{
heading: "Products",
description: "Choose a workspace",
image: "/products.png",
items: [{ label: "CRM", description: "Customer records", icon: "icon-crm", badge: "New" }],
actionLabel: "All products",
},
],
featured: {
eyebrow: "What's new",
title: "Release notes",
image: "/release.png",
actionLabel: "See more",
},
footer: "One connected platform",
actionLabel: "View everything",
});
const dom = mountHtml(html);
expect(dom.querySelector(".wrn-mega")?.getAttribute("data-variant")).toBe("icon-grid");
expect(dom.querySelector(".wrn-mega__rail-link")?.textContent).toContain("Enterprise");
expect(dom.querySelector(".wrn-mega__badge")?.textContent).toContain("New");
expect(dom.querySelector(".wrn-mega__column-image")).not.toBeNull();
expect(dom.querySelector(".wrn-mega__featured")?.textContent).toContain("Release notes");
expect(dom.querySelector(".wrn-mega__footer")?.textContent).toContain("View everything");
});
test("sidebar renders single items, labelled groups and nested levels", async () => { test("sidebar renders single items, labelled groups and nested levels", async () => {
const source = readFileSync(uiComponentPath("Sidebar"), "utf8"); const source = readFileSync(uiComponentPath("Sidebar"), "utf8");
const html = await renderComponent(source, { const html = await renderComponent(source, {
@@ -3027,6 +3137,8 @@ test("navbar uses a valid aria-current and takes roving focus on its menu", asyn
*/ */
expect(source).not.toContain("? 'page' : ''"); expect(source).not.toContain("? 'page' : ''");
expect(source).toContain("data-wrn-roving"); expect(source).toContain("data-wrn-roving");
expect(source).toContain("pointer-events: none;");
expect(source).toContain(".wrn-navbar__menus > *");
const html = await renderComponent(source, { const html = await renderComponent(source, {
label: "Main", label: "Main",
@@ -3066,6 +3178,38 @@ test("breadcrumb marks the trail end without emitting an empty aria-current", as
expect(dom.querySelectorAll(".wrn-breadcrumb__item").length).toBeGreaterThanOrEqual(3); expect(dom.querySelectorAll(".wrn-breadcrumb__item").length).toBeGreaterThanOrEqual(3);
}); });
test("breadcrumb renders a standalone active page with its icon", async () => {
const source = readFileSync(uiComponentPath("Breadcrumb"), "utf8");
const html = await renderComponent(source, {
showHome: true,
items: [{ label: "Products", href: "/products", icon: "icon-[lucide--blocks]" }],
active: "CRM",
activeIcon: "icon-[lucide--contact-round]",
});
expect(html).toContain("CRM");
expect(html).toContain("icon-[lucide--contact-round]");
expect(html).toMatch(/aria-current="page"/);
const dom = mountHtml(html);
expect(dom.querySelectorAll(".wrn-breadcrumb__item").length).toBe(3);
});
test("breadcrumb default separators remain visible without generated icon CSS", async () => {
const source = readFileSync(uiComponentPath("Breadcrumb"), "utf8");
const html = await renderComponent(source, {
showHome: true,
items: [{ label: "Legal" }, { label: "Terms", current: true }],
});
expect(html).toContain("");
expect(html).not.toContain("icon-[lucide--chevron-right]");
});
test("stats bar desktop gutters align its surface with container content", () => {
const source = readFileSync(uiComponentPath("StatsBar"), "utf8");
expect(source).toContain("--stats-max-width: 80rem");
expect(source).toContain("--stats-gutter-width: 4rem");
expect(source).toContain("calc(var(--stats-max-width) - var(--stats-gutter-width))");
});
test("mega menu bridges the gap between its trigger and panel", async () => { test("mega menu bridges the gap between its trigger and panel", async () => {
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8"); const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
/* /*
@@ -3078,6 +3222,39 @@ test("mega menu bridges the gap between its trigger and panel", async () => {
expect(source).toMatch(/\.wrn-mega__panel::before \{[^}]*bottom: 100%/s); expect(source).toMatch(/\.wrn-mega__panel::before \{[^}]*bottom: 100%/s);
}); });
test("mega menu keeps hover activation and click activation from cancelling each other", () => {
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
expect(source).toContain("openOnHover: boolean = true");
expect(source).toContain("@click='activatePanel(event)'");
expect(source).toContain("@mouseenter='hoverPanel(event)'");
expect(source).toContain("showPanel(sourceEvent)");
expect(source).not.toContain("function scheduleOpen");
expect(source).toContain('.wrn-mega__panel[data-show="false"] { display: none; }');
expect(source).toContain('style=\'{visible ? "" : "display:none"}\'');
expect(source).not.toContain("@focus='showPanel(event)'");
expect(source).toContain("max-height: calc(100dvh - 5.5rem)");
});
test("mega menu gives the pointer time to cross from its trigger into a floating panel", () => {
const source = readFileSync(uiComponentPath("MegaMenu"), "utf8");
expect(source).toContain("state closeTimer = null");
expect(source).toContain("@mouseleave='scheduleHide()'");
expect(source).toContain("@mouseenter='cancelScheduledHide()'");
expect(source).toContain("}, 220)");
expect(source).not.toContain("@mouseleave='hidePanel(\"pointer-leave\")'");
});
test("navbar pins full-width mega panels to desktop viewport gutters", () => {
const source = readFileSync(uiComponentPath("Navbar"), "utf8");
expect(source).toMatch(
/\.wrn-navbar__mega\[data-full-width="true"\] \.wrn-mega__panel \{[^}]*position: fixed/s,
);
expect(source).toMatch(/right: 1rem;\s+left: 1rem;/s);
expect(source).toMatch(
/data-collapse-at="1100px"[^}]+\.wrn-navbar__mega\[data-full-width="true"\][^}]+position: static/s,
);
});
test("stepper shows only the active step content and offers back, next and skip", async () => { test("stepper shows only the active step content and offers back, next and skip", async () => {
const source = readFileSync(uiComponentPath("Stepper"), "utf8"); const source = readFileSync(uiComponentPath("Stepper"), "utf8");
const html = await renderComponent(source, { const html = await renderComponent(source, {
@@ -3213,6 +3390,13 @@ test("custom scrollbar styles the scrollbar and drops its fake output", async ()
expect(style).toContain("18rem"); expect(style).toContain("18rem");
}); });
test("page header collapses compiled empty slot markers", () => {
const source = readFileSync(uiComponentPath("PageHeader"), "utf8");
expect(source).toContain(".wrn-page-header__meta:not(:has(> :not(template)))");
expect(source).toContain(".wrn-page-header__body:not(:has(> :not(template)))");
expect(source).toContain(".wrn-page-header__actions--slot-only:not(:has(> :not(template)))");
});
test("layout components ship their own styles instead of Tailwind utilities", () => { test("layout components ship their own styles instead of Tailwind utilities", () => {
/* /*
* These were built from utility classes and class: conditionals. That works * These were built from utility classes and class: conditionals. That works
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/uploader", "name": "@wrnexus/uploader",
"version": "0.8.10", "version": "0.8.13",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./src/index.ts", "main": "./src/index.ts",
@@ -0,0 +1,55 @@
import { describe, expect, test } from "bun:test";
import {
UploadPolicyError,
createSignedFileToken,
enforceUploadPolicy,
inspectUpload,
sniffContentType,
verifySignedFileToken,
} from "../src/security.ts";
describe("upload policy hardening", () => {
test("enforces size, type, filename, and checksum independently", async () => {
const inspection = await inspectUpload("report.pdf", new Uint8Array([0x25, 0x50, 0x44, 0x46]));
const cases: Array<[Parameters<typeof enforceUploadPolicy>[1], string | undefined, string]> = [
[{ maxBytes: 3 }, undefined, "UPLOAD_TOO_LARGE"],
[{ accept: ["image/png"] }, undefined, "UPLOAD_TYPE_REJECTED"],
[{ filenamePattern: /^invoice-/ }, undefined, "UPLOAD_FILENAME_REJECTED"],
[{ requireChecksum: true }, undefined, "UPLOAD_CHECKSUM_REQUIRED"],
[{}, "0".repeat(64), "UPLOAD_CHECKSUM_MISMATCH"],
];
for (const [policy, checksum, code] of cases) {
try {
enforceUploadPolicy(inspection, policy, checksum);
throw new Error("policy unexpectedly accepted upload");
} catch (error) {
expect(error).toBeInstanceOf(UploadPolicyError);
expect((error as UploadPolicyError).code).toBe(code);
}
}
enforceUploadPolicy(
inspection,
{ maxBytes: 4, accept: ["application/pdf"] },
inspection.sha256,
);
});
test("recognizes bounded signatures and rejects malformed signed-token inputs", async () => {
expect(sniffContentType(new Uint8Array([0xff, 0xd8, 0xff]))).toBe("image/jpeg");
expect(sniffContentType(new Uint8Array([0x50, 0x4b, 0x03, 0x04]))).toBe("application/zip");
expect(sniffContentType(new Uint8Array([1, 2, 3]))).toBeNull();
await expect(
createSignedFileToken({ store: "", key: "x", expiresAt: 1 }, "long-enough-secret"),
).rejects.toThrow("store");
expect(await verifySignedFileToken("not-a-token", "long-enough-secret")).toBeNull();
});
test("signed file tokens reject expiry, tampering, and the wrong signing key", async () => {
const value = { store: "uploads", key: "safe/report.pdf", expiresAt: 2_000 };
const token = await createSignedFileToken(value, "long-enough-secret");
expect(await verifySignedFileToken(token, "long-enough-secret", 1_999)).toEqual(value);
expect(await verifySignedFileToken(token, "long-enough-secret", 2_000)).toBeNull();
expect(await verifySignedFileToken(`${token}x`, "long-enough-secret", 1_999)).toBeNull();
expect(await verifySignedFileToken(token, "different-long-secret", 1_999)).toBeNull();
});
});
+9 -1
View File
@@ -19,7 +19,15 @@ function sha256(value) {
} }
function loadTypeScript() { function loadTypeScript() {
const candidates = [process.env.TYPESCRIPT_PATH, "typescript"].filter(Boolean); // TypeScript 7's root package is the native CLI and no longer exposes the
// legacy compiler API used by the embedded editor compiler. Keep that API
// isolated behind @wrnexus/typecheck's explicit TypeScript 6 dependency
// while the workspace itself type-checks with TypeScript 7.
const candidates = [
process.env.TYPESCRIPT_PATH,
join(root, "packages", "typecheck", "node_modules", "typescript"),
"typescript",
].filter(Boolean);
for (const candidate of candidates) { for (const candidate of candidates) {
try { try {
return require(candidate); return require(candidate);
+6 -1
View File
@@ -2,10 +2,15 @@ import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
import console from "node:console"; import console from "node:console";
import { dirname, join, relative, resolve } from "node:path"; import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
import process from "node:process"; import process from "node:process";
import ts from "typescript";
const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const require = createRequire(import.meta.url);
// TypeScript 7's root package is the native CLI and no longer exposes the
// legacy compiler API. Public API extraction intentionally uses the compiler
// version embedded by @wrnexus/typecheck.
const ts = require(join(root, "packages", "typecheck", "node_modules", "typescript"));
const baselinePath = join(root, "docs", "public-api-0.8.json"); const baselinePath = join(root, "docs", "public-api-0.8.json");
function flattenTargets(value) { function flattenTargets(value) {

Some files were not shown because too many files have changed in this diff Show More