From 95206513302194bfeacf725a2b513ade0196bbd8 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Mon, 19 Oct 2026 17:40:50 +0530 Subject: [PATCH] fix(studio): render complete interactive gallery previews --- bun.lock | 30 +- .../2026-10-19-studio-interactive-gallery.md | 55 + docs/ui-visual-contract-0.8.json | 4 +- packages/compiler/package.json | 2 +- packages/compiler/src/client-codegen.ts | 26 +- .../compiler/test/function-defaults.test.ts | 36 + packages/design-studio/README.md | 6 + packages/design-studio/package.json | 5 +- packages/design-studio/src/client.ts | 26 +- .../design-studio/src/gallery-samples.json | 5081 +++++++++++++++++ packages/design-studio/src/gallery.ts | 160 + packages/design-studio/src/preview-assets.ts | 57 + .../design-studio/src/preview-composition.ts | 87 + packages/design-studio/src/preview.ts | 63 +- packages/design-studio/src/shell.ts | 2 +- packages/design-studio/test/gallery.test.ts | 65 + packages/design-studio/test/server.test.ts | 2 +- packages/design-system/package.json | 2 +- packages/design-system/src/presets.ts | 2 +- packages/design-system/test/recipes.test.ts | 11 + packages/ui/components/DeviceFrame.wrn | 2 +- packages/ui/components/switch.wrn | 3 +- packages/ui/package.json | 2 +- packages/ui/src/chart-config.ts | 17 + packages/ui/src/chart-runtime.ts | 38 +- packages/ui/test/chart.test.ts | 16 +- 26 files changed, 5761 insertions(+), 39 deletions(-) create mode 100644 docs/releases/2026-10-19-studio-interactive-gallery.md create mode 100644 packages/design-studio/src/gallery-samples.json create mode 100644 packages/design-studio/src/gallery.ts create mode 100644 packages/design-studio/src/preview-assets.ts create mode 100644 packages/design-studio/src/preview-composition.ts create mode 100644 packages/design-studio/test/gallery.test.ts diff --git a/bun.lock b/bun.lock index 83b3d1df..affe5a9f 100644 --- a/bun.lock +++ b/bun.lock @@ -354,7 +354,7 @@ }, "packages/compiler": { "name": "@wrnexus/compiler", - "version": "0.8.44", + "version": "0.8.45", "dependencies": { "@wrnexus/csr": "workspace:*", "@wrnexus/store": "workspace:*", @@ -391,8 +391,12 @@ }, "packages/design-studio": { "name": "@wrnexus/design-studio", - "version": "0.1.3", + "version": "0.1.4", + "bin": { + "wrnexus-design-studio": "src/cli.ts", + }, "dependencies": { + "@iconify-json/lucide": "1.2.123", "@wrnexus/ai": "workspace:*", "@wrnexus/compiler": "workspace:*", "@wrnexus/core": "workspace:*", @@ -405,7 +409,7 @@ }, "packages/design-system": { "name": "@wrnexus/design-system", - "version": "0.1.0", + "version": "0.1.1", }, "packages/dev-server": { "name": "@wrnexus/dev-server", @@ -778,7 +782,7 @@ }, "packages/ui": { "name": "@wrnexus/ui", - "version": "0.8.118", + "version": "0.8.120", "dependencies": { "@wrnexus/core": "workspace:*", "@wrnexus/design-system": "workspace:*", @@ -999,7 +1003,7 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + "@iconify-json/lucide": ["@iconify-json/lucide@1.2.123", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-0CozmpKXEOEEhltrfT2zt+/t1hez67Y+hM2VJWoIcBKdBrb83VYuKf+b5A0QU9HfQm+RNn2GjFWlTOwi+Ss6IA=="], "@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=="], @@ -2153,10 +2157,20 @@ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="], + "admin/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + + "auth-showcase/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + + "basic-app/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + + "captcha-showcase/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + "cheerio/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + "component-showcase/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + "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=="], "data-urls/whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], @@ -2173,6 +2187,8 @@ "htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "i18n-showcase/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + "js-beautify/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -2197,6 +2213,10 @@ "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "web/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + + "wrnexus-crm-example/@iconify-json/lucide": ["@iconify-json/lucide@1.2.125", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-tOCk1QKMtKnCfPAgZRHgjRkQTP7wF5IO+iPKvvp8vxGZYPkSLhx4HTV3Ng0pIZ3wNWrS6kVpHkunJ1dc19L1og=="], + "@types/jsdom/parse5/entities": ["entities@8.1.0", "", {}, "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA=="], "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], diff --git a/docs/releases/2026-10-19-studio-interactive-gallery.md b/docs/releases/2026-10-19-studio-interactive-gallery.md new file mode 100644 index 00000000..a3484b0e --- /dev/null +++ b/docs/releases/2026-10-19-studio-interactive-gallery.md @@ -0,0 +1,55 @@ +# Studio interactive gallery release + +Packages: Studio 0.1.4, design-system 0.1.1, compiler 0.8.45, UI 0.8.120. + +## Result + +All 104 gallery entries now have explicit sample props and content. Collection +components receive rows, options, messages, tabs, tree nodes or metrics rather +than empty defaults. Layout components receive slot content, including the +conditional templates instantiated after hydration. Composite components render +their nested shipped components instead of leaving empty mount placeholders. + +Previews include generated browser functions for the shipped components. Local +Chart.js and Drawflow bundles render their real providers. Icon masks use the +shipped Lucide collection, including its aliases; no CDN or icon font is needed. +The map uses a labeled local schematic and supports marker selection and zoom. +DeviceFrame shows local content and supports rotation. Toaster has a sample +notification trigger; Confetti has room to animate without nested scrollbars. + +Large examples span the available gallery width. Sample output events are +visible, and preview links do not navigate the frame to unrelated routes. Forms +and uploads remain demonstrations: they do not submit user data. The editor +reports rendering, readiness, and failures while preserving the draft. Readiness +messages must come from the current preview window and match its document path. + +Switch tracks retain pill geometry across presets and older saved recipes. The +component's semantic shape takes precedence over the ordinary control radius. +DeviceFrame now uses the existing motion-base token rather than an undefined +motion-normal token. The visual-contract update contains only those two files. + +A generated compiler commit variable shadowed FlowCanvas's `pending` state and +threw during interaction. Commit bookkeeping now uses short names allocated +against authored bindings. Tests cover pending, values, committed and the helper +prefixes, while the generated-module size ratchet continues to pass. + +The user authorized including the concurrently edited chart-label improvements. +Painted category ticks shorten at compact widths; complete labels remain in +wrapped tooltips. Existing caller-provided callbacks remain authoritative. + +## Verification + +- Combined compiler, UI, design-system and Studio run: 548 passing tests. +- TypeScript, public API baseline and UI visual-contract checks pass. +- Gallery tests verify 104 sample cards, local assets, generated functions, + populated slots and absence of unresolved nested component mounts. +- Browser review covers all seven pages, with 16 samples on the first six and + eight on the last. Actual checks include accordion expansion, Switch toggling + and its computed 999px radius, modal content, chart canvas creation, table rows, + graph nodes and selection, Confetti start, data-map selection, map zoom/selection + and a visible notification. No external map/embed access is required. +- Existing draft schema, validation, generation locks and APIs remain unchanged. + +Source sample data is adapted from the existing component showcase profiles and +reviewed as fixed Studio fixtures. The package has no runtime dependency on the +component-showcase checkout. New contracts must receive a fixture before shipping. diff --git a/docs/ui-visual-contract-0.8.json b/docs/ui-visual-contract-0.8.json index 0a62fa85..72f016e1 100644 --- a/docs/ui-visual-contract-0.8.json +++ b/docs/ui-visual-contract-0.8.json @@ -29,7 +29,7 @@ "packages/ui/components/DataMap.wrn": "66752f98718c748f25e0eed76d39f504fc20b26d3b3ae841db85f5d5f02df6cb", "packages/ui/components/DataTable.wrn": "c25aa8849d140562970cd2ffaa33b720cc903e809ef7fb20a564425e8295d5b9", "packages/ui/components/DatePicker.wrn": "107be1aa2ae2b329b50c9063f2f25bf4ac7dccebe6a2b6ca412cc12afac8f3af", - "packages/ui/components/DeviceFrame.wrn": "20485a867f404098afec5814174e3f83ca7110e193c787027c8b755bb1f5909e", + "packages/ui/components/DeviceFrame.wrn": "ae6a19ee27ba24d5673e3b4fa951bfcf8f12f6ef40bad0e61f349666e13afa64", "packages/ui/components/Divider.wrn": "3d1065aa2dccb11f31eff35105ce7a3a0321317850e4df02b6c298e4a492cadc", "packages/ui/components/DragAndDrop.wrn": "5af6c01a9b58eca44868205708f47738c1c2b25589b94035cb2f4f31eec06ba6", "packages/ui/components/Drawer.wrn": "54f4a07464cc7bcd3f52453207c568d81044fca45850ca2ec1d8b6e6442b24d5", @@ -103,7 +103,7 @@ "packages/ui/components/select.wrn": "9bb6c0a6238c76ef1b96ecc30eee1e7208abc6b1d195d9f0880244aada465791", "packages/ui/components/skeleton.wrn": "8bf41be508416c9cb3dc05103bde9ae944499da12ea244fe853749daaed07f3f", "packages/ui/components/spinner.wrn": "0144152d936a861ffcd3c37f226ede28d31c0dc945f3c3cf85474db5b4335cf9", - "packages/ui/components/switch.wrn": "00a40becd26bc7ddc63954f1c8856b3c9b0a4ad13b78c8db5c86910bc9e260d0", + "packages/ui/components/switch.wrn": "e769639a573798ceb658bf0e901cd4d7612c28f79de09655c6b7c9f1a95c1cf1", "packages/ui/components/textarea.wrn": "e1402419f3f6bd91b2119a8a49704683cabfba3329a9806c4a78242fd0ff0036", "packages/ui/components/tooltip.wrn": "2bd977b0b8e6fccfeac4ea96b71d01065a237779243d3b8236db34448ea3b381", "packages/ui/styles/SelectStyles.wrn": "b84470cf11d6830963adc58c595776d843ed8b126c0fe242dd8e961aace991fa", diff --git a/packages/compiler/package.json b/packages/compiler/package.json index 31d36200..5aebc501 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.8.44", + "version": "0.8.45", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/compiler/src/client-codegen.ts b/packages/compiler/src/client-codegen.ts index e26f6649..15793773 100644 --- a/packages/compiler/src/client-codegen.ts +++ b/packages/compiler/src/client-codegen.ts @@ -520,20 +520,30 @@ export function generateBrowserModule(ast: PageAst): string { [...sharedState, ...sharedProps].filter((name) => identifierReferenced(source, name)), ); } - const sharedCommit = `const values = { ${sharedState.map((name) => `${JSON.stringify(name)}: ${name}`).join(", ")} }; - const pending = {}; - for (const name of Object.keys(values)) { - if (!Object.is(values[name], committed[name])) { pending[name] = values[name]; committed[name] = values[name]; } + const usedCommitNames = new Set([...sharedState, ...sharedProps, ...functionNames]); + const commitName = (base: string) => { + let name = base; + while (usedCommitNames.has(name)) name += "_"; + usedCommitNames.add(name); + return name; + }; + const commitValues = commitName("__v"), + commitPending = commitName("__p"), + commitSnapshot = commitName("__c"); + const sharedCommit = `const ${commitValues} = { ${sharedState.map((name) => `${JSON.stringify(name)}: ${name}`).join(", ")} }; + const ${commitPending} = {}; + for (const name of Object.keys(${commitValues})) { + if (!Object.is(${commitValues}[name], ${commitSnapshot}[name])) { ${commitPending}[name] = ${commitValues}[name]; ${commitSnapshot}[name] = ${commitValues}[name]; } } - if (Object.keys(pending).length === 0) return; - const apply = () => { for (const name of Object.keys(pending)) context.state[name] = pending[name]; }; + if (Object.keys(${commitPending}).length === 0) return; + const apply = () => { for (const name of Object.keys(${commitPending})) context.state[name] = ${commitPending}[name]; }; if (typeof context.batch === "function") context.batch(apply); else apply();`; const restores = [...sharedState, ...sharedProps] .map( (name) => `${JSON.stringify(name)}:()=>{${ sharedState.includes(name) - ? `${name} = context.state.${name}; committed.${name} = ${name};` + ? `${name} = context.state.${name}; ${commitSnapshot}.${name} = ${name};` : `${name} = context.props.${name};` }}`, ) @@ -588,7 +598,7 @@ function __wrnexusCreateClientFunctions(context) { const props = context.props; const refs = context.refs; ${apiBindings(ast)} - const committed = { ${sharedState.map((name) => `${JSON.stringify(name)}: ${name}`).join(", ")} }; + const ${commitSnapshot} = { ${sharedState.map((name) => `${JSON.stringify(name)}: ${name}`).join(", ")} }; const __wrnexusCommit = () => { ${sharedCommit} }; const __wrnexusRestores = { ${restores} }; const __wrnexusRestoreKeys = ${JSON.stringify(restoreKeys)}; diff --git a/packages/compiler/test/function-defaults.test.ts b/packages/compiler/test/function-defaults.test.ts index f543debb..6834f6ae 100644 --- a/packages/compiler/test/function-defaults.test.ts +++ b/packages/compiler/test/function-defaults.test.ts @@ -11,6 +11,42 @@ const source = `component FunctionDefaults { } view { } }`; + +test("browser state named pending or values does not collide with commit bookkeeping", () => { + const source = `component CommitNames { + state pending = "ready" + state values = 1 + state committed = false + state __v = 3 + state __p = false + state __c = "safe" + functions { + shared function status() { return pending + values + __c + __v } + client function advance() { pending = "done"; values += 1; committed = true } + } + view { } + }`; + const module = generateBrowserModule(parse(source)); + const functions = new Function( + module.replace(/\bexport\s+(?=const|function)/g, "") + ";return __wrnexusClientFunctions;", + )(); + const context = { + state: { pending: "ready", values: 1, committed: false, __v: 3, __p: false, __c: "safe" }, + props: {}, + batch: (apply: () => void) => apply(), + }; + expect(functions.status(context)).toBe("ready1safe3"); + functions.advance(context); + expect(context.state).toEqual({ + pending: "done", + values: 2, + committed: true, + __v: 3, + __p: false, + __c: "safe", + }); + expect(functions.status(context)).toBe("done2safe3"); +}); test("browser functions retain defaults, prior-argument references and prop references", () => { const module = generateBrowserModule(parse(source)); const functions = new Function( diff --git a/packages/design-studio/README.md b/packages/design-studio/README.md index 5717dff2..71cf7754 100644 --- a/packages/design-studio/README.md +++ b/packages/design-studio/README.md @@ -75,6 +75,12 @@ Run `bun run scripts/evaluate-design-agents.ts` for the six-page/twelve-preview ## Installed packages and upgrades +The component gallery uses 104 populated, interactive samples. Icons, browser +functions, chart and graph runtimes are included locally. Large examples get +appropriate space; output events appear below the sample. Map and device samples +use local content. Forms/uploads do not submit data, and preview links stay in +the preview. Loading and failure states preserve the current draft. + The preset and comparison menus offer 13 starting systems: Square, Soft, Angular, Swiss, Forest, Ocean, Rose, Violet, Ember, Editorial, Terminal, Brutalist and Contrast. Each has light and dark modes and uses the existing component contracts. diff --git a/packages/design-studio/package.json b/packages/design-studio/package.json index 9f0b1b36..4f61a710 100644 --- a/packages/design-studio/package.json +++ b/packages/design-studio/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/design-studio", - "version": "0.1.3", + "version": "0.1.4", "private": true, "type": "module", "main": "src/index.ts", @@ -15,11 +15,12 @@ "./upgrades": "./src/upgrades.ts" }, "dependencies": { + "@iconify-json/lucide": "1.2.123", "@wrnexus/ai": "workspace:*", - "@wrnexus/design-system": "workspace:*", "@wrnexus/compiler": "workspace:*", "@wrnexus/core": "workspace:*", "@wrnexus/csr": "workspace:*", + "@wrnexus/design-system": "workspace:*", "@wrnexus/ssr": "workspace:*", "@wrnexus/styles": "workspace:*", "@wrnexus/ui": "workspace:*" diff --git a/packages/design-studio/src/client.ts b/packages/design-studio/src/client.ts index a4e5dc37..c04e9ee3 100644 --- a/packages/design-studio/src/client.ts +++ b/packages/design-studio/src/client.ts @@ -26,6 +26,7 @@ let application: { enabled?: boolean; revision: string; previous?: string; direc }; let page = 0; let previewGeneration = 0; +let previewLoadTimer: ReturnType; let reviewed: { system: DesignSystem; name: string } | undefined; let activeTab = "foundations"; let noticeTimer: ReturnType; @@ -246,6 +247,8 @@ function render() { } async function previews() { const generation = ++previewGeneration; + clearTimeout(previewLoadTimer); + $("preview-status").textContent = "Rendering preview…"; const settings = { ...selection(), direction: value("preview-direction"), @@ -281,9 +284,19 @@ async function previews() { : {}), }), ), - ); + ).catch((error) => { + if (generation === previewGeneration) + $("preview-status").textContent = + "Could not update the preview. Your draft is preserved. " + error.message; + throw error; + }); if (generation !== previewGeneration) return; $("preview-frame").src = result[0].url; + previewLoadTimer = setTimeout(() => { + if (generation === previewGeneration) + $("preview-status").textContent = + "The preview did not finish loading. Use Refresh to try again; your draft is preserved."; + }, 15_000); if (comparison) $("compare-frame").src = result[1].url; for (const id of ["preview-frame", "compare-frame"]) $(id).style.width = value("preview-width"); $("preview-label").textContent = reviewed @@ -293,6 +306,17 @@ async function previews() { : "Current draft"; $("show-draft").hidden = !reviewed; } +window.addEventListener("message", (event) => { + const frame = $("preview-frame"); + if ( + event.source === frame.contentWindow && + event.data?.type === "wrn-studio-preview-ready" && + event.data.path === new URL(frame.src).pathname + ) { + clearTimeout(previewLoadTimer); + $("preview-status").textContent = "Preview ready"; + } +}); async function changed() { reviewed = undefined; render(); diff --git a/packages/design-studio/src/gallery-samples.json b/packages/design-studio/src/gallery-samples.json new file mode 100644 index 00000000..594ad2b2 --- /dev/null +++ b/packages/design-studio/src/gallery-samples.json @@ -0,0 +1,5081 @@ +{ + "Accordion": { + "props": { + "size": "default", + "variant": "default", + "class": "showcase-instance showcase-instance--1", + "items": [ + { + "value": "workspace", + "label": "How do I create a workspace?", + "content": "Choose Create workspace, add a name, and invite your team. You can update the details any time in Settings." + }, + { + "value": "billing", + "label": "Can I change my plan later?", + "content": "Yes. Open Billing to compare plans and choose the one that suits your team." + }, + { + "value": "security", + "label": "How is my account protected?", + "content": "Enable two-step verification and add a recovery method to keep your account secure." + } + ], + "defaultOpen": [ + "workspace" + ], + "multiple": false, + "alwaysOpen": false, + "disabled": false, + "indicator": "chevron", + "indicatorPosition": "end", + "showIndicator": true, + "bordered": false, + "separated": false, + "flush": false, + "contentItalic": false + }, + "content": "" + }, + "AdvancedSelect": { + "props": { + "size": "default", + "label": "Default advanced select", + "name": "advanced-select-1", + "value": "design", + "values": [], + "options": [ + { + "value": "design", + "label": "Design", + "description": "Product and visual design", + "icon": "icon-[lucide--palette]", + "color": "#8b5cf6", + "avatar": "/showcase-images/avatar-design.svg" + }, + { + "value": "engineering", + "label": "Engineering", + "description": "Platform and application engineering", + "icon": "icon-[lucide--code-2]", + "color": "#0ea5e9", + "avatar": "/showcase-images/avatar-engineering.svg" + }, + { + "value": "growth", + "label": "Growth", + "description": "Marketing and customer growth", + "icon": "icon-[lucide--trending-up]", + "color": "#10b981", + "avatar": "/showcase-images/avatar-growth.svg" + }, + { + "value": "support", + "label": "Support", + "description": "Customer operations", + "disabled": false + } + ], + "groups": [], + "placeholder": "Choose a team", + "placeholderIcon": "", + "multiple": false, + "searchable": true, + "allowEmpty": false, + "tags": false, + "disabled": false, + "required": false, + "invalid": false, + "validationMessage": "", + "searchMode": "contains", + "minSearchLength": 0, + "searchResultLimit": 0, + "maxSelections": 0, + "showCounter": false, + "optionTemplate": "default", + "fixed": false, + "remote": false, + "remoteUrl": "", + "infinite": false, + "hasMore": false + }, + "content": "" + }, + "Alert": { + "props": { + "size": "default", + "color": "success", + "variant": "soft", + "class": "", + "radius": "md", + "shadow": "none", + "title": "Changes saved", + "description": "Your workspace settings are up to date.", + "items": [], + "actions": [], + "showIcon": true, + "icon": "icon-[lucide--circle-check]", + "dismissible": true, + "dismissLabel": "Dismiss alert", + "role": "alert", + "live": "polite", + "linkLabel": "", + "linkHref": "", + "actionLabel": "", + "actionHref": "", + "compact": false + }, + "content": "" + }, + "AnnouncementBar": { + "props": { + "badge": "Public notice", + "badgeIcon": "icon-[lucide--sparkles]", + "message": "All composite components are now available", + "description": "Review each component and its recommended use cases.", + "icon": "icon-[lucide--megaphone]", + "actionLabel": "View components", + "actionHref": "#components", + "actionIcon": "icon-[lucide--arrow-down]", + "dismissible": true, + "dismissLabel": "Announcement Bar", + "sticky": false, + "compact": false, + "flush": false, + "mobileCompact": false, + "mobileMessageLines": 2, + "hideBadgeOnMobile": false, + "hideDescriptionOnMobile": false, + "flushMobile": false, + "size": "default", + "width": "default", + "variant": "soft", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "AuthForm": { + "props": { + "size": "default", + "action": "#auth-form-demo", + "title": "Auth Form example", + "description": "A polished default auth form for a modern application.", + "showRemember": true, + "showName": true, + "submitLabel": "Auth Form", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "AuthSplitLayout": { + "props": { + "size": "default", + "title": "Auth Split Layout example", + "description": "A polished default auth split layout for a modern application.", + "features": [ + { + "label": "Team members", + "values": [ + "5", + "Unlimited", + "Unlimited" + ] + }, + { + "label": "Support", + "values": [ + "Email", + "Priority", + "Dedicated" + ] + }, + { + "label": "Audit history", + "values": [ + "7 days", + "90 days", + "Custom" + ] + } + ], + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Avatar": { + "props": { + "src": "", + "alt": "Aisha Khan", + "initials": "AK", + "size": "md", + "color": "primary", + "variant": "solid", + "shape": "circle", + "status": "", + "statusLabel": "", + "statusPosition": "bottom", + "badge": "", + "badgeIcon": "", + "badgeLabel": "", + "tooltip": "Aisha Khan · Product designer", + "name": "Aisha Khan", + "description": "", + "loading": "lazy", + "class": "" + }, + "content": "" + }, + "AvatarGroup": { + "props": { + "items": [ + { + "name": "Aisha Khan", + "initials": "AK", + "tooltip": "Aisha Khan · Product designer" + }, + { + "name": "Daniel Lee", + "initials": "DL", + "tooltip": "Daniel Lee · Engineer" + }, + { + "name": "Maya Patel", + "initials": "MP", + "tooltip": "Maya Patel · Product manager" + }, + { + "name": "Oliver Reed", + "initials": "OR", + "tooltip": "Oliver Reed · Support" + }, + { + "name": "Sofia Chen", + "initials": "SC", + "tooltip": "Sofia Chen · Operations" + } + ], + "size": "md", + "color": "primary", + "variant": "solid", + "shape": "circle", + "layout": "stack", + "maxVisible": 3, + "columns": 3, + "borderColor": "", + "showTooltips": true, + "overflowLabel": "Show remaining members", + "class": "" + }, + "content": "" + }, + "BackToTop": { + "props": { + "threshold": 500, + "label": "Back to top", + "ariaLabel": "Scroll back to top", + "icon": "icon-[lucide--arrow-up]", + "position": "right", + "offset": "md", + "behavior": "smooth", + "showProgress": false, + "showLabel": false, + "alwaysVisible": true, + "size": "default", + "color": "primary", + "variant": "solid", + "shape": "round", + "class": "" + }, + "content": "" + }, + "Badge": { + "props": { + "label": "Active", + "humanize": true, + "size": "sm", + "color": "success", + "variant": "soft", + "class": "showcase-instance showcase-instance--1", + "icon": "", + "dot": false, + "dotOnly": false, + "dotLabel": "Status", + "animated": false, + "dismissible": false, + "dismissLabel": "Remove badge", + "truncate": false, + "anchorLabel": "", + "anchorIcon": "" + }, + "content": "" + }, + "Blockquote": { + "props": { + "citationTitle": "Blockquote example", + "size": "md", + "align": "start", + "variant": "default", + "quoteMark": true, + "italic": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Breadcrumb": { + "props": { + "label": "Breadcrumb", + "items": [ + { + "label": "Home", + "href": "/", + "icon": "icon-[lucide--house]" + }, + { + "label": "Components", + "href": "/base" + }, + { + "label": "Page header", + "current": true + } + ], + "showHome": true, + "homeLabel": "Breadcrumb", + "homeHref": "#breadcrumb-demo", + "size": "small", + "variant": "minimal", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Button": { + "props": { + "label": "Save & Submit", + "as": "button", + "type": "submit", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "ButtonGroup": { + "props": { + "items": [ + { + "label": "Day", + "value": "day" + }, + { + "label": "Week", + "value": "week" + }, + { + "label": "Month", + "value": "month" + } + ], + "value": "week", + "size": "md", + "color": "primary", + "variant": "default", + "orientation": "horizontal", + "responsive": false, + "attached": true, + "selectable": true, + "toolbar": false, + "disabled": false, + "ariaLabel": "Button group", + "class": "" + }, + "content": "" + }, + "CTASection": { + "props": { + "eyebrow": "Ready to continue", + "title": "Build public pages with reusable components", + "description": "Keep application pages focused on content and business data while the shared UI package owns presentation.", + "icon": "icon-[lucide--rocket]", + "align": "split", + "size": "default", + "variant": "gradient", + "primaryLabel": "Use these components", + "primaryHref": "#components", + "primaryIcon": "icon-[lucide--blocks]", + "secondaryLabel": "Back to home", + "secondaryHref": "/", + "secondaryIcon": "icon-[lucide--house]", + "visualTitle": "CTASection example", + "visualDescription": "A polished default ctasection for a modern application.", + "visualItems": [ + { + "id": "ctasection-0-1", + "key": "ctasection-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#ctasection-demo", + "actionHref": "#ctasection-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "ctasection-0-2", + "key": "ctasection-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#ctasection-demo", + "actionHref": "#ctasection-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "maxWidth": "full", + "fullBleed": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "
\n
\n \n Composable visual panel\n

Add a screenshot, product preview, media block, chart, form, or illustration.

\n
" + }, + "Card": { + "props": { + "title": "Citizen service", + "icon": "", + "titleKey": "", + "subtitle": "", + "subtitleKey": "", + "description": "Access a trusted online service.", + "descriptionKey": "", + "showHeader": true, + "showFooter": true, + "header": "", + "footer": "", + "imageSrc": "", + "imageAlt": "", + "imagePosition": "top", + "actionLabel": "", + "actionLabelKey": "", + "actionHref": "", + "headerActions": [], + "navigation": [], + "activeNav": "", + "mobileNavigation": false, + "alertTitle": "", + "alertDescription": "", + "empty": false, + "emptyTitle": "No data to show", + "emptyIcon": "icon-[lucide--inbox]", + "items": [], + "size": "md", + "color": "primary", + "variant": "default", + "layout": "vertical", + "align": "left", + "hover": "none", + "radius": "default", + "shadow": "default", + "equalHeight": false, + "scrollable": false, + "maxHeight": "18rem", + "dismissible": false, + "ariaLabel": "", + "class": "" + }, + "content": "

Supporting content belongs inside the default slot.

" + }, + "Carousel": { + "props": { + "size": "default", + "color": "primary", + "title": "", + "description": "", + "items": [ + { + "title": "A space to create", + "description": "Bring your next idea to life.", + "imageSrc": "/showcase-images/green-hero.svg" + }, + { + "title": "Made for teamwork", + "description": "Keep everyone moving together.", + "imageSrc": "/showcase-images/blue-mountain.svg" + }, + { + "title": "Room to grow", + "description": "Build something that matters.", + "imageSrc": "/showcase-images/violet-circle.svg" + } + ], + "activeIndex": 0, + "slidesPerView": 1, + "gap": "0.75rem", + "showPagination": true, + "isAutoPlay": false, + "autoplayInterval": 4000, + "isInfiniteLoop": false, + "isRTL": false, + "isCentered": false, + "isDraggable": false, + "isAutoHeight": false, + "isSnap": false, + "showCounter": true, + "thumbnails": "none", + "ariaLabel": "Product highlights", + "framed": false, + "variant": "default", + "class": "" + }, + "content": "" + }, + "Chart": { + "props": { + "title": "Sample weekly changes", + "description": "Sample data", + "items": [], + "data": [ + { + "label": "Mon", + "value": 24 + }, + { + "label": "Tue", + "value": -12 + }, + { + "label": "Wed", + "value": 29 + }, + { + "label": "Thu", + "value": 52 + }, + { + "label": "Fri", + "value": 45 + } + ], + "labels": [], + "datasets": [], + "type": "bar", + "valueKey": "value", + "labelKey": "label", + "height": 220, + "showLegend": true, + "showValues": false, + "size": "default", + "color": "primary", + "variant": "bar", + "class": "" + }, + "content": "" + }, + "ChatBubble": { + "props": { + "size": "default", + "color": "primary", + "title": "", + "description": "", + "items": [ + { + "text": "Hi Maya, is the new dashboard ready to review?", + "direction": "incoming", + "timestamp": "10:24" + }, + { + "text": "Yes! I have shared the latest version with you.", + "direction": "outgoing", + "timestamp": "10:25", + "receipt": "read" + }, + { + "text": "Thanks, I will take a look this afternoon.", + "direction": "incoming", + "timestamp": "10:26" + }, + { + "text": "Great. The release notes are attached too.", + "direction": "outgoing", + "timestamp": "10:27", + "receipt": "delivered" + }, + { + "text": "One more screenshot coming up.", + "direction": "outgoing", + "timestamp": "10:28", + "receipt": "sent" + }, + { + "text": "Uploading the final export now.", + "direction": "outgoing", + "timestamp": "10:29", + "receipt": "sending" + }, + { + "text": "Here is the invoice PDF.", + "direction": "outgoing", + "timestamp": "10:30", + "receipt": "failed", + "status": "Not delivered", + "actionLabel": "Retry" + } + ], + "oneSided": false, + "showAvatars": false, + "showMetadata": true, + "ariaLabel": "Conversation", + "variant": "default", + "class": "" + }, + "content": "" + }, + "Checkbox": { + "props": { + "size": "default", + "label": "Checkbox", + "hiddenLabel": false, + "placeholder": "Enter a sample value", + "variant": "normal", + "icon": "", + "value": "sample-1", + "values": [ + { + "id": "checkbox-0-1", + "key": "checkbox-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#checkbox-demo", + "actionHref": "#checkbox-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "checkbox-0-2", + "key": "checkbox-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#checkbox-demo", + "actionHref": "#checkbox-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "options": [ + { + "id": "checkbox-0-1", + "key": "checkbox-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#checkbox-demo", + "actionHref": "#checkbox-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "checkbox-0-2", + "key": "checkbox-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#checkbox-demo", + "actionHref": "#checkbox-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "checked": false, + "indeterminate": false, + "card": false, + "rightAligned": false, + "list": false, + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Clipboard": { + "props": { + "value": "npm install @wrnexus/ui", + "label": "Copy", + "copiedLabel": "Copied", + "errorLabel": "Copy failed", + "title": "", + "description": "", + "iconOnly": false, + "code": true, + "size": "default", + "color": "primary", + "disabled": false, + "class": "" + }, + "content": "" + }, + "Collapse": { + "props": { + "size": "default", + "items": [ + { + "id": "collapse-0-1", + "key": "collapse-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#collapse-demo", + "actionHref": "#collapse-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "collapse-0-2", + "key": "collapse-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#collapse-demo", + "actionHref": "#collapse-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "multiple": false, + "initialOpenIndexes": [ + { + "id": "collapse-0-1", + "key": "collapse-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#collapse-demo", + "actionHref": "#collapse-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "collapse-0-2", + "key": "collapse-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#collapse-demo", + "actionHref": "#collapse-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "ariaLabel": "Collapse 1 example", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "ColorPicker": { + "props": { + "size": "default", + "color": "primary", + "id": "", + "name": "brand-color", + "label": "Brand color", + "hiddenLabel": false, + "placeholder": "", + "value": "#8b5cf6", + "icon": "", + "iconPosition": "start", + "helperText": "", + "cornerHint": "", + "error": "", + "inline": false, + "variant": "normal", + "readonly": false, + "disabled": false, + "required": false, + "class": "" + }, + "content": "" + }, + "Columns": { + "props": { + "size": "default", + "columns": 2, + "gap": "lg", + "maxWidth": "xl", + "class": "showcase-instance showcase-instance--1" + }, + "content": "
Content columnSupporting column
" + }, + "ComboBox": { + "props": { + "label": "Default autocomplete", + "name": "combobox-1", + "value": "", + "options": [ + { + "value": "design", + "label": "Design", + "description": "Product and visual design", + "icon": "icon-[lucide--palette]", + "color": "#8b5cf6", + "avatar": "/showcase-images/avatar-design.svg" + }, + { + "value": "engineering", + "label": "Engineering", + "description": "Platform and application engineering", + "icon": "icon-[lucide--code-2]", + "color": "#0ea5e9", + "avatar": "/showcase-images/avatar-engineering.svg" + }, + { + "value": "growth", + "label": "Growth", + "description": "Marketing and customer growth", + "icon": "icon-[lucide--trending-up]", + "color": "#10b981", + "avatar": "/showcase-images/avatar-growth.svg" + }, + { + "value": "support", + "label": "Support", + "description": "Customer operations", + "icon": "icon-[lucide--life-buoy]", + "color": "#f59e0b" + } + ], + "groups": [], + "placeholder": "Search teams", + "clearable": true, + "allowCustomValue": false, + "disabled": false, + "required": false, + "invalid": false, + "validationMessage": "", + "searchMode": "contains", + "minSearchLength": 0, + "searchResultLimit": 0, + "optionTemplate": "default", + "fixed": false, + "remote": false, + "remoteUrl": "", + "remoteQueryParam": "q", + "remoteAutoLoad": true, + "infinite": false, + "hasMore": false + }, + "content": "" + }, + "Confetti": { + "props": { + "label": "Celebrate milestone", + "duration": 1400, + "count": 40, + "autoStart": false, + "disabled": false, + "size": "default", + "color": "primary", + "class": "" + }, + "content": "" + }, + "Container": { + "props": { + "size": "default", + "columns": 2, + "maxWidth": "xl", + "centered": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "
Centered container content
" + }, + "ContextMenu": { + "props": { + "items": [ + { + "label": "Open dashboard", + "description": "View the operational workspace", + "value": "dashboard", + "icon": "icon-[lucide--layout-dashboard]" + }, + { + "label": "Share record", + "value": "share", + "icon": "icon-[lucide--share-2]", + "shortcut": "Ctrl S" + }, + { + "type": "divider" + }, + { + "label": "Delete record", + "value": "delete", + "icon": "icon-[lucide--trash-2]", + "danger": true + } + ], + "open": false, + "defaultOpen": false, + "trigger": "contextmenu", + "placement": "pointer", + "align": "start", + "size": "default", + "variant": "raised", + "title": "Record actions", + "description": "Right-click the target surface", + "label": "Context Menu", + "closeOnSelect": true, + "closeOnOutside": true, + "disabled": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "
Right-click for contextual actions
" + }, + "CustomScrollbar": { + "props": { + "size": "default", + "axis": "vertical", + "thickness": 8, + "maxHeight": "14rem", + "radius": "999px", + "class": "showcase-instance showcase-instance--1" + }, + "content": "

Line 1. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 2. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 3. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 4. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 5. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 6. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 7. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 8. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 9. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 10. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 11. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

Line 12. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.

" + }, + "DataMap": { + "props": { + "size": "default", + "color": "primary", + "title": "Activity by team", + "description": "Select a tile to inspect its activity score.", + "items": [ + { + "label": "Design", + "value": 32 + }, + { + "label": "Engineering", + "value": 84 + }, + { + "label": "Support", + "value": 56 + }, + { + "label": "Sales", + "value": 45 + }, + { + "label": "Marketing", + "value": 68 + }, + { + "label": "Operations", + "value": 20 + } + ], + "variant": "default", + "class": "" + }, + "content": "" + }, + "DataTable": { + "props": { + "size": "default", + "columns": "[{\"key\": \"name\", \"label\": \"Account\", \"sortable\": true}, {\"key\": \"plan\", \"label\": \"Plan\", \"sortable\": true}, {\"key\": \"owner\", \"label\": \"Owner\"}, {\"key\": \"seats\", \"label\": \"Seats\", \"align\": \"end\", \"sortable\": true}, {\"key\": \"status\", \"label\": \"Status\", \"align\": \"center\"}]", + "rows": "[{\"id\": 1, \"name\": \"Northwind\", \"plan\": \"Scale\", \"owner\": \"A. Okafor\", \"seats\": 6, \"status\": \"Active\", \"statusHtml\": \"Active\"}, {\"id\": 2, \"name\": \"Acme Industrial\", \"plan\": \"Team\", \"owner\": \"R. Silva\", \"seats\": 13, \"status\": \"Trial\", \"statusHtml\": \"Trial\"}, {\"id\": 3, \"name\": \"Globex\", \"plan\": \"Enterprise\", \"owner\": \"M. Chen\", \"seats\": 20, \"status\": \"Past due\", \"statusHtml\": \"Past due\"}, {\"id\": 4, \"name\": \"Initech\", \"plan\": \"Starter\", \"owner\": \"J. Dubois\", \"seats\": 27, \"status\": \"Active\", \"statusHtml\": \"Active\"}, {\"id\": 5, \"name\": \"Umbrella\", \"plan\": \"Scale\", \"owner\": \"P. Novak\", \"seats\": 34, \"status\": \"Trial\", \"statusHtml\": \"Trial\"}, {\"id\": 6, \"name\": \"Stark Labs\", \"plan\": \"Team\", \"owner\": \"A. Okafor\", \"seats\": 41, \"status\": \"Past due\", \"statusHtml\": \"Past due\"}, {\"id\": 7, \"name\": \"Wayne Foods\", \"plan\": \"Enterprise\", \"owner\": \"R. Silva\", \"seats\": 48, \"status\": \"Active\", \"statusHtml\": \"Active\"}, {\"id\": 8, \"name\": \"Soylent\", \"plan\": \"Starter\", \"owner\": \"M. Chen\", \"seats\": 55, \"status\": \"Trial\", \"statusHtml\": \"Trial\"}, {\"id\": 9, \"name\": \"Hooli\", \"plan\": \"Scale\", \"owner\": \"J. Dubois\", \"seats\": 62, \"status\": \"Past due\", \"statusHtml\": \"Past due\"}, {\"id\": 10, \"name\": \"Vehement\", \"plan\": \"Team\", \"owner\": \"P. Novak\", \"seats\": 69, \"status\": \"Active\", \"statusHtml\": \"Active\"}, {\"id\": 11, \"name\": \"Massive Dynamic\", \"plan\": \"Enterprise\", \"owner\": \"A. Okafor\", \"seats\": 76, \"status\": \"Trial\", \"statusHtml\": \"Trial\"}, {\"id\": 12, \"name\": \"Cyberdyne\", \"plan\": \"Starter\", \"owner\": \"R. Silva\", \"seats\": 83, \"status\": \"Past due\", \"statusHtml\": \"Past due\"}, {\"id\": 13, \"name\": \"Tyrell\", \"plan\": \"Scale\", \"owner\": \"M. Chen\", \"seats\": 90, \"status\": \"Active\", \"statusHtml\": \"Active\"}, {\"id\": 14, \"name\": \"Aperture\", \"plan\": \"Team\", \"owner\": \"J. Dubois\", \"seats\": 97, \"status\": \"Trial\", \"statusHtml\": \"Trial\"}, {\"id\": 15, \"name\": \"Black Mesa\", \"plan\": \"Enterprise\", \"owner\": \"P. Novak\", \"seats\": 104, \"status\": \"Past due\", \"statusHtml\": \"Past due\"}]", + "remote": false, + "loadingLabel": "Data Table", + "errorLabel": "Data Table", + "retryLabel": "Data Table", + "caption": "Accounts", + "description": "15 records across four plans.", + "searchable": true, + "searchPlaceholder": "Enter a sample value", + "paginated": true, + "pageSize": 5, + "pageSizes": [ + 5, + 10, + 25 + ], + "selectable": true, + "actions": [], + "striped": true, + "bordered": true, + "emptyLabel": "Data Table", + "noResultsLabel": "Data Table", + "clearSearchLabel": "Data Table", + "stickyFirstColumn": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "DatePicker": { + "props": { + "size": "default", + "label": "Start date", + "name": "start-date", + "value": "2026-09-20", + "placeholder": "Enter a sample value", + "type": "date", + "firstDayOfWeek": 0, + "months": [ + { + "label": "Jan", + "value": "01" + }, + { + "label": "Feb", + "value": "02" + }, + { + "label": "Mar", + "value": "03" + }, + { + "label": "Apr", + "value": "04" + }, + { + "label": "May", + "value": "05" + }, + { + "label": "Jun", + "value": "06" + }, + { + "label": "Jul", + "value": "07" + }, + { + "label": "Aug", + "value": "08" + }, + { + "label": "Sep", + "value": "09" + }, + { + "label": "Oct", + "value": "10" + }, + { + "label": "Nov", + "value": "11" + }, + { + "label": "Dec", + "value": "12" + } + ], + "days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "years": [ + 2024, + 2025, + 2026, + 2027, + 2028, + 2029, + 2030 + ], + "variant": "normal", + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "DeviceFrame": { + "props": { + "size": "default", + "title": "Device Frame example", + "description": "A polished default device frame for a modern application.", + "items": [ + { + "id": "device-frame-0-1", + "key": "device-frame-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#device-frame-demo", + "actionHref": "#device-frame-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "device-frame-0-2", + "key": "device-frame-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#device-frame-demo", + "actionHref": "#device-frame-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "variant": "default", + "frameTitle": "Device Frame example", + "showToolbar": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Divider": { + "props": { + "size": "default", + "label": "Related information", + "orientation": "horizontal", + "variant": "solid", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "DragAndDrop": { + "props": { + "size": "default", + "color": "primary", + "title": "Project priorities", + "description": "Drag to reorder, or use the arrow buttons.", + "items": [ + { + "label": "Review designs" + }, + { + "label": "Build the dashboard" + }, + { + "label": "Test accessibility" + }, + { + "label": "Prepare release notes" + } + ], + "variant": "default", + "class": "" + }, + "content": "" + }, + "Drawer": { + "props": { + "open": false, + "defaultOpen": false, + "placement": "right", + "size": "md", + "variant": "default", + "title": "Citizen service details", + "description": "Review supporting information without leaving the current page.", + "icon": "icon-[lucide--panel-right]", + "label": "Drawer", + "closeLabel": "Drawer", + "showClose": true, + "closeOnBackdrop": true, + "closeOnEscape": true, + "duration": 260, + "overlay": true, + "scrollable": true, + "triggerLabel": "Open drawer", + "triggerIcon": "icon-[lucide--panel-right-open]", + "class": "showcase-instance showcase-instance--1" + }, + "content": "

Place forms, filters, navigation, or record details here.

" + }, + "DrawflowCanvas": { + "props": { + "toolbar": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Dropdown": { + "props": { + "value": "sample-1", + "items": [ + { + "label": "Open dashboard", + "description": "View the operational workspace", + "value": "dashboard", + "icon": "icon-[lucide--layout-dashboard]" + }, + { + "label": "Share record", + "value": "share", + "icon": "icon-[lucide--share-2]", + "shortcut": "Ctrl S" + }, + { + "type": "divider" + }, + { + "label": "Delete record", + "value": "delete", + "icon": "icon-[lucide--trash-2]", + "danger": true + } + ], + "open": false, + "defaultOpen": false, + "label": "Open actions", + "ariaLabel": "Dropdown 1 example", + "icon": "icon-[lucide--menu]", + "showChevron": true, + "menuLabel": "Dropdown", + "placement": "bottom-start", + "width": "lg", + "size": "default", + "variant": "raised", + "closeOnSelect": true, + "closeOnOutside": true, + "disabled": false, + "emptyLabel": "Dropdown", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "FeatureCard": { + "props": { + "icon": "icon-[lucide--shield-check]", + "iconStyle": "soft", + "eyebrow": "Citizen support", + "title": "Citizen services", + "description": "Report incidents, request certificates, and find safety information.", + "href": "#citizen-services", + "external": false, + "actionLabel": "Explore services", + "showArrow": true, + "stretchedLink": true, + "badge": "Public", + "size": "default", + "color": "primary", + "variant": "default", + "hover": "lift", + "align": "start", + "disabled": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "FeatureGrid": { + "props": { + "size": "default", + "items": [ + { + "icon": "icon-[lucide--shield-check]", + "eyebrow": "Citizen support", + "title": "Citizen services", + "description": "Report incidents, request certificates, and find safety information.", + "href": "#citizen-services", + "actionLabel": "Explore services", + "badge": "Public", + "color": "primary", + "variant": "default", + "hover": "lift" + }, + { + "icon": "icon-[lucide--map-pin]", + "eyebrow": "Directory", + "title": "Station directory", + "description": "Locate offices, jurisdictions, contacts, and public service hours.", + "href": "#station-directory", + "actionLabel": "Find a station", + "badge": "Directory", + "color": "info", + "variant": "soft", + "hover": "border" + }, + { + "icon": "icon-[lucide--radio-tower]", + "eyebrow": "Verified", + "title": "Public updates", + "description": "Publish official alerts, notices, advisories, and verified information.", + "href": "#public-updates", + "actionLabel": "View updates", + "badge": "Live", + "color": "success", + "variant": "gradient", + "hover": "glow" + } + ], + "columns": 3, + "tabletColumns": 2, + "mobileColumns": 1, + "gap": "lg", + "equalHeight": true, + "align": "start", + "maxWidth": "full", + "variant": "default", + "label": "Feature Grid", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "FeatureIconCard": { + "props": { + "icon": "icon-[lucide--shield-check]", + "title": "Secure by design", + "description": "Build with consistent validation, accessibility, and theme support.", + "href": "#security", + "actionLabel": "Review security", + "badge": "Core", + "size": "default", + "variant": "default", + "align": "start", + "clickableSurface": false, + "descriptionTone": "A polished default feature icon card for a modern application.", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "FileInput": { + "props": { + "size": "default", + "name": "attachment", + "label": "Attachment", + "hiddenLabel": false, + "placeholder": "Choose a file", + "value": "", + "icon": "", + "accept": ".pdf,image/*", + "multiple": false, + "helperText": "PDF or image", + "inline": false, + "variant": "normal", + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "FileUploadProgress": { + "props": { + "size": "default", + "color": "primary", + "label": "Upload progress", + "value": 50, + "max": 100, + "showValue": true, + "fileName": "brand-guidelines.pdf", + "fileSize": "4.8 MB", + "uploadedSize": "2.4 MB", + "status": "uploading", + "cancelLabel": "Cancel upload", + "retryLabel": "Retry upload", + "showCompleteAction": false, + "class": "" + }, + "content": "" + }, + "FlowCanvas": { + "props": { + "nodes": [ + { + "id": "flow-canvas-0-1", + "key": "flow-canvas-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#flow-canvas-demo", + "actionHref": "#flow-canvas-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "flow-canvas-0-2", + "key": "flow-canvas-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#flow-canvas-demo", + "actionHref": "#flow-canvas-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "palette": [ + { + "id": "flow-canvas-0-1", + "key": "flow-canvas-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#flow-canvas-demo", + "actionHref": "#flow-canvas-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "flow-canvas-0-2", + "key": "flow-canvas-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#flow-canvas-demo", + "actionHref": "#flow-canvas-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "label": "Flow Canvas", + "arrangeLabel": "Flow Canvas", + "connectLabel": "Flow Canvas", + "size": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Footer": { + "props": { + "size": "default", + "label": "Footer", + "items": [ + { + "type": "header", + "label": "Product", + "description": "Build consistent interfaces with WRNexusJS.", + "items": [ + { + "label": "Components", + "href": "/base" + }, + { + "label": "Themes", + "href": "/themes" + }, + { + "label": "Examples", + "href": "/blocks" + } + ] + }, + { + "type": "header", + "label": "Developers", + "items": [ + { + "label": "Documentation", + "href": "/docs" + }, + { + "label": "Installation", + "href": "/installation" + }, + { + "label": "Framework guides", + "href": "/framework-guides" + } + ] + }, + { + "type": "header", + "label": "Resources", + "items": [ + { + "label": "Accessibility", + "href": "/accessibility" + }, + { + "label": "Design tokens", + "href": "/colors" + }, + { + "label": "Typography", + "href": "/fonts" + } + ] + }, + { + "type": "header", + "label": "Company", + "items": [ + { + "label": "WorkRoot", + "href": "https://workroot.in", + "external": true + }, + { + "label": "WRNexusJS", + "href": "https://wrnexusjs.dev", + "external": true + } + ] + } + ], + "columns": 4, + "maxWidth": "wide", + "mobileColumns": 2, + "copyright": "© 2026 WRNexusJS. All rights reserved.", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Grid": { + "props": { + "size": "default", + "columns": 3, + "gap": "md", + "maxWidth": "xl", + "class": "showcase-instance showcase-instance--1" + }, + "content": "
OneTwoThree
" + }, + "Hero": { + "props": { + "eyebrow": "Reusable public UI", + "eyebrowIcon": "icon-[lucide--sparkles]", + "icon": "", + "title": "Build polished pages", + "highlight": "with consistent hero layouts", + "description": "Create strong first impressions for public portals, product pages, and service overviews.", + "align": "start", + "size": "default", + "variant": "gradient", + "layout": "split", + "visualPosition": "right", + "visualStyle": "card", + "showDecorations": true, + "fullBleed": true, + "visualTitle": "Hero example", + "visualDescription": "A polished default hero for a modern application.", + "visualItems": [ + { + "id": "hero-0-1", + "key": "hero-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "hero-0-2", + "key": "hero-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "primaryLabel": "Explore components", + "primaryHref": "#components", + "primaryIcon": "icon-[lucide--arrow-right]", + "secondaryLabel": "Read documentation", + "secondaryHref": "#docs", + "secondaryIcon": "icon-[lucide--book-open]", + "tertiaryLabel": "Hero", + "tertiaryHref": "#hero-demo", + "badges": [ + { + "id": "hero-0-1", + "key": "hero-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "hero-0-2", + "key": "hero-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "trustItems": [ + { + "id": "hero-0-1", + "key": "hero-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "hero-0-2", + "key": "hero-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#hero-demo", + "actionHref": "#hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "maxWidth": "wide", + "class": "showcase-instance showcase-instance--1" + }, + "content": "
\n
\n \n Composable visual panel\n

Add a screenshot, product preview, media block, chart, form, or illustration.

\n
" + }, + "HeroActions": { + "props": { + "actions": [ + { + "label": "Browse components", + "href": "#components", + "icon": "icon-[lucide--blocks]", + "variant": "primary" + }, + { + "label": "View metrics", + "href": "#metrics", + "icon": "icon-[lucide--chart-no-axes-column-increasing]", + "variant": "outline" + } + ], + "align": "left", + "orientation": "horizontal", + "stackOnMobile": true, + "fullWidthMobile": true, + "size": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Image": { + "props": { + "size": "default", + "src": "/showcase-images/violet-circle.svg", + "alt": "Abstract violet circle on a dark surface", + "width": "800", + "height": "450", + "rounded": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Input": { + "props": { + "size": "default", + "label": "Input", + "hiddenLabel": false, + "placeholder": "Enter a sample value", + "value": "sample-1", + "variant": "normal", + "icon": "", + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "InputGroup": { + "props": { + "size": "default", + "color": "primary", + "id": "", + "name": "workspace-url", + "label": "Workspace address", + "hiddenLabel": false, + "value": "", + "placeholder": "your-team", + "type": "text", + "startText": "https://", + "endText": ".example.com", + "icon": "", + "iconPosition": "start", + "actionLabel": "", + "helperText": "", + "cornerHint": "", + "error": "", + "inline": false, + "variant": "normal", + "readonly": false, + "disabled": false, + "required": false, + "class": "" + }, + "content": "" + }, + "InputNumber": { + "props": { + "size": "md", + "color": "primary", + "variant": "default", + "name": "default-quantity", + "value": 1, + "min": 0, + "max": 99, + "step": 1 + }, + "content": "" + }, + "Kbd": { + "props": { + "size": "default", + "color": "primary", + "label": "⌘ K", + "class": "" + }, + "content": "" + }, + "LayoutSplitter": { + "props": { + "size": 40, + "orientation": "horizontal", + "minSize": 20, + "step": 5, + "label": "Resize panels", + "class": "showcase-instance showcase-instance--1" + }, + "content": "

Project files

Drag the divider to resize these panels.

Editor

Your selected file appears here.

" + }, + "LegendIndicator": { + "props": { + "size": "default", + "title": "Traffic sources", + "description": "Select a source to toggle its visibility.", + "items": [ + { + "label": "Direct", + "color": "#8b5cf6", + "value": "42%" + }, + { + "label": "Search", + "color": "#14b8a6", + "value": "35%" + }, + { + "label": "Referral", + "color": "#f59e0b", + "value": "23%" + } + ], + "variant": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Link": { + "props": { + "size": "default", + "label": "Open components", + "href": "/base", + "external": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "List": { + "props": { + "size": "default", + "title": "Citizen resources", + "description": "Commonly requested public information.", + "items": [ + { + "label": "Report crime", + "href": "#report", + "icon": "icon-[lucide--shield-alert]" + }, + { + "label": "Track complaint", + "href": "#track", + "icon": "icon-[lucide--scan-search]" + }, + { + "label": "Certificates", + "href": "#certificates", + "icon": "icon-[lucide--file-check-2]" + } + ], + "variant": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "ListGroup": { + "props": { + "size": "default", + "color": "primary", + "title": "Workspace", + "description": "", + "items": [ + { + "value": "inbox", + "label": "Inbox", + "description": "Messages that need your attention", + "icon": "icon-[lucide--inbox]", + "badge": "8" + }, + { + "value": "projects", + "label": "Projects", + "description": "Your team's active work", + "icon": "icon-[lucide--folder]" + }, + { + "value": "archive", + "label": "Archive", + "description": "Completed conversations", + "icon": "icon-[lucide--archive]" + } + ], + "variant": "default", + "value": "inbox", + "class": "" + }, + "content": "" + }, + "Map": { + "props": { + "size": "default", + "title": "Bengaluru", + "description": "Pan and zoom to explore the city", + "items": [], + "variant": "default", + "provider": "openstreetmap", + "latitude": 12.9716, + "longitude": 77.5946, + "zoomLevel": 13, + "imageSrc": "", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "MarketingSectionHeader": { + "props": { + "eyebrow": "Platform capabilities", + "title": "Build complete public experiences", + "description": "Use reusable sections, cards, metrics, heroes, and CTAs.", + "align": "split", + "size": "default", + "actionLabel": "Explore components", + "actionHref": "#components", + "actionIcon": "icon-[lucide--arrow-right]", + "actionExternal": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Marquee": { + "props": { + "size": "default", + "title": "Supported capabilities", + "description": "A polished default marquee for a modern application.", + "items": [ + { + "label": "SSR" + }, + { + "label": "CSR" + }, + { + "label": "Themes" + }, + { + "label": "Accessibility" + }, + { + "label": "Responsive UI" + } + ], + "variant": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "MegaMenu": { + "props": { + "size": "default", + "label": "Products", + "icon": "icon-[lucide--box]", + "columns": [ + { + "heading": "Platform", + "items": [ + { + "label": "Runtime", + "href": "/runtime", + "description": "The browser half of the framework." + }, + { + "label": "Compiler", + "href": "/compiler", + "description": "WRN to JavaScript." + } + ] + }, + { + "heading": "Tooling", + "items": [ + { + "label": "CLI", + "href": "/cli", + "description": "Scaffold, build and deploy." + }, + { + "label": "Editor", + "href": "/editor", + "description": "Language server and syntax." + } + ] + } + ], + "rail": [ + { + "id": "mega-menu-0-1", + "key": "mega-menu-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#mega-menu-demo", + "actionHref": "#mega-menu-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "mega-menu-0-2", + "key": "mega-menu-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#mega-menu-demo", + "actionHref": "#mega-menu-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "featured": { + "id": "mega-menu-0-1", + "key": "mega-menu-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#mega-menu-demo", + "actionHref": "#mega-menu-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + "variant": "default", + "columnCount": 3, + "fullWidth": false, + "openOnHover": true, + "actionLabel": "Mega Menu", + "actionHref": "#mega-menu-demo", + "defaultOpen": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "MetricCard": { + "props": { + "label": "Availability", + "value": "99.9", + "description": "Target service availability.", + "icon": "icon-[lucide--activity]", + "suffix": "%", + "trend": "Stable", + "trendLabel": "30 days", + "trendDirection": "positive", + "progress": -1, + "progressLabel": "Metric Card", + "href": "#status", + "external": false, + "actionLabel": "Open status", + "showArrow": true, + "selectable": false, + "disabled": false, + "size": "default", + "color": "success", + "variant": "default", + "align": "start", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "MetricGrid": { + "props": { + "items": [ + { + "label": "Public services", + "value": "24", + "description": "Citizen-facing services available online.", + "icon": "icon-[lucide--hand-heart]", + "trend": "+4", + "trendLabel": "this release", + "trendDirection": "up", + "color": "primary" + }, + { + "label": "Police stations", + "value": "126", + "description": "Searchable station and jurisdiction entries.", + "icon": "icon-[lucide--landmark]", + "trend": "Updated", + "trendLabel": "today", + "color": "info" + }, + { + "label": "Availability", + "value": "99.9", + "suffix": "%", + "description": "Target service availability.", + "icon": "icon-[lucide--activity]", + "trend": "Stable", + "trendLabel": "30 days", + "trendDirection": "positive", + "color": "success" + } + ], + "columns": 3, + "tabletColumns": 2, + "mobileColumns": 1, + "gap": "lg", + "equalHeight": true, + "dividers": false, + "size": "default", + "variant": "default", + "maxWidth": "full", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Modal": { + "props": { + "open": false, + "defaultOpen": false, + "title": "Confirm service request", + "description": "Review the information before continuing.", + "icon": "icon-[lucide--shield-check]", + "label": "Modal", + "size": "md", + "variant": "default", + "showHeader": true, + "showClose": true, + "closeLabel": "Modal", + "closeOnBackdrop": true, + "closeOnEscape": true, + "closeOnCancel": true, + "closeOnConfirm": false, + "showFooter": true, + "cancelLabel": "Go back", + "confirmLabel": "Confirm request", + "confirmIcon": "icon-[lucide--check]", + "confirmDisabled": false, + "confirmLoading": false, + "destructive": false, + "triggerLabel": "Open modal", + "triggerIcon": "icon-[lucide--square-stack]", + "scrollable": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "

This request will be submitted for review.

" + }, + "Nav": { + "props": { + "size": "default", + "items": [ + { + "label": "Home", + "href": "/", + "value": "home", + "icon": "icon-[lucide--house]" + }, + { + "label": "Inbox", + "href": "/inbox", + "value": "inbox", + "badge": "9" + }, + { + "label": "Reports", + "href": "/reports", + "value": "reports" + }, + { + "label": "Archive", + "href": "/archive", + "value": "archive", + "disabled": true + } + ], + "active": "inbox", + "label": "Nav", + "collapsible": true, + "toggleLabel": "Nav", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Navbar": { + "props": { + "size": "default", + "label": "Primary navigation", + "topbarLabel": "Navbar", + "brand": { + "label": "Northwind", + "description": "Console", + "href": "/" + }, + "items": [ + { + "label": "Overview", + "href": "/", + "value": "overview" + }, + { + "label": "Projects", + "href": "/projects", + "value": "projects" + }, + { + "label": "Reports", + "href": "/reports", + "value": "reports" + } + ], + "actions": [ + { + "label": "Sign in", + "href": "/login", + "variant": "link" + }, + { + "label": "Start free", + "href": "/signup", + "variant": "primary" + } + ], + "active": "projects", + "sticky": false, + "floating": false, + "openOnHover": false, + "mobileLabel": "Navbar", + "flushMobile": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "PageHeader": { + "props": { + "eyebrow": "Component showcase", + "title": "Page headers with clear hierarchy", + "description": "Use PageHeader for services, directories, legal pages, guides, dashboards, and public information.", + "icon": "icon-[lucide--panel-top]", + "align": "start", + "centered": false, + "compact": false, + "size": "default", + "maxWidth": "full", + "showBreadcrumbs": true, + "breadcrumbs": [ + { + "label": "Home", + "href": "/", + "icon": "icon-[lucide--house]" + }, + { + "label": "Components", + "href": "/base" + }, + { + "label": "Page header", + "current": true + } + ], + "breadcrumbParentHref": "#page-header-demo", + "primaryLabel": "Primary action", + "primaryHref": "#primary", + "primaryIcon": "icon-[lucide--arrow-down]", + "secondaryLabel": "Secondary action", + "secondaryHref": "#secondary", + "secondaryIcon": "icon-[lucide--house]", + "variant": "default", + "borderBottom": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Pagination": { + "props": { + "color": "primary", + "size": "default", + "page": 2, + "pageSize": 10, + "total": 137, + "variant": "compact", + "siblingCount": 1, + "showSummary": true, + "label": "Pagination", + "previousLabel": "Previous", + "nextLabel": "Next", + "hrefTemplate": "", + "class": "" + }, + "content": "" + }, + "PinInput": { + "props": { + "size": "default", + "label": "Default four-digit PIN", + "name": "pin-input-1", + "value": "", + "length": 4, + "pattern": "[0-9]", + "inputMode": "numeric", + "placeholder": "○", + "masked": false, + "disabled": false, + "readonly": false, + "required": false, + "autoSubmit": false, + "allowPaste": true, + "separator": "", + "groupSize": 0, + "helpText": "", + "invalid": false, + "validationMessage": "" + }, + "content": "" + }, + "Popover": { + "props": { + "open": false, + "defaultOpen": false, + "triggerLabel": "View details", + "triggerIcon": "icon-[lucide--info]", + "title": "Verified public information", + "description": "This panel can contain text, actions, forms, or custom content.", + "icon": "icon-[lucide--badge-check]", + "width": "lg", + "size": "default", + "variant": "raised", + "showArrow": true, + "showClose": true, + "closeLabel": "Popover", + "closeOnOutside": true, + "closeOnEscape": true, + "actionLabel": "Open guide", + "actionHref": "#guide", + "closeOnAction": true, + "disabled": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "

Popover content remains attached to its trigger and becomes mobile-friendly on small screens.

" + }, + "PortalDashboard": { + "props": { + "size": "default", + "title": "Portal Dashboard example", + "description": "A polished default portal dashboard for a modern application.", + "descriptionKey": "A polished default portal dashboard for a modern application.", + "metrics": [ + { + "id": "portal-dashboard-0-1", + "key": "portal-dashboard-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "portal-dashboard-0-2", + "key": "portal-dashboard-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "actions": [ + { + "id": "portal-dashboard-0-1", + "key": "portal-dashboard-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "portal-dashboard-0-2", + "key": "portal-dashboard-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "updates": [ + { + "id": "portal-dashboard-0-1", + "key": "portal-dashboard-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "portal-dashboard-0-2", + "key": "portal-dashboard-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "tasks": [ + { + "id": "portal-dashboard-0-1", + "key": "portal-dashboard-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "portal-dashboard-0-2", + "key": "portal-dashboard-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#portal-dashboard-demo", + "actionHref": "#portal-dashboard-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "PreferenceSwitcher": { + "props": { + "size": "default", + "themeLabel": "Preference Switcher", + "colorLabel": "Preference Switcher", + "languageLabel": "Preference Switcher", + "compact": false, + "iconOnly": true, + "showCurrentLanguage": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Progress": { + "props": { + "size": "default", + "label": "Progress", + "value": 36, + "max": 100, + "showValue": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "PublicPageShell": { + "props": { + "maxWidth": "xl", + "fullWidth": false, + "background": "default", + "size": "default", + "variant": "default", + "class": "showcase-instance showcase-instance--1" + }, + "content": "\n
\n
Header slot
\n
Public page content
\n
Footer slot
\n
" + }, + "Radio": { + "props": { + "size": "default", + "label": "Radio", + "hiddenLabel": false, + "placeholder": "Enter a sample value", + "variant": "normal", + "icon": "", + "value": "sample-1", + "options": [ + { + "id": "radio-0-1", + "key": "radio-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#radio-demo", + "actionHref": "#radio-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "radio-0-2", + "key": "radio-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#radio-demo", + "actionHref": "#radio-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "checked": false, + "card": false, + "rightAligned": false, + "list": false, + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "RangeSlider": { + "props": { + "size": "default", + "color": "primary", + "id": "", + "name": "volume", + "label": "Scale", + "hiddenLabel": false, + "placeholder": "", + "variant": "normal", + "icon": "", + "iconPosition": "start", + "value": 2.4, + "min": 0, + "max": 5, + "step": 0.2, + "showValue": true, + "showBounds": true, + "showSteps": false, + "marks": [], + "helperText": "", + "cornerHint": "", + "error": "", + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "" + }, + "content": "" + }, + "Rating": { + "props": { + "size": "default", + "color": "primary", + "title": "Rate your experience", + "description": "", + "items": [], + "variant": "default", + "value": 3, + "max": 5, + "step": 1, + "readonly": false, + "disabled": false, + "class": "" + }, + "content": "" + }, + "Scrollspy": { + "props": { + "size": "default", + "items": [ + { + "label": "Overview", + "href": "#overview" + }, + { + "label": "Installation", + "href": "#installation" + }, + { + "label": "Usage", + "href": "#usage" + }, + { + "label": "API", + "href": "#api" + } + ], + "active": "#overview", + "label": "Scrollspy", + "heading": "On this page", + "sticky": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "SearchBox": { + "props": { + "size": "default", + "label": "Search documentation", + "name": "site-search", + "value": "sample-1", + "placeholder": "Search components and guides", + "type": "search", + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Section": { + "props": { + "id": "standard-section", + "size": "default", + "variant": "grid", + "spacing": "lg", + "maxWidth": "xl", + "fullWidth": false, + "borderTop": false, + "borderBottom": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "
Standard section content
" + }, + "SectionHeader": { + "props": { + "eyebrow": "Public services", + "title": "Everything citizens need", + "description": "Discover online services, directories, guidance, and verified public information.", + "align": "left", + "size": "default", + "headingLevel": 2, + "maxWidth": "3xl", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Select": { + "props": { + "size": "default", + "label": "Select", + "hiddenLabel": false, + "value": "sample-1", + "values": [ + { + "id": "select-0-1", + "key": "select-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#select-demo", + "actionHref": "#select-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "select-0-2", + "key": "select-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#select-demo", + "actionHref": "#select-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "options": [ + { + "id": "select-0-1", + "key": "select-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#select-demo", + "actionHref": "#select-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "select-0-2", + "key": "select-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#select-demo", + "actionHref": "#select-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "placeholder": "Enter a sample value", + "variant": "normal", + "icon": "", + "inline": false, + "multiple": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Sidebar": { + "props": { + "size": "default", + "label": "Workspace", + "items": [ + { + "label": "Dashboard", + "href": "/", + "value": "dash", + "icon": "icon-[lucide--gauge]" + }, + { + "heading": "Projects", + "items": [ + { + "label": "Active", + "href": "/active", + "value": "active", + "badge": "4" + }, + { + "label": "Archive", + "value": "archive", + "items": [ + { + "label": "2025", + "href": "/a/2025", + "value": "a2025" + }, + { + "label": "2024", + "href": "/a/2024", + "value": "a2024" + } + ] + } + ] + }, + { + "heading": "Account", + "items": [ + { + "label": "Settings", + "href": "/settings", + "value": "settings", + "icon": "icon-[lucide--settings]" + } + ] + } + ], + "active": "active", + "mobileLabel": "Sidebar", + "drawerTitle": "Sidebar example", + "mobile": true, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Skeleton": { + "props": { + "label": "Skeleton", + "size": "md", + "lines": 3, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Spinner": { + "props": { + "label": "Spinner", + "size": "md", + "lines": 3, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "SplitHero": { + "props": { + "eyebrow": "Balanced layout", + "title": "Combine detailed content", + "highlight": "with a strong visual panel", + "description": "Use SplitHero for product explanations, department introductions, campaigns, and feature storytelling.", + "primaryLabel": "View public services", + "primaryHref": "#services", + "primaryIcon": "icon-[lucide--arrow-right]", + "secondaryLabel": "Contact department", + "secondaryHref": "#contact", + "secondaryIcon": "icon-[lucide--phone]", + "visualPosition": "right", + "reverse": false, + "ratio": "balanced", + "align": "start", + "size": "default", + "variant": "default", + "maxWidth": "wide", + "fullBleed": true, + "flushMobile": false, + "descriptionWeight": "A polished default split hero for a modern application.", + "mobileStack": true, + "visualTitle": "Split Hero example", + "visualDescription": "A polished default split hero for a modern application.", + "visualItems": [ + { + "id": "split-hero-0-1", + "key": "split-hero-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#split-hero-demo", + "actionHref": "#split-hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "split-hero-0-2", + "key": "split-hero-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#split-hero-demo", + "actionHref": "#split-hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "trustItems": [ + { + "id": "split-hero-0-1", + "key": "split-hero-0-1", + "value": "primary", + "label": "Primary workflow", + "title": "Primary workflow", + "description": "A clean default configuration for everyday product interfaces.", + "text": "A configurable sample message.", + "href": "#split-hero-demo", + "actionHref": "#split-hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": true, + "selected": true, + "checked": true, + "disabled": false, + "outgoing": false, + "open": true, + "number": 1, + "count": 16, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 1", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + }, + { + "id": "split-hero-0-2", + "key": "split-hero-0-2", + "value": "secondary", + "label": "Additional option", + "title": "Supporting example", + "description": "A clean default configuration for everyday product interfaces.", + "text": "Another configurable message.", + "href": "#split-hero-demo", + "actionHref": "#split-hero-demo", + "actionLabel": "View details", + "icon": "icon-[lucide--sparkles]", + "iconClass": "icon-[lucide--sparkles]", + "variant": "primary", + "status": "Active", + "time": "09:30", + "current": false, + "selected": false, + "checked": false, + "disabled": false, + "outgoing": true, + "open": true, + "number": 2, + "count": 32, + "percentage": 72, + "color": "#7c3aed", + "target": "_self", + "ariaLabel": "Open primary workflow 2", + "items": [ + { + "label": "Nested option A", + "value": "nested-a" + }, + { + "label": "Nested option B", + "value": "nested-b" + } + ], + "links": [ + { + "label": "Documentation", + "href": "#documentation" + }, + { + "label": "API reference", + "href": "#api-reference" + } + ], + "values": [ + "Included", + "Standard" + ] + } + ], + "class": "showcase-instance showcase-instance--1" + }, + "content": "
\n
\n \n Composable visual panel\n

Add a screenshot, product preview, media block, chart, form, or illustration.

\n
" + }, + "StatsBar": { + "props": { + "items": [ + { + "label": "Composite components", + "value": "18", + "description": "Reusable public-page building blocks", + "icon": "icon-[lucide--blocks]", + "color": "primary" + }, + { + "label": "Rendering", + "value": "SSR", + "description": "Server-first WRNexusJS rendering", + "icon": "icon-[lucide--server]", + "color": "info" + }, + { + "label": "Responsive", + "value": "Yes", + "description": "Mobile, tablet, and desktop layouts", + "icon": "icon-[lucide--monitor-smartphone]", + "color": "success" + }, + { + "label": "Theme aware", + "value": "Yes", + "description": "Active theme and accent palettes", + "icon": "icon-[lucide--palette]", + "color": "warning" + } + ], + "columns": 4, + "compact": true, + "dividers": true, + "icons": true, + "size": "default", + "variant": "raised", + "maxWidth": "xl", + "mobileColumns": 1, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Stepper": { + "props": { + "size": "default", + "steps": [ + { + "label": "Account", + "description": "Your details" + }, + { + "label": "Billing", + "description": "Payment method" + }, + { + "label": "Confirm", + "description": "Review and submit" + } + ], + "active": 1, + "controlled": false, + "clickable": false, + "label": "Stepper", + "showPanel": false, + "controls": false, + "allowSkip": false, + "nextDisabled": false, + "backLabel": "Back", + "nextLabel": "Next", + "skipLabel": "Skip", + "finishLabel": "Finish", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "StrongPassword": { + "props": { + "label": "Live strength meter", + "name": "strong-password-1", + "value": "Northstar!2026", + "placeholder": "Create a strong password", + "autocomplete": "new-password", + "minLength": 8, + "specialCharactersSet": "!@#$%^&*()_+-=[]{}|;:,.<>?", + "requireLowercase": true, + "requireUppercase": true, + "requireNumber": true, + "requireSpecialCharacter": true, + "showRequirements": false, + "presentation": "inline", + "hintText": "", + "disabled": false, + "readonly": false, + "required": false, + "invalid": false, + "validationMessage": "" + }, + "content": "" + }, + "StyledIcon": { + "props": { + "size": "default", + "color": "primary", + "title": "Styled Icon", + "description": "", + "items": [], + "variant": "default", + "icon": "icon-[lucide--sparkles]", + "label": "Verified", + "class": "" + }, + "content": "" + }, + "Switch": { + "props": { + "size": "default", + "label": "Switch", + "hiddenLabel": false, + "placeholder": "Enter a sample value", + "variant": "normal", + "icon": "", + "value": "sample-1", + "inline": false, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Tabs": { + "props": { + "size": "default", + "items": [ + { + "label": "Overview", + "value": "overview", + "content": "Overview content" + }, + { + "label": "Requirements", + "value": "requirements", + "content": "Requirements content" + }, + { + "label": "Process", + "value": "process", + "content": "Process content" + } + ], + "active": "overview", + "orientation": "horizontal", + "label": "Service information", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "TextLink": { + "props": { + "label": "Learn more", + "href": "#learn", + "external": false, + "icon": "", + "showArrow": true, + "underline": false, + "size": "default", + "variant": "default", + "disabled": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Textarea": { + "props": { + "size": "default", + "label": "Textarea", + "hiddenLabel": false, + "placeholder": "Enter a sample value", + "value": "sample-1", + "variant": "normal", + "icon": "", + "inline": false, + "rows": 5, + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "TimePicker": { + "props": { + "size": "default", + "name": "meeting-time", + "label": "Meeting time", + "hiddenLabel": false, + "value": "09:30", + "placeholder": "Enter a sample value", + "variant": "normal", + "icon": "", + "inline": false, + "minuteStep": 5, + "hours": [ + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23" + ], + "minutes": [ + "00", + "05", + "10", + "15", + "20", + "25", + "30", + "35", + "40", + "45", + "50", + "55" + ], + "readonly": false, + "disabled": false, + "required": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Timeline": { + "props": { + "size": "default", + "title": "Request progress", + "description": "Latest updates for this service request.", + "items": [ + { + "title": "Request submitted", + "description": "Received successfully", + "time": "09:30", + "status": "complete" + }, + { + "title": "Verification", + "description": "Documents are being reviewed", + "time": "10:15", + "current": true + }, + { + "title": "Decision", + "description": "Pending verification", + "time": "—" + } + ], + "variant": "default", + "numbered": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "Toaster": { + "props": { + "size": "default", + "position": "bottom-right", + "duration": 4500, + "max": 4, + "pauseOnHover": true, + "showIcon": true, + "closable": true, + "showProgress": true, + "closeLabel": "Toaster", + "class": "showcase-instance showcase-instance--1" + }, + "content": "" + }, + "ToggleCount": { + "props": { + "size": "default", + "variant": "segmented", + "class": "showcase-instance showcase-instance--1", + "value": "monthly", + "firstValue": "monthly", + "firstLabel": "Monthly", + "secondValue": "annual", + "secondLabel": "Yearly", + "ariaLabel": "Toggle Count 1 example", + "items": [ + { + "title": "Starter", + "label": "Starter", + "description": "For individuals getting started.", + "monthly": 12, + "annual": 120 + }, + { + "title": "Team", + "label": "Team", + "description": "For teams working together.", + "monthly": 29, + "annual": 290 + } + ], + "firstValueKey": "monthly", + "secondValueKey": "annual", + "align": "start", + "fullWidth": false, + "disabled": false, + "animate": true, + "animationDuration": 450, + "animationSteps": 18 + }, + "content": "" + }, + "TogglePassword": { + "props": { + "size": "default", + "label": "Password", + "name": "toggle-password-1", + "value": "Northstar!2026", + "placeholder": "Enter your password", + "autocomplete": "current-password", + "minlength": "", + "maxlength": "128", + "pattern": "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}", + "fields": [], + "visible": false, + "toggleable": true, + "toggleMode": "button", + "checkboxLabel": "Show password", + "disabled": false, + "readonly": false, + "required": false, + "invalid": false, + "helpText": "", + "validationMessage": "" + }, + "content": "" + }, + "Tooltip": { + "props": { + "id": "help-tooltip", + "open": false, + "defaultOpen": false, + "title": "Helpful information", + "description": "A polished default tooltip for a modern application.", + "content": "Tooltips explain controls without interrupting the user.", + "placement": "top", + "size": "default", + "variant": "dark", + "offset": 10, + "showArrow": true, + "interactive": false, + "disabled": false, + "class": "showcase-instance showcase-instance--1" + }, + "content": "
" + }, + "TreeView": { + "props": { + "title": "Project files", + "description": "", + "items": [ + { + "value": "src", + "label": "src", + "children": [ + { + "value": "pages", + "label": "pages", + "children": [ + { + "value": "home", + "label": "index.wrn" + } + ] + }, + { + "value": "styles", + "label": "styles.css" + } + ] + }, + { + "value": "readme", + "label": "README.md" + } + ], + "valueKey": "value", + "labelKey": "label", + "defaultExpanded": [ + "src" + ], + "selected": "", + "size": "default", + "color": "primary", + "class": "" + }, + "content": "" + }, + "Typography": { + "props": { + "size": "default", + "columns": 2, + "maxWidth": "xl", + "class": "showcase-instance showcase-instance--1" + }, + "content": "

Readable page content

Typography creates consistent hierarchy and comfortable reading rhythm.

  • Clear headings
  • Readable paragraphs
  • Consistent lists
" + }, + "WysiwygEditor": { + "props": { + "size": "default", + "color": "primary", + "title": "Project update", + "description": "", + "items": [], + "variant": "default", + "value": "Share progress, decisions, and next steps with your team.", + "readonly": false, + "placeholder": "Write something…", + "class": "" + }, + "content": "" + } +} diff --git a/packages/design-studio/src/gallery.ts b/packages/design-studio/src/gallery.ts new file mode 100644 index 00000000..5442848d --- /dev/null +++ b/packages/design-studio/src/gallery.ts @@ -0,0 +1,160 @@ +import samples from "./gallery-samples.json"; +import { resolveControlTemplate } from "@wrnexus/core"; + +const illustration = `data:image/svg+xml,${encodeURIComponent('')}`; +const map = `data:image/svg+xml,${encodeURIComponent('')}`; + +function localAssets(value: unknown, key = ""): unknown { + if (typeof value === "string") + return /^(?:https?:\/\/|\/showcase-images\/)/.test(value) && + /src|image|avatar|poster|logo/i.test(key) + ? illustration + : /^https?:\/\//.test(value) && /href|action/i.test(key) + ? "#sample" + : value; + if (Array.isArray(value)) return value.map((entry) => localAssets(entry, key)); + if (value && typeof value === "object") + return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, localAssets(v, k)])); + return value; +} +/** Fixed, populated sample content; never contains draft-supplied executable markup. */ +export function gallerySample(name: string) { + const source = (samples as Record; content: string }>)[ + name + ]; + if (!source) throw Error(`Missing gallery sample: ${name}`); + const props = localAssets(structuredClone(source.props)) as Record; + const field = [ + "Input", + "Textarea", + "Select", + "Checkbox", + "Radio", + "Switch", + "AdvancedSelect", + "ComboBox", + "DatePicker", + "TimePicker", + "RangeSlider", + "InputNumber", + "PinInput", + "FileInput", + "StrongPassword", + "TogglePassword", + ].includes(name); + if (Object.hasOwn(props, "id") || field) props.id = `sample-${name.toLowerCase()}`; + if (field && !props.name) props.name = `sample-${name.toLowerCase()}`; + props.class = ""; + let content = source.content; + let hint = "Sample data. Interact with the controls to try this component."; + if (name === "Map") { + Object.assign(props, { + title: "Sample location map", + description: "Schematic sample: select a marker or use the zoom controls.", + imageSrc: map, + imageAlt: "Schematic sample map", + items: [ + { label: "Office", x: 32, y: 42 }, + { label: "Park", x: 68, y: 70 }, + ], + }); + hint = "Local schematic sample; no map service or API key is needed."; + } + if (name === "DrawflowCanvas" || name === "FlowCanvas") + Object.assign(props, { + nodes: [ + { + id: "start", + label: "Start", + x: 30, + y: 50, + input: false, + ports: [{ key: "next", label: "Next", target: "review" }], + }, + { + id: "review", + label: "Review", + x: 260, + y: 100, + ports: [], + }, + ], + palette: [{ kind: "task", label: "Add task" }], + }); + if (name === "Toaster") + content = ''; + if (name === "BackToTop") { + props.threshold = 0; + props.alwaysVisible = true; + props.showLabel = true; + hint = "Scroll this preview, then use the back-to-top control."; + } + if (name === "DeviceFrame") { + props.items = []; + content = + "

Sample mobile page

This local content sits inside the device frame.

Account preferences

Rotate the preview to compare portrait and landscape layouts.

"; + } + if (name === "VisuallyHidden") + hint = "This accessibility component intentionally hides its content visually."; + if (name === "CustomScrollbar") + content = Array.from( + { length: 12 }, + (_, i) => `

Sample row ${i + 1} — scroll to see more content.

`, + ).join(""); + if (["AdvancedSelect", "Checkbox", "Select"].includes(name)) delete props.values; + if (["AuthForm", "AuthSplitLayout", "FileInput"].includes(name)) + hint = "Preview only. Submissions and file uploads are not sent anywhere."; + return { props, content, hint }; +} + +export function previewSlots(html: string, content: string) { + const named = new Map(); + // Author-controlled sample slots are the only HTML accepted here. + let defaultContent = ""; + let cursor = 0; + const slots = /
/g; + for (let match = slots.exec(content); match; match = slots.exec(content)) { + defaultContent += content.slice(cursor, match.index); + const tags = /<\/?div\b[^>]*>/g; + tags.lastIndex = slots.lastIndex; + let depth = 1; + let end = tags.lastIndex; + for (let tag = tags.exec(content); tag; tag = tags.exec(content)) { + depth += tag[0].startsWith("]*)>([\s\S]*?)<\/slot>/g, (all, attributes) => { + const name = /\bname=["']([^"']+)["']/.exec(attributes)?.[1]; + const replacement = name ? named.get(name) : defaultContent; + return replacement ? `${replacement}` : all; + }); +} + +/** Conditional/loop bodies are request-owned templates, and need the same slots as SSR. */ +export function previewSampleMarkup(html: string, content: string) { + const seen = new Set(); + const visit = (markup: string): string => { + for (const match of markup.matchAll(/data-wrn-(?:if|each)="(@[^"]+)"/g)) { + if (seen.has(match[1]!)) continue; + seen.add(match[1]!); + const definition = resolveControlTemplate(match[1]!); + for (const entry of Array.isArray(definition) ? definition : [definition]) { + if (!entry || typeof entry !== "object") continue; + for (const key of ["body", "empty"]) { + if (typeof entry[key] === "string") entry[key] = visit(entry[key]); + } + } + } + return previewSlots(markup, content); + }; + return visit(html); +} diff --git a/packages/design-studio/src/preview-assets.ts b/packages/design-studio/src/preview-assets.ts new file mode 100644 index 00000000..a182ca0c --- /dev/null +++ b/packages/design-studio/src/preview-assets.ts @@ -0,0 +1,57 @@ +import lucide from "@iconify-json/lucide/icons.json"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { createRequire } from "node:module"; + +/** Inline the real shipped icon shapes: preview frames never need an icon CDN. */ +export function previewIconCss(markup: string) { + const names = new Set([...markup.matchAll(/icon-\[lucide--([\w-]+)\]/g)].map((m) => m[1]!)); + // Controllers introduce these icons after interaction. + for (const name of ["check", "x", "chevron-down", "chevron-up", "loader-circle"]) names.add(name); + const icons = lucide.icons as Record; + return [...names] + .map((name) => { + const aliases = lucide.aliases as Record; + let key = name; + const visited = new Set(); + while (!icons[key] && aliases[key] && !visited.has(key)) { + visited.add(key); + key = aliases[key]!.parent; + } + const icon = icons[key]; + if (!icon) throw Error(`Unknown preview icon: lucide--${name}`); + const svg = `${icon.body}`; + const url = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`; + return `[class~="icon-[lucide--${name}]"]{display:inline-block;width:1em;height:1em;flex-shrink:0;background-color:currentColor;mask:${url} center/contain no-repeat;-webkit-mask:${url} center/contain no-repeat}`; + }) + .join("\n"); +} + +const runtimeCache = new Map>(); +export async function previewProviderRuntime(name: "chart" | "drawflow") { + if (!runtimeCache.has(name)) + runtimeCache.set( + name, + (async () => { + const result = await Bun.build({ + entrypoints: [fileURLToPath(import.meta.resolve(`@wrnexus/ui/${name}-runtime`))], + target: "browser", + format: "iife", + minify: true, + }); + if (!result.success) throw Error(result.logs.join("\n")); + return result.outputs[0]!.text(); + })().catch((error) => { + runtimeCache.delete(name); + throw error; + }), + ); + return runtimeCache.get(name)!; +} +export function previewDrawflowCss() { + const require = createRequire(import.meta.resolve("@wrnexus/ui/drawflow-runtime")); + return ( + readFileSync(require.resolve("drawflow/dist/drawflow.min.css"), "utf8") + + readFileSync(fileURLToPath(import.meta.resolve("@wrnexus/ui/drawflow.css")), "utf8") + ); +} diff --git a/packages/design-studio/src/preview-composition.ts b/packages/design-studio/src/preview-composition.ts new file mode 100644 index 00000000..287e1a4b --- /dev/null +++ b/packages/design-studio/src/preview-composition.ts @@ -0,0 +1,87 @@ +import { resolveControlTemplate } from "@wrnexus/core"; +import { COMPONENT_CONTRACTS } from "@wrnexus/design-system"; +import { previewSampleMarkup } from "./gallery.ts"; + +/** Resolve shipped component mounts in both visible markup and request-owned branches. */ +export async function preparePreviewMarkup( + body: string, + render: (name: string, props: Record) => Promise, +) { + const prepared = new Set(); + const prototypes = new Map(); + const names = new Set(); + const decode = (value: string) => + value.replace( + /&(?:quot|#39|lt|gt|amp);/g, + (entity) => + ({ """: '"', "'": "'", "<": "<", ">": ">", "&": "&" })[entity]!, + ); + const visit = async (markup: string, depth = 0): Promise => { + if (depth > 30) throw Error("Preview component composition exceeded its depth limit."); + for (const match of markup.matchAll(/data-wrn-hydration="([^:"]+):/g)) names.add(match[1]!); + for (const match of markup.matchAll(/data-wrn-(?:if|each)="(@[^"]+)"/g)) { + if (prepared.has(match[1]!)) continue; + prepared.add(match[1]!); + const definition = resolveControlTemplate(match[1]!); + for (const entry of Array.isArray(definition) ? definition : [definition]) + if (entry && typeof entry === "object") + for (const key of ["body", "empty"]) + if (typeof entry[key] === "string") entry[key] = await visit(entry[key], depth + 1); + } + const mounts = /]*\bdata-component="([\w-]+)"[^>]*)>/g; + let output = "", + cursor = 0; + for (let match = mounts.exec(markup); match; match = mounts.exec(markup)) { + output += markup.slice(cursor, match.index); + const tags = /<\/?div\b[^>]*>/g; + tags.lastIndex = mounts.lastIndex; + let nesting = 1, + end = tags.lastIndex, + close = end; + for (let tag = tags.exec(markup); tag; tag = tags.exec(markup)) { + nesting += tag[0].startsWith(" + n.toLowerCase().replace(/[-_]/g, "") === match[2]!.toLowerCase().replace(/[-_]/g, ""), + ); + if (!name) throw Error(`Unknown nested preview component: ${match[2]}`); + const props: Record = {}; + for (const attr of match[1]!.matchAll(/([\w-]+)="([^"]*)"/g)) { + if (attr[1] === "data-component" || attr[1]!.startsWith("data-wrn-")) continue; + const value = decode(attr[2]!); + if (/^\{[\s\S]*\}$/.test(value)) { + try { + JSON.parse(value); + } catch { + continue; + } + } + props[attr[1]!] = value; + } + const inner = markup.slice(mounts.lastIndex, close); + const key = JSON.stringify([name, props, inner]); + if (!prototypes.has(key)) + prototypes.set(key, previewSampleMarkup(await render(name, props), inner)); + const bindings = [ + ...match[1]!.matchAll(/\s(data-wrn-(?:prop-bind-[\w-]+|loop-locals)="[^"]*")/g), + ] + .map((m) => " " + m[1]) + .join(""); + const rendered = prototypes + .get(key)! + .replace(/(<[^>]*\bdata-scope="[^"]*")/, "$1" + bindings); + output += await visit(rendered, depth + 1); + cursor = end; + mounts.lastIndex = end; + } + return output + markup.slice(cursor); + }; + return { body: await visit(body), names }; +} diff --git a/packages/design-studio/src/preview.ts b/packages/design-studio/src/preview.ts index 2eb122b5..34f3d265 100644 --- a/packages/design-studio/src/preview.ts +++ b/packages/design-studio/src/preview.ts @@ -1,10 +1,13 @@ import { validateAgentPage } from "./agent-pages.ts"; import { readFileSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; -import { join } from "node:path"; +import { join, dirname } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { createHash } from "node:crypto"; -import { compileWrnFile } from "@wrnexus/compiler"; +import { compileWrnFile, generateBrowserModule, parse } from "@wrnexus/compiler"; +import { gallerySample, previewSampleMarkup } from "./gallery.ts"; +import { preparePreviewMarkup } from "./preview-composition.ts"; +import { previewIconCss, previewProviderRuntime, previewDrawflowCss } from "./preview-assets.ts"; import { runWithRequestContext } from "@wrnexus/core"; import { REACTIVE_RUNTIME } from "@wrnexus/csr"; import { renderDocument } from "@wrnexus/ssr"; @@ -17,16 +20,31 @@ import { } from "@wrnexus/design-system"; const modules = new Map) => string }>>(); +const controllers = new Map(); +const iconSources = new Map(); const escape = (text: string) => text.replace( /[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]!, ); async function component(name: string, props: Record = {}) { - if (!Object.hasOwn(COMPONENT_CONTRACTS, name)) throw Error("Unknown component."); + if (!Object.hasOwn(COMPONENT_CONTRACTS, name) && name !== "SelectStyles") + throw Error("Unknown component."); if (!modules.has(name)) { - const sourcePath = uiComponentPath(name); + const sourcePath = + name === "SelectStyles" + ? join(dirname(uiComponentPath("AdvancedSelect")), "../styles/SelectStyles.wrn") + : uiComponentPath(name); const source = readFileSync(sourcePath, "utf8"); + const ast = parse(source); + iconSources.set(ast.name, source); + const browser = generateBrowserModule(ast); + if (/^import\s/m.test(browser)) + throw Error(`Preview controller has an unresolved import: ${name}`); + controllers.set( + ast.name, + `(()=>{${browser.replace(/\bexport\s+(?=(?:const|function)\b)/g, "")}\nreturn {bindClientScope,__wrnexusClientFunctions,__wrnexusImportedBindings};})()`, + ); const directory = join(tmpdir(), "wrnexus-design-studio"); mkdirSync(directory, { recursive: true }); const target = join( @@ -60,15 +78,19 @@ export async function previewDocument( const mode = options.mode ?? system.defaultMode ?? "light"; return runWithRequestContext({} as never, async () => { let body: string; + let galleryIcons = ""; if (options.content === "catalog") { const page = Math.max(0, Math.min(6, options.page ?? 0)); const names = Object.keys(COMPONENT_CONTRACTS).slice(page * 16, (page + 1) * 16); const cards = []; - for (const name of names) + for (const name of names) { + const sample = gallerySample(name); + galleryIcons += JSON.stringify(sample.props) + sample.content; cards.push( - `

${escape(name)}

${await component(name)}
`, + `

${escape(name)}

${escape(sample.hint)}

${previewSampleMarkup(await component(name, sample.props), sample.content)}${name === "Toaster" ? sample.content : ""}
`, ); - body = `

Component gallery

Page ${page + 1} of 7 · Default states

`; + } + body = `

Component gallery

Page ${page + 1} of 7 · Interactive samples

`; } else { const nav = await component("Nav", { items: [ @@ -135,14 +157,33 @@ export async function previewDocument( }); body = `${nav}${heading}
${card}
${input}${select}${textarea}
${toggle}${checkbox}
${button}${disabled}${loading}
`; } - const runtime = REACTIVE_RUNTIME; + const prepared = await preparePreviewMarkup(body, component); + body = prepared.body; + const names = prepared.names; + const definitions = [...names] + .filter((name) => controllers.has(name)) + .map((name) => `${JSON.stringify(name)}:${controllers.get(name)}`) + .join(","); + const bindings = `const previewModules={${definitions}};function bindPreview(){document.querySelectorAll('[data-wrn-hydration]').forEach(el=>{const name=el.getAttribute('data-wrn-hydration').split(':')[0];if(previewModules[name])el.__wrnexusClientModule=previewModules[name];});}bindPreview();new MutationObserver(bindPreview).observe(document.documentElement,{childList:true,subtree:true});`; + let providers = ""; + let providerCss = ""; + if (body.includes("data-chart-config=")) providers += await previewProviderRuntime("chart"); + if (body.includes("data-drawflow-editor")) { + providers += await previewProviderRuntime("drawflow"); + providerCss += previewDrawflowCss(); + } + const runtime = + bindings + + REACTIVE_RUNTIME + + providers + + "\ndocument.addEventListener('DOMContentLoaded',()=>parent.postMessage({type:'wrn-studio-preview-ready',path:location.pathname},'*'),{once:true});"; return renderDocument({ meta: { title: system.name + " preview" }, body, htmlAttrs: ` dir="${options.direction === "rtl" ? "rtl" : "ltr"}" data-theme="${escape(mode)}"`, - extraHead: ``, + extraHead: ``, componentRecipeCss: theme.recipeCss, - extraBody: ``, + extraBody: ``, }); }); } @@ -165,7 +206,7 @@ export async function previewAgentPage( meta: { title: page.title }, body: `

${escape(page.title)}

${body.join("")}
`, htmlAttrs: ` data-theme="${escape(options.mode ?? system.defaultMode ?? "light")}"`, - extraHead: ``, + extraHead: ``, componentRecipeCss: theme.recipeCss, }); }); diff --git a/packages/design-studio/src/shell.ts b/packages/design-studio/src/shell.ts index 6986d80e..f97e43fa 100644 --- a/packages/design-studio/src/shell.ts +++ b/packages/design-studio/src/shell.ts @@ -23,6 +23,6 @@ ${guidanceHtml} .map(([id, system]) => ``) .join( "", - )}

Previews use isolated frames. Invalid drafts keep the last valid design. Gallery shows default component states; provider regions retain their own styling.

Current draft

`; + )}

Previews use isolated frames. Invalid drafts keep the last valid design. Gallery uses populated interactive samples. Forms and uploads do not send data.

Loading preview…

Current draft

`; export const studioCss = `${guidanceCss}:root{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;color:#171719;background:#fff;line-height:1.45;font-size:14px;--accent:#002fa7}*{box-sizing:border-box}body{margin:0}header{display:flex;align-items:center;gap:10px;min-height:72px;padding:16px 24px;border-bottom:1px solid #d9d9de;position:sticky;top:0;background:white;z-index:2}.brand{text-decoration:none;color:inherit;font-size:17px;letter-spacing:-.5px;margin-right:auto}.brand strong{margin-left:8px}#save-state{font-size:12px;color:#555;max-width:230px}button,input,select,textarea{font:inherit}button{padding:9px 12px;border:1px solid #c8c8d0;background:#fff;color:#171719;cursor:pointer;border-radius:0}button:hover{border-color:var(--accent);color:var(--accent)}button.primary{background:var(--accent);color:white;border-color:var(--accent)}button:disabled{opacity:.45;cursor:default}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,a:focus-visible,summary:focus-visible{outline:2px solid var(--accent);outline-offset:3px}main{display:grid;grid-template-columns:235px minmax(340px,.9fr) minmax(420px,1.1fr);min-height:calc(100vh - 72px)}aside{border-right:1px solid #d9d9de;padding:28px 20px}.eyebrow{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:#666;margin:0 0 12px}h1{font-size:31px;line-height:1.08;letter-spacing:-1.4px;font-weight:500;margin:0 0 16px}h2{font-size:23px;line-height:1.2;letter-spacing:-.7px;font-weight:500;margin:0}h3{font-weight:500}.intro,.help,.footnote{color:#63636d;font-size:12px}.intro{margin-bottom:28px;max-width:28em}.footnote{margin-top:32px}label{display:block;font-size:12px;margin:12px 0;color:#3d3d45}input,select,textarea{display:block;width:100%;margin-top:6px;padding:9px;border:1px solid #c8c8d0;border-radius:0;background:white;color:#171719;min-width:0}textarea{font-family:Consolas,monospace;font-size:12px;tab-size:2;resize:vertical}nav{display:grid;margin:28px -20px 0}nav button{text-align:left;border:0;border-top:1px solid #e2e2e7;padding:15px 20px}nav button[aria-current=page]{border-left:3px solid var(--accent);padding-left:17px;color:var(--accent);background:#f7f7f8}.workspace{min-width:0;padding:28px 24px;border-right:1px solid #d9d9de}.workspace-title,.preview-heading{display:flex;justify-content:space-between;gap:12px;align-items:start;margin-bottom:24px}.badge{font-size:11px;border:1px solid #c8c8d0;padding:5px 8px;white-space:nowrap}.row{display:flex;gap:10px;align-items:end;flex-wrap:wrap}.row>label{flex:1;min-width:100px}.row>button{margin-bottom:12px}.token-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0 18px}.token{border-top:1px solid #e2e2e7;padding:10px 0;min-width:0}.token label{margin:0}.token small{font-size:11px;display:block;overflow-wrap:anywhere;color:#686870;margin-top:7px}.token .token-name{font-weight:600;font-size:12px}.token-actions{display:flex;gap:8px}.token-actions input{flex:1}.swatch{width:18px;height:18px;border:1px solid #999;display:inline-block;vertical-align:middle;margin-right:6px}.preview{min-width:0;padding:28px 24px;background:#f7f7f8;align-self:start;position:sticky;top:72px;max-height:calc(100vh - 72px);overflow:auto}.preview-controls{display:flex;flex-wrap:wrap;gap:10px}.preview-controls label{flex:1;min-width:100px;margin-top:0}.frames{display:grid;gap:16px}.frames:has(#comparison:not([hidden])){grid-template-columns:1fr 1fr}.frames>div{min-width:0}.frame-scroll{overflow:auto;background:white;border:1px solid #d9d9de}iframe{border:0;width:100%;height:680px;display:block;background:white}.frames p{font-size:12px}details{border-top:1px solid #d9d9de;padding-top:16px;margin-top:24px}summary{cursor:pointer}pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;background:#f7f7f8;padding:12px;max-height:350px;overflow:auto}#diagnostics:not(:empty){border-left:3px solid #b72032;padding:8px 12px;margin-bottom:20px;color:#8c1526;font-size:12px}#message:not(:empty){position:fixed;bottom:16px;left:24px;max-width:min(650px,calc(100vw - 48px));padding:14px 20px;background:#171719;color:white;z-index:4;white-space:pre-wrap}#contract{font-size:12px}.editor[hidden],[hidden]{display:none!important}#revisions li{margin-bottom:10px}#revisions button{text-align:left;width:100%}@media(min-width:1600px){main{grid-template-columns:250px minmax(470px,.8fr) minmax(600px,1.2fr)}}@media(max-width:1150px){main{grid-template-columns:210px minmax(0,1fr)}.preview{grid-column:2;position:static;max-height:none}.workspace{border-right:0}aside{grid-row:span 2}}@media(max-width:650px){header{flex-wrap:wrap;padding:12px}.brand{width:100%}#save-state{margin-right:auto}main{display:block}aside{border-right:0;padding:24px}h1 br{display:none}nav{display:flex;overflow:auto;margin:24px -24px 0}nav button{white-space:nowrap;padding:12px}.workspace,.preview{padding:24px}aside .intro,aside .footnote{display:none}.frames:has(#comparison:not([hidden])){grid-template-columns:1fr}.token-grid{grid-template-columns:1fr}iframe{height:600px}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important}}`; diff --git a/packages/design-studio/test/gallery.test.ts b/packages/design-studio/test/gallery.test.ts new file mode 100644 index 00000000..7fe40e79 --- /dev/null +++ b/packages/design-studio/test/gallery.test.ts @@ -0,0 +1,65 @@ +import { expect, test } from "bun:test"; +import { COMPONENT_CONTRACTS, CORE_DESIGN_SYSTEMS } from "@wrnexus/design-system"; +import { runWithRequestContext } from "@wrnexus/core"; +import { gallerySample, previewSlots, previewSampleMarkup } from "../src/gallery.ts"; +import { previewIconCss } from "../src/preview-assets.ts"; +import { previewDocument } from "../src/preview.ts"; + +test("every contracted gallery component has an explicit populated standalone sample", () => { + for (const name of Object.keys(COMPONENT_CONTRACTS)) { + const sample = gallerySample(name); + expect(Object.keys(sample.props).length).toBeGreaterThan(0); + expect(sample.hint).toBeTruthy(); + expect(JSON.stringify(sample.props)).not.toContain("/showcase-images/"); + } + for (const name of ["Accordion", "DataMap", "Tabs", "TreeView", "FeatureGrid", "MetricGrid"]) { + expect((gallerySample(name).props.items as unknown[]).length).toBeGreaterThan(0); + } + expect(gallerySample("DrawflowCanvas").props.nodes).toHaveLength(2); + expect(gallerySample("Map").props.imageSrc).toMatch(/^data:image\/svg\+xml,/); + expect(gallerySample("DeviceFrame").content).toContain("Sample mobile page"); + expect(gallerySample("BackToTop").props.alwaysVisible).toBe(true); +}); + +test("sample slot insertion preserves nested markup and fallback labels", () => { + const html = + 'Fallback'; + const result = previewSlots( + html, + '

Body

', + ); + expect(result).toContain("
"); + expect(result).toContain("

Body

"); + expect(result).toContain('name="footer">Fallback'); + expect(previewSlots('Save', "")).toContain( + "Save", + ); + expect(() => + runWithRequestContext({} as never, () => previewSampleMarkup("

Sample

", "")), + ).not.toThrow(); +}); + +test("icons embed real local SVG masks, including upstream icon aliases", () => { + const css = previewIconCss("icon-[lucide--code-2] icon-[lucide--check-circle-2]"); + expect(css).toContain("data:image/svg+xml,"); + expect(css).toContain("stroke"); + expect(css).toContain("icon-[lucide--code-2]"); + expect(css).not.toMatch(/https?:\/\/[^"\s]*\.svg/); +}); + +test("all gallery pages include real browser functions and fully rendered nested components", async () => { + let count = 0; + for (let page = 0; page < 7; page++) { + const html = await previewDocument(CORE_DESIGN_SYSTEMS.square, { content: "catalog", page }); + count += [...html.matchAll(/
]*\bdata-component=/); + expect(html).toContain("data:image/svg+xml,"); + if (page === 0) expect(html).toContain("Supporting content belongs inside the default slot."); + if (page === 2) expect(html).toContain("Sample row 12"); + if (page === 5) expect(html).toContain("data-preview-toast"); + } + expect(count).toBe(104); +}, 20_000); diff --git a/packages/design-studio/test/server.test.ts b/packages/design-studio/test/server.test.ts index 5df1eef0..4c5d0c9d 100644 --- a/packages/design-studio/test/server.test.ts +++ b/packages/design-studio/test/server.test.ts @@ -168,7 +168,7 @@ test("catalog previews cover all 104 component defaults and unknown routes do no }); const { url } = await result.json(); const html = await (await send(url)).text(); - count += (html.match(/data-wrn-component=/g) ?? []).length; + count += (html.match(/
{ ); } }); + +test("switch track stays a pill when control corners change", () => { + for (const original of Object.values(CORE_DESIGN_SYSTEMS)) { + const system = structuredClone(original); + system.tokens["shape.control"]!.value = "0"; + for (const mode of ["light", "dark"]) { + const css = compileDesignSystem(system, { mode }).recipeCss; + expect(css).toMatch(/\[data-wrn-owner="Switch"\][^{]*\{[^}]*border-radius:999px/); + } + } +}); test("contracts reject unknown components, parts, axes, states, versions and executable style values", () => { for (const recipes of [ { Unknown: { contractVersion: 1 } }, diff --git a/packages/ui/components/DeviceFrame.wrn b/packages/ui/components/DeviceFrame.wrn index 29550836..a17b50ad 100644 --- a/packages/ui/components/DeviceFrame.wrn +++ b/packages/ui/components/DeviceFrame.wrn @@ -73,7 +73,7 @@ component DeviceFrame { border: 0.65rem solid var(--wrn-color-text); border-radius: 2rem; background: var(--wrn-color-background); - transition: width var(--wrn-motion-normal) var(--wrn-ease-standard); + transition: width var(--wrn-motion-base) var(--wrn-ease-standard); } .wrn-next--device-frame[data-device="tablet"] .wrn-next__device-shell { diff --git a/packages/ui/components/switch.wrn b/packages/ui/components/switch.wrn index d0d57653..28c1e93c 100644 --- a/packages/ui/components/switch.wrn +++ b/packages/ui/components/switch.wrn @@ -52,7 +52,8 @@ component Switch { height: 1.375rem; margin: 0; border: 1px solid color-mix(in srgb, var(--wrn-color-border-strong, var(--wrn-color-border)) 70%, transparent); - border-radius: 999px; + /* A switch track keeps its semantic pill geometry, including older themes. */ + border-radius: 999px !important; background: var(--wrn-color-surface-muted, var(--wrn-color-surface-2)); box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1); cursor: pointer; diff --git a/packages/ui/package.json b/packages/ui/package.json index cb6e434b..cf935534 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.8.118", + "version": "0.8.120", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/src/chart-config.ts b/packages/ui/src/chart-config.ts index 17b41c9a..0d205213 100644 --- a/packages/ui/src/chart-config.ts +++ b/packages/ui/src/chart-config.ts @@ -1,5 +1,22 @@ import type { ChartConfiguration, ChartData, ChartType } from "chart.js"; +/** Shorten only the painted tick. Dataset labels remain intact for tooltips and tables. */ +export function chartTickLabel(label: unknown, limit: number): string { + const text = String(label ?? ""); + if (text.length <= limit) return text; + const start = Math.ceil((limit - 1) / 2); + return text.slice(0, start) + "…" + text.slice(-(limit - 1 - start)); +} +export function chartTooltipLines(label: unknown, limit: number): string[] { + const lines: string[] = []; + for (const part of String(label ?? "").split("\n")) { + if (!part.length) lines.push(""); + for (let index = 0; index < part.length; index += limit) + lines.push(part.slice(index, index + limit)); + } + return lines; +} + export interface ChartProps { title?: string; items?: unknown[]; diff --git a/packages/ui/src/chart-runtime.ts b/packages/ui/src/chart-runtime.ts index ea5a1fba..00d8dd32 100644 --- a/packages/ui/src/chart-runtime.ts +++ b/packages/ui/src/chart-runtime.ts @@ -1,6 +1,12 @@ import Chart from "chart.js/auto"; import type { ChartConfiguration, Plugin } from "chart.js"; -import { chartConfiguration, mergeOptions, type ChartProps } from "./chart-config"; +import { + chartConfiguration, + mergeOptions, + chartTickLabel, + chartTooltipLines, + type ChartProps, +} from "./chart-config"; const selector = '[data-wrnexus-runtime~="chartjs"][data-chart-config]'; const mounted = new Map< @@ -95,6 +101,36 @@ function configuration(root: HTMLElement, props: ChartProps): ChartConfiguration }, config.options, ); + if (["bar", "line"].includes(config.type)) { + const axis = config.options!.indexAxis === "y" ? "y" : "x"; + const interaction = config.options!.interaction; + if (interaction?.mode === "index" && !interaction.axis) interaction.axis = axis; + const scale = ((config.options!.scales ??= {})[axis] ??= {}); + if (!scale.type || scale.type === "category") { + const ticks = (scale.ticks ??= {}); + if (!ticks.callback) + ticks.callback = function (value) { + return chartTickLabel( + this.getLabelForValue(Number(value)), + this.chart.width < 480 ? 18 : 36, + ); + }; + } + } + const tooltip = config.options!.plugins && config.options!.plugins.tooltip; + if ((tooltip as unknown) !== false && (config.options!.plugins as unknown) !== false) { + const plugin = (config.options!.plugins ??= {}); + const options = (plugin.tooltip ??= {}); + const callbacks = (options.callbacks ??= {}); + if (!callbacks.title) + callbacks.title = (items) => + items.length + ? chartTooltipLines( + items[0].label, + Math.max(12, Math.min(80, Math.floor((items[0].chart.width - 32) / 8))), + ) + : []; + } config.options!.onClick = (event, elements, chart) => { if (!elements.length) return; const { index, datasetIndex } = elements[0]; diff --git a/packages/ui/test/chart.test.ts b/packages/ui/test/chart.test.ts index 3acb8607..6c956d9c 100644 --- a/packages/ui/test/chart.test.ts +++ b/packages/ui/test/chart.test.ts @@ -1,11 +1,25 @@ import { expect, test } from "bun:test"; import { readFileSync } from "node:fs"; -import { chartConfiguration, chartTypes } from "../src/chart-config"; +import { + chartConfiguration, + chartTypes, + chartTickLabel, + chartTooltipLines, +} from "../src/chart-config"; import { renderComponent, mountHtml } from "../../test/src/index"; import { discoverPlugins, createPluginRunner } from "../../plugin/src/index"; import { resolve } from "node:path"; const source = readFileSync(new URL("../components/Chart.wrn", import.meta.url), "utf8"); +test("compact category ticks preserve the complete tooltip text", () => { + const label = "2026-10-17T10:03:00 · completed · audit-four"; + expect(chartTickLabel(label, 18)).toHaveLength(18); + expect(chartTickLabel(label, 18)).toContain("…"); + expect(chartTickLabel("Short", 18)).toBe("Short"); + const lines = chartTooltipLines(label, 18); + expect(lines.every((line) => line.length <= 18)).toBe(true); + expect(lines.join("")).toBe(label); +}); for (const type of chartTypes) { test(`Chart.js ${type} preserves native datasets`, () => { const data = {