generator-white-label 9.0.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CLI.md +13 -17
  2. package/EXISTING_APPLICATIONS.md +155 -0
  3. package/PACKAGE_MANAGERS.md +6 -4
  4. package/README.md +135 -153
  5. package/app/404.ts +28 -0
  6. package/app/README.md +19 -7
  7. package/{scaffold/no-jsx/app → app}/assets/script/index.ts +1 -0
  8. package/{scaffold/no-jsx/app → app}/assets/script/tasks/TaskView.ts +1 -1
  9. package/app/assets/view/CodeBlock.ts +15 -0
  10. package/app/assets/view/examples/tasks/TaskExample.ts +50 -0
  11. package/app/assets/view/examples/tasks/task-state.ts +1 -1
  12. package/app/assets/view/layout/SiteFooter.ts +11 -0
  13. package/app/assets/view/layout/SiteHeader.ts +17 -0
  14. package/app/assets/view/page-data.ts +2 -2
  15. package/app/assets/view/sections/GettingStartedSection.ts +26 -0
  16. package/app/assets/view/sections/HeroSection.ts +15 -0
  17. package/app/assets/view/sections/LiveExampleSection.ts +24 -0
  18. package/app/assets/view/sections/PackageDocsSection.ts +69 -0
  19. package/app/assets/view/sections/{SourceGuideSection.tsx → SourceGuideSection.ts} +11 -16
  20. package/app/index.ts +50 -0
  21. package/cli/index.ts +4 -41
  22. package/dist/app/404.d.ts +1 -1
  23. package/dist/app/404.js +18 -2
  24. package/dist/app/404.js.map +1 -1
  25. package/dist/app/assets/script/index.d.ts +1 -6
  26. package/dist/app/assets/script/index.js +1 -6
  27. package/dist/app/assets/script/index.js.map +1 -1
  28. package/dist/app/assets/script/tasks/TaskView.d.ts +1 -6
  29. package/dist/app/assets/script/tasks/TaskView.js +2 -8
  30. package/dist/app/assets/script/tasks/TaskView.js.map +1 -1
  31. package/dist/app/assets/view/CodeBlock.d.ts +3 -3
  32. package/dist/app/assets/view/CodeBlock.js +4 -2
  33. package/dist/app/assets/view/CodeBlock.js.map +1 -1
  34. package/dist/app/assets/view/examples/tasks/TaskExample.d.ts +2 -8
  35. package/dist/app/assets/view/examples/tasks/TaskExample.js +41 -9
  36. package/dist/app/assets/view/examples/tasks/TaskExample.js.map +1 -1
  37. package/dist/app/assets/view/examples/tasks/task-state.d.ts +1 -1
  38. package/dist/app/assets/view/examples/tasks/task-state.js.map +1 -1
  39. package/dist/app/assets/view/layout/SiteFooter.d.ts +2 -2
  40. package/dist/app/assets/view/layout/SiteFooter.js +8 -3
  41. package/dist/app/assets/view/layout/SiteFooter.js.map +1 -1
  42. package/dist/app/assets/view/layout/SiteHeader.d.ts +2 -8
  43. package/dist/app/assets/view/layout/SiteHeader.js +14 -9
  44. package/dist/app/assets/view/layout/SiteHeader.js.map +1 -1
  45. package/dist/app/assets/view/page-data.d.ts +2 -2
  46. package/dist/app/assets/view/page-data.js.map +1 -1
  47. package/dist/app/assets/view/sections/GettingStartedSection.d.ts +1 -1
  48. package/dist/app/assets/view/sections/GettingStartedSection.js +21 -6
  49. package/dist/app/assets/view/sections/GettingStartedSection.js.map +1 -1
  50. package/dist/app/assets/view/sections/HeroSection.d.ts +2 -8
  51. package/dist/app/assets/view/sections/HeroSection.js +12 -9
  52. package/dist/app/assets/view/sections/HeroSection.js.map +1 -1
  53. package/dist/app/assets/view/sections/LiveExampleSection.d.ts +2 -6
  54. package/dist/app/assets/view/sections/LiveExampleSection.js +17 -7
  55. package/dist/app/assets/view/sections/LiveExampleSection.js.map +1 -1
  56. package/dist/app/assets/view/sections/PackageDocsSection.d.ts +1 -1
  57. package/dist/app/assets/view/sections/PackageDocsSection.js +57 -20
  58. package/dist/app/assets/view/sections/PackageDocsSection.js.map +1 -1
  59. package/dist/app/assets/view/sections/SourceGuideSection.d.ts +2 -7
  60. package/dist/app/assets/view/sections/SourceGuideSection.js +20 -8
  61. package/dist/app/assets/view/sections/SourceGuideSection.js.map +1 -1
  62. package/dist/app/index.d.ts +2 -8
  63. package/dist/app/index.js +36 -11
  64. package/dist/app/index.js.map +1 -1
  65. package/dist/cli/index.d.ts +0 -3
  66. package/dist/cli/index.js +4 -36
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/scaffold/index.d.ts +2 -13
  69. package/dist/scaffold/index.js +17 -53
  70. package/dist/scaffold/index.js.map +1 -1
  71. package/dist/scripts/build.js +97 -28
  72. package/dist/scripts/build.js.map +1 -1
  73. package/dist/server/handler.js +2 -9
  74. package/dist/server/handler.js.map +1 -1
  75. package/package.json +22 -22
  76. package/pnpm-workspace.yaml +0 -1
  77. package/scaffold/AGENTS.md +13 -8
  78. package/scaffold/index.ts +17 -57
  79. package/scripts/build.ts +90 -30
  80. package/server/handler.ts +2 -10
  81. package/tsconfig.site.json +1 -4
  82. package/app/404.tsx +0 -28
  83. package/app/assets/script/index.tsx +0 -13
  84. package/app/assets/script/tasks/TaskView.tsx +0 -24
  85. package/app/assets/view/CodeBlock.tsx +0 -17
  86. package/app/assets/view/examples/tasks/TaskExample.tsx +0 -63
  87. package/app/assets/view/layout/SiteFooter.tsx +0 -11
  88. package/app/assets/view/layout/SiteHeader.tsx +0 -23
  89. package/app/assets/view/sections/GettingStartedSection.tsx +0 -27
  90. package/app/assets/view/sections/HeroSection.tsx +0 -21
  91. package/app/assets/view/sections/LiveExampleSection.tsx +0 -29
  92. package/app/assets/view/sections/PackageDocsSection.tsx +0 -69
  93. package/app/index.tsx +0 -54
  94. package/scaffold/no-jsx/README.md +0 -73
  95. package/scaffold/no-jsx/app/404.ts +0 -10
  96. package/scaffold/no-jsx/app/assets/view/examples/tasks/TaskExample.ts +0 -39
  97. package/scaffold/no-jsx/app/index.ts +0 -58
  98. package/tsconfig.site.no-jsx.json +0 -24
@@ -0,0 +1,26 @@
1
+ import {html} from 'white-label-view/html';
2
+ import CodeBlock, {syntax} from '../CodeBlock.js';
3
+
4
+ /** Keep the shortest path from generator install to a verified local build visible. */
5
+ export default function GettingStartedSection() {
6
+ return html`<section class="section" id="start" aria-labelledby="start-title">
7
+ <div class="container start-grid">
8
+ <div class="section-intro">
9
+ <p class="eyebrow">Get started</p>
10
+ <h2 id="start-title">Create a project with the generator.</h2>
11
+ <p>The generated project is static-first, TypeScript-first, accessible by default, and ready for application-specific code.</p>
12
+ </div>
13
+ <div>
14
+ <p class="code-label">Create a project</p>
15
+ ${CodeBlock({lines: [html`<span class="${syntax.type}">npx</span> generator-white-label`]})}
16
+ <p class="code-label">Then develop</p>
17
+ ${CodeBlock({lines: [
18
+ html`<span class="${syntax.type}">npm</span> ci`,
19
+ html`<span class="${syntax.type}">npm</span> test`,
20
+ html`<span class="${syntax.type}">npm</span> run build -- --version=<span class="${syntax.value}">local</span>`
21
+ ]})}
22
+ <p><a href="https://github.com/bshack/white-label#readme">Read the complete generator documentation</a></p>
23
+ </div>
24
+ </div>
25
+ </section>`;
26
+ }
@@ -0,0 +1,15 @@
1
+ import {html} from 'white-label-view/html';
2
+
3
+ /** Static presentation stays a plain tagged-template function; View is only needed for lifecycle/state. */
4
+ export default function HeroSection() {
5
+ return html`<section class="hero" id="top" aria-labelledby="hero-title">
6
+ <div class="container hero__inner">
7
+ <h1 id="hero-title">Small pieces.<br>Complete applications.</h1>
8
+ <p class="hero__lede">Framework-independent TypeScript packages for state, rendering, coordination, routing, and project generation.</p>
9
+ <div class="hero__links">
10
+ <a class="primary-link" href="#example">See the example</a>
11
+ <a href="#start">Install the generator</a>
12
+ </div>
13
+ </div>
14
+ </section>`;
15
+ }
@@ -0,0 +1,24 @@
1
+ import {html} from 'white-label-view/html';
2
+ import TaskExample from '../examples/tasks/TaskExample.js';
3
+ import {createInitialTaskState, describeTaskStatus} from '../examples/tasks/task-state.js';
4
+
5
+ /** Render useful initial HTML with the same tagged template reused by Browser View. */
6
+ export default function LiveExampleSection() {
7
+ const state = createInitialTaskState();
8
+
9
+ return html`<section class="section" id="example" aria-labelledby="example-title">
10
+ <div class="container">
11
+ <div class="section-intro">
12
+ <p class="eyebrow">Live example</p>
13
+ <h2 id="example-title">Four packages. One small application.</h2>
14
+ <p>Add a task, toggle its state, or change the filter. Router translates the URL into intent, Mediator coordinates actions, Model owns state, and View keeps the interface in sync.</p>
15
+ </div>
16
+ <div class="demo" data-task-example>
17
+ ${TaskExample({state})}
18
+ <p class="visually-hidden" role="status" aria-live="polite" aria-atomic="true" data-task-status>
19
+ ${describeTaskStatus(state)}
20
+ </p>
21
+ </div>
22
+ </div>
23
+ </section>`;
24
+ }
@@ -0,0 +1,69 @@
1
+ import {html} from 'white-label-view/html';
2
+ import CodeBlock, {syntax} from '../CodeBlock.js';
3
+
4
+ const repositories = {
5
+ mediator: 'https://github.com/bshack/white-label-mediator',
6
+ model: 'https://github.com/bshack/white-label-model',
7
+ router: 'https://github.com/bshack/white-label-router',
8
+ view: 'https://github.com/bshack/white-label-view'
9
+ };
10
+
11
+ /** Package documentation stays close to the example it describes. */
12
+ export default function PackageDocsSection() {
13
+ return html`<section class="section" id="packages" aria-labelledby="packages-title">
14
+ <div class="container">
15
+ <div class="section-intro">
16
+ <p class="eyebrow">Documentation</p>
17
+ <h2 id="packages-title">The core flow stays explicit.</h2>
18
+ <p>Each package has one job, can be used independently, and stays easy to replace or test because application concerns are not hidden behind a framework.</p>
19
+ </div>
20
+ <div class="architecture"><ol>
21
+ <li><strong>Router</strong><span>turns a URL into application intent</span></li>
22
+ <li><strong>Mediator</strong><span>coordinates that intent between modules</span></li>
23
+ <li><strong>Model</strong><span>stores and publishes application state</span></li>
24
+ <li><strong>View</strong><span>renders the resulting interface</span></li>
25
+ </ol></div>
26
+ <div class="docs-grid">
27
+ <article>
28
+ <p class="eyebrow">Model</p><h3>State is observable, not magical.</h3>
29
+ ${CodeBlock({lines: [
30
+ html`<span class="${syntax.keyword}">const</span> model = <span class="${syntax.keyword}">new</span> <span class="${syntax.type}">Model</span>({count: 0});`,
31
+ html`model.addEventListener(<span class="${syntax.value}">'change'</span>, event =&gt; render(event.detail));`,
32
+ html`model.update({count: 1});`
33
+ ]})}
34
+ <p><a href="${repositories.model}">Model documentation</a></p>
35
+ </article>
36
+ <article>
37
+ <p class="eyebrow">View</p><h3>Tagged HTML is the first-party template syntax.</h3>
38
+ ${CodeBlock({lines: [
39
+ html`<span class="${syntax.keyword}">import</span> {html} from <span class="${syntax.value}">'white-label-view/html'</span>;`,
40
+ html`<span class="${syntax.keyword}">const</span> view = <span class="${syntax.keyword}">new</span> <span class="${syntax.type}">View</span>({`,
41
+ html` model,`,
42
+ html` template: state =&gt; html\`${'<p>'}\${state.count}${'</p>'}\``,
43
+ html`}).initialize();`
44
+ ]})}
45
+ <p><a href="${repositories.view}">View documentation</a></p>
46
+ </article>
47
+ <article>
48
+ <p class="eyebrow">Mediator</p><h3>Modules communicate through events.</h3>
49
+ ${CodeBlock({lines: [
50
+ html`mediator.addEventListener(<span class="${syntax.value}">'counter:increment'</span>, increment);`,
51
+ html`mediator.dispatchEvent(<span class="${syntax.keyword}">new</span> <span class="${syntax.type}">CustomEvent</span>(<span class="${syntax.value}">'counter:increment'</span>));`
52
+ ]})}
53
+ <p><a href="${repositories.mediator}">Mediator documentation</a></p>
54
+ </article>
55
+ <article>
56
+ <p class="eyebrow">Router</p><h3>Routes describe intent.</h3>
57
+ ${CodeBlock({lines: [
58
+ html`router.routes = {`,
59
+ html` <span class="${syntax.value}">'/'</span>: (_scope, location) =&gt; {`,
60
+ html` mediator.dispatchEvent(<span class="${syntax.keyword}">new</span> <span class="${syntax.type}">CustomEvent</span>(<span class="${syntax.value}">'filter:set'</span>, {detail: location.data.query.filter}));`,
61
+ html` }`,
62
+ html`};`
63
+ ]})}
64
+ <p><a href="${repositories.router}">Router documentation</a></p>
65
+ </article>
66
+ </div>
67
+ </div>
68
+ </section>`;
69
+ }
@@ -1,28 +1,23 @@
1
- /**
2
- * Point learners at the implementation itself.
3
- *
4
- * The landing page is intentionally organized so each path answers a different
5
- * question: composition, rendering, state, coordination, routing, or testing.
6
- */
1
+ import {html} from 'white-label-view/html';
2
+
3
+ /** Point learners at the implementation paths that demonstrate each responsibility. */
7
4
  export default function SourceGuideSection() {
8
- return (
9
- <section className="section" id="source" aria-labelledby="source-title">
10
- <div className="container start-grid">
11
- <div className="section-intro">
12
- <p className="eyebrow">Read the source</p>
5
+ return html`<section class="section" id="source" aria-labelledby="source-title">
6
+ <div class="container start-grid">
7
+ <div class="section-intro">
8
+ <p class="eyebrow">Read the source</p>
13
9
  <h2 id="source-title">This site is part of the documentation.</h2>
14
10
  <p>The landing page is built with the same White Label packages it teaches. Start at the page composition, then follow the imports into the interactive example.</p>
15
11
  </div>
16
- <div className="source-paths">
12
+ <div class="source-paths">
17
13
  <dl>
18
- <div><dt>Page composition</dt><dd><code>app/index.tsx</code></dd></div>
19
- <div><dt>Static JSX views</dt><dd><code>app/assets/view/</code></dd></div>
14
+ <div><dt>Page composition</dt><dd><code>app/index.ts</code></dd></div>
15
+ <div><dt>Tagged HTML views</dt><dd><code>app/assets/view/</code></dd></div>
20
16
  <div><dt>Task application</dt><dd><code>app/assets/script/tasks/</code></dd></div>
21
17
  <div><dt>Integration tests</dt><dd><code>test/app.test.js</code></dd></div>
22
18
  </dl>
23
19
  <p><a href="https://github.com/bshack/white-label/tree/master/app">Browse the application source</a></p>
24
20
  </div>
25
21
  </div>
26
- </section>
27
- );
22
+ </section>`;
28
23
  }
package/app/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ import {html, unsafeHTML} from 'white-label-view/html';
2
+ import SiteFooter from './assets/view/layout/SiteFooter.js';
3
+ import SiteHeader from './assets/view/layout/SiteHeader.js';
4
+ import type {PageData} from './assets/view/page-data.js';
5
+ import GettingStartedSection from './assets/view/sections/GettingStartedSection.js';
6
+ import HeroSection from './assets/view/sections/HeroSection.js';
7
+ import LiveExampleSection from './assets/view/sections/LiveExampleSection.js';
8
+ import PackageDocsSection from './assets/view/sections/PackageDocsSection.js';
9
+ import SourceGuideSection from './assets/view/sections/SourceGuideSection.js';
10
+
11
+ function structuredDataScript(value: unknown) {
12
+ const json = JSON.stringify(value).replaceAll('<', '\\u003c');
13
+ return unsafeHTML(`<script type="application/ld+json">${json}</script>`);
14
+ }
15
+
16
+ /** Compose the static page from small tagged-template functions. */
17
+ export default function IndexPage(data: Record<string, unknown>) {
18
+ const page = data as unknown as PageData;
19
+
20
+ return html`<html id="index" lang="en-US" dir="ltr">
21
+ <head>
22
+ <meta charset="utf-8">
23
+ <meta name="viewport" content="width=device-width, initial-scale=1">
24
+ <title>${page.title}</title>
25
+ <meta name="description" content="${page.meta.description}">
26
+ <meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1">
27
+ <link rel="canonical" href="${page.siteUrl}/">
28
+ <meta property="og:title" content="${page.title}">
29
+ <meta property="og:description" content="${page.meta.description}">
30
+ <meta property="og:type" content="website">
31
+ <meta property="og:url" content="${page.siteUrl}/">
32
+ <link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/global.css" media="all">
33
+ <link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/print.css" media="print">
34
+ ${structuredDataScript(page.structuredData)}
35
+ </head>
36
+ <body itemscope itemtype="https://schema.org/WebPage">
37
+ <a class="skip-link" href="#main">Skip to content</a>
38
+ ${SiteHeader()}
39
+ <main id="main" tabindex="-1">
40
+ ${HeroSection()}
41
+ ${LiveExampleSection()}
42
+ ${PackageDocsSection()}
43
+ ${SourceGuideSection()}
44
+ ${GettingStartedSection()}
45
+ </main>
46
+ ${SiteFooter()}
47
+ <script src="${page.cdn}release/${page.version}/assets/script/index.compiled.js" defer></script>
48
+ </body>
49
+ </html>`;
50
+ }
package/cli/index.ts CHANGED
@@ -4,7 +4,6 @@ import {realpathSync} from 'node:fs';
4
4
  import {readdir} from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import process from 'node:process';
7
- import {createInterface} from 'node:readline/promises';
8
7
  import {fileURLToPath} from 'node:url';
9
8
  import {createProject} from '../scaffold/index.js';
10
9
 
@@ -13,22 +12,17 @@ export interface CliDependencies {
13
12
  createProject?: typeof createProject;
14
13
  stdout?: Pick<NodeJS.WriteStream, 'write'>;
15
14
  stderr?: Pick<NodeJS.WriteStream, 'write'>;
16
- input?: NodeJS.ReadableStream;
17
- output?: NodeJS.WritableStream;
18
- isInteractive?: boolean;
19
15
  }
20
16
 
21
17
  function usage() {
22
18
  return [
23
19
  'Usage:',
24
- ' white-label create <directory> [--jsx | --no-jsx]',
20
+ ' white-label create <directory>',
25
21
  '',
26
22
  'Commands:',
27
- ' create <directory> Create a White Label project.',
23
+ ' create <directory> Create a tagged-template White Label project.',
28
24
  '',
29
25
  'Options:',
30
- ' --jsx Generate TypeScript with JSX/TSX templates.',
31
- ' --no-jsx Generate plain TypeScript with HTML string templates.',
32
26
  ' -h, --help Show this help message.'
33
27
  ].join('\n');
34
28
  }
@@ -37,18 +31,6 @@ export function formatCliError(error: unknown) {
37
31
  return error instanceof Error ? error.message : String(error);
38
32
  }
39
33
 
40
- async function askForJsx(input: NodeJS.ReadableStream, output: NodeJS.WritableStream): Promise<boolean> {
41
- const readline = createInterface({input, output});
42
- try {
43
- const answer = await readline.question(
44
- 'Use JSX/TSX for page and view templates? Choose Yes for JSX syntax like <section>...</section>, or No for plain TypeScript that returns HTML strings. (Y/n) '
45
- );
46
- return !/^n(?:o)?$/i.test(answer.trim());
47
- } finally {
48
- readline.close();
49
- }
50
- }
51
-
52
34
  /** Reject an existing non-empty target so a mistyped CLI destination cannot overwrite project files. */
53
35
  async function assertDestinationAvailable(destination: string) {
54
36
  try {
@@ -67,8 +49,6 @@ export async function runCli(args: readonly string[], dependencies: CliDependenc
67
49
  const scaffold = dependencies.createProject ?? createProject;
68
50
  const stdout = dependencies.stdout ?? process.stdout;
69
51
  const stderr = dependencies.stderr ?? process.stderr;
70
- const input = dependencies.input ?? process.stdin;
71
- const output = dependencies.output ?? process.stdout;
72
52
  const [command, destination, ...options] = args;
73
53
 
74
54
  if (command === '--help' || command === '-h' || args.length === 0) {
@@ -76,31 +56,14 @@ export async function runCli(args: readonly string[], dependencies: CliDependenc
76
56
  return 0;
77
57
  }
78
58
 
79
- const allowedOptions = new Set(['--jsx', '--no-jsx']);
80
- if (
81
- command !== 'create' ||
82
- !destination ||
83
- options.some(option => !allowedOptions.has(option)) ||
84
- (options.includes('--jsx') && options.includes('--no-jsx'))
85
- ) {
59
+ if (command !== 'create' || !destination || options.length) {
86
60
  stderr.write(`${usage()}\n`);
87
61
  return 1;
88
62
  }
89
63
 
90
- let jsx: boolean;
91
- if (options.includes('--jsx')) {
92
- jsx = true;
93
- } else if (options.includes('--no-jsx')) {
94
- jsx = false;
95
- } else if (dependencies.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
96
- jsx = await askForJsx(input, output);
97
- } else {
98
- jsx = true;
99
- }
100
-
101
64
  const resolvedDestination = path.resolve(cwd(), destination);
102
65
  await assertDestinationAvailable(resolvedDestination);
103
- await scaffold({destination: resolvedDestination, jsx});
66
+ await scaffold({destination: resolvedDestination});
104
67
  stdout.write(`Created White Label project at ${resolvedDestination}\n`);
105
68
  return 0;
106
69
  }
package/dist/app/404.d.ts CHANGED
@@ -1 +1 @@
1
- export default function NotFoundPage(data: Record<string, unknown>): import("white-label-view/jsx-runtime").JSXMarkup;
1
+ export default function NotFoundPage(data: Record<string, unknown>): import("white-label-view/html").HTMLMarkup;
package/dist/app/404.js CHANGED
@@ -1,6 +1,22 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "white-label-view/jsx-runtime";
1
+ import { html } from 'white-label-view/html';
2
2
  export default function NotFoundPage(data) {
3
3
  const page = data;
4
- return (_jsxs("html", { lang: "en-US", dir: "ltr", children: [_jsxs("head", { children: [_jsx("meta", { charset: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("meta", { name: "robots", content: "noindex,follow" }), _jsx("title", { children: "Page not found | White Label" }), _jsx("link", { rel: "stylesheet", href: `${page.cdn}release/${page.version}/assets/style/global.css` })] }), _jsxs("body", { children: [_jsx("a", { className: "skip-link", href: "#main", children: "Skip to the message" }), _jsxs("main", { id: "main", tabindex: "-1", className: "mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8", children: [_jsx("h1", { className: "text-5xl font-extrabold tracking-tight", children: "Page not found" }), _jsx("p", { className: "mt-6 text-lg", children: "The requested page does not exist." }), _jsx("p", { className: "mt-6", children: _jsx("a", { className: "font-bold underline", href: page.www, children: "Return to the White Label starter" }) })] })] })] }));
4
+ return html `<html lang="en-US" dir="ltr">
5
+ <head>
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <meta name="robots" content="noindex,follow">
9
+ <title>Page not found | White Label</title>
10
+ <link rel="stylesheet" href="${page.cdn}release/${page.version}/assets/style/global.css">
11
+ </head>
12
+ <body>
13
+ <a class="skip-link" href="#main">Skip to the message</a>
14
+ <main id="main" tabindex="-1" class="mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8">
15
+ <h1 class="text-5xl font-extrabold tracking-tight">Page not found</h1>
16
+ <p class="mt-6 text-lg">The requested page does not exist.</p>
17
+ <p class="mt-6"><a class="font-bold underline" href="${page.www}">Return to the White Label starter</a></p>
18
+ </main>
19
+ </body>
20
+ </html>`;
5
21
  }
6
22
  //# sourceMappingURL=404.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"404.js","sourceRoot":"","sources":["../../app/404.tsx"],"names":[],"mappings":";AAMA,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,IAA6B;IAC9D,MAAM,IAAI,GAAG,IAA2B,CAAC;IACzC,OAAO,CACH,gBAAM,IAAI,EAAC,OAAO,EAAC,GAAG,EAAC,KAAK,aACxB,2BACI,eAAM,OAAO,EAAC,OAAO,GAAG,EACxB,eAAM,IAAI,EAAC,UAAU,EAAC,OAAO,EAAC,qCAAqC,GAAG,EACtE,eAAM,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAC,gBAAgB,GAAG,EAC/C,2DAA2C,EAC3C,eAAM,GAAG,EAAC,YAAY,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO,0BAA0B,GAAI,IAC1F,EACP,2BACI,YAAG,SAAS,EAAC,WAAW,EAAC,IAAI,EAAC,OAAO,oCAAwB,EAC7D,gBAAM,EAAE,EAAC,MAAM,EAAC,QAAQ,EAAC,IAAI,EAAC,SAAS,EAAC,8CAA8C,aAClF,aAAI,SAAS,EAAC,wCAAwC,+BAAoB,EAC1E,YAAG,SAAS,EAAC,cAAc,mDAAuC,EAClE,YAAG,SAAS,EAAC,MAAM,YAAC,YAAG,SAAS,EAAC,qBAAqB,EAAC,IAAI,EAAE,IAAI,CAAC,GAAG,kDAAuC,GAAI,IAC7G,IACJ,IACJ,CACV,CAAC;AACN,CAAC","sourcesContent":["interface PageData {\n cdn: string;\n version: string;\n www: string;\n}\n\nexport default function NotFoundPage(data: Record<string, unknown>) {\n const page = data as unknown as PageData;\n return (\n <html lang=\"en-US\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta name=\"robots\" content=\"noindex,follow\" />\n <title>Page not found | White Label</title>\n <link rel=\"stylesheet\" href={`${page.cdn}release/${page.version}/assets/style/global.css`} />\n </head>\n <body>\n <a className=\"skip-link\" href=\"#main\">Skip to the message</a>\n <main id=\"main\" tabindex=\"-1\" className=\"mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8\">\n <h1 className=\"text-5xl font-extrabold tracking-tight\">Page not found</h1>\n <p className=\"mt-6 text-lg\">The requested page does not exist.</p>\n <p className=\"mt-6\"><a className=\"font-bold underline\" href={page.www}>Return to the White Label starter</a></p>\n </main>\n </body>\n </html>\n );\n}\n"]}
1
+ {"version":3,"file":"404.js","sourceRoot":"","sources":["../../app/404.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,uBAAuB,CAAC;AAQ3C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,IAA6B;IAC9D,MAAM,IAAI,GAAG,IAA2B,CAAC;IACzC,OAAO,IAAI,CAAA;;;;;;+CAMgC,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,OAAO;;;;;;;2EAOH,IAAI,CAAC,GAAG;;;gBAGnE,CAAC;AACjB,CAAC","sourcesContent":["import {html} from 'white-label-view/html';\n\ninterface PageData {\n cdn: string;\n version: string;\n www: string;\n}\n\nexport default function NotFoundPage(data: Record<string, unknown>) {\n const page = data as unknown as PageData;\n return html`<html lang=\"en-US\" dir=\"ltr\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"robots\" content=\"noindex,follow\">\n <title>Page not found | White Label</title>\n <link rel=\"stylesheet\" href=\"${page.cdn}release/${page.version}/assets/style/global.css\">\n </head>\n <body>\n <a class=\"skip-link\" href=\"#main\">Skip to the message</a>\n <main id=\"main\" tabindex=\"-1\" class=\"mx-auto max-w-5xl px-4 py-24 sm:px-6 lg:px-8\">\n <h1 class=\"text-5xl font-extrabold tracking-tight\">Page not found</h1>\n <p class=\"mt-6 text-lg\">The requested page does not exist.</p>\n <p class=\"mt-6\"><a class=\"font-bold underline\" href=\"${page.www}\">Return to the White Label starter</a></p>\n </main>\n </body>\n </html>`;\n}\n"]}
@@ -1,8 +1,3 @@
1
- /**
2
- * Browser entry points should stay small.
3
- *
4
- * The task application owns its own composition and lifecycle, so the entry
5
- * module has one obvious responsibility: start the feature for this document.
6
- */
1
+ /** Start the feature composition for this document. */
7
2
  export { initializeTaskApplication } from './tasks/TaskApplication.js';
8
3
  export { normalizeTaskFilter } from './tasks/TaskRouter.js';
@@ -1,11 +1,6 @@
1
1
  /** @module app/assets/script/index */
2
2
  import { initializeTaskApplication } from './tasks/TaskApplication.js';
3
- /**
4
- * Browser entry points should stay small.
5
- *
6
- * The task application owns its own composition and lifecycle, so the entry
7
- * module has one obvious responsibility: start the feature for this document.
8
- */
3
+ /** Start the feature composition for this document. */
9
4
  export { initializeTaskApplication } from './tasks/TaskApplication.js';
10
5
  export { normalizeTaskFilter } from './tasks/TaskRouter.js';
11
6
  initializeTaskApplication(document);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../app/assets/script/index.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AAErE;;;;;GAKG;AACH,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAE1D,yBAAyB,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["/** @module app/assets/script/index */\nimport {initializeTaskApplication} from './tasks/TaskApplication.js';\n\n/**\n * Browser entry points should stay small.\n *\n * The task application owns its own composition and lifecycle, so the entry\n * module has one obvious responsibility: start the feature for this document.\n */\nexport {initializeTaskApplication} from './tasks/TaskApplication.js';\nexport {normalizeTaskFilter} from './tasks/TaskRouter.js';\n\ninitializeTaskApplication(document);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../app/assets/script/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AAErE,uDAAuD;AACvD,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAE1D,yBAAyB,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["/** @module app/assets/script/index */\nimport {initializeTaskApplication} from './tasks/TaskApplication.js';\n\n/** Start the feature composition for this document. */\nexport {initializeTaskApplication} from './tasks/TaskApplication.js';\nexport {normalizeTaskFilter} from './tasks/TaskRouter.js';\n\ninitializeTaskApplication(document);\n"]}
@@ -1,9 +1,4 @@
1
1
  import View from 'white-label-view';
2
2
  import type TaskModel from './TaskModel.js';
3
- /**
4
- * View owns rendering, but not application decisions.
5
- *
6
- * Passing the already-rendered task root lets View adopt useful static HTML
7
- * instead of replacing the document merely because JavaScript has started.
8
- */
3
+ /** Adopt the build-time task root and reuse the same tagged template for model updates. */
9
4
  export declare function createTaskView(parentElement: HTMLElement, model: TaskModel): View;
@@ -1,12 +1,6 @@
1
- import { jsx as _jsx } from "white-label-view/jsx-runtime";
2
1
  import View from 'white-label-view';
3
2
  import TaskExample from '../../view/examples/tasks/TaskExample.js';
4
- /**
5
- * View owns rendering, but not application decisions.
6
- *
7
- * Passing the already-rendered task root lets View adopt useful static HTML
8
- * instead of replacing the document merely because JavaScript has started.
9
- */
3
+ /** Adopt the build-time task root and reuse the same tagged template for model updates. */
10
4
  export function createTaskView(parentElement, model) {
11
5
  const element = parentElement.querySelector('[data-task-app]');
12
6
  if (!element) {
@@ -17,7 +11,7 @@ export function createTaskView(parentElement, model) {
17
11
  model,
18
12
  parentElement,
19
13
  template(data) {
20
- return _jsx(TaskExample, { state: data });
14
+ return TaskExample({ state: data });
21
15
  }
22
16
  }).initialize();
23
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TaskView.js","sourceRoot":"","sources":["../../../../../app/assets/script/tasks/TaskView.tsx"],"names":[],"mappings":";AAAA,OAAO,IAAI,MAAM,kBAAkB,CAAC;AACpC,OAAO,WAAW,MAAM,0CAA0C,CAAC;AAInE;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,aAA0B,EAAE,KAAgB;IACvE,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAc,iBAAiB,CAAC,CAAC;IAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IAAA,CAAC;IAEtF,OAAO,IAAI,IAAI,CAAC;QACZ,OAAO;QACP,KAAK;QACL,aAAa;QACb,QAAQ,CAAC,IAAI;YACT,OAAO,KAAC,WAAW,IAAC,KAAK,EAAE,IAAiB,GAAI,CAAC;QACrD,CAAC;KACJ,CAAC,CAAC,UAAU,EAAE,CAAC;AACpB,CAAC","sourcesContent":["import View from 'white-label-view';\nimport TaskExample from '../../view/examples/tasks/TaskExample.js';\nimport type {TaskState} from '../../view/examples/tasks/task-state.js';\nimport type TaskModel from './TaskModel.js';\n\n/**\n * View owns rendering, but not application decisions.\n *\n * Passing the already-rendered task root lets View adopt useful static HTML\n * instead of replacing the document merely because JavaScript has started.\n */\nexport function createTaskView(parentElement: HTMLElement, model: TaskModel): View {\n const element = parentElement.querySelector<HTMLElement>('[data-task-app]');\n if (!element) {throw new TypeError('Task example requires its static task-app root');}\n\n return new View({\n element,\n model,\n parentElement,\n template(data) {\n return <TaskExample state={data as TaskState} />;\n }\n }).initialize();\n}\n"]}
1
+ {"version":3,"file":"TaskView.js","sourceRoot":"","sources":["../../../../../app/assets/script/tasks/TaskView.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,kBAAkB,CAAC;AACpC,OAAO,WAAW,MAAM,0CAA0C,CAAC;AAInE,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,aAA0B,EAAE,KAAgB;IACvE,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAc,iBAAiB,CAAC,CAAC;IAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IAAA,CAAC;IAEtF,OAAO,IAAI,IAAI,CAAC;QACZ,OAAO;QACP,KAAK;QACL,aAAa;QACb,QAAQ,CAAC,IAAI;YACT,OAAO,WAAW,CAAC,EAAC,KAAK,EAAE,IAAiB,EAAC,CAAC,CAAC;QACnD,CAAC;KACJ,CAAC,CAAC,UAAU,EAAE,CAAC;AACpB,CAAC","sourcesContent":["import View from 'white-label-view';\nimport TaskExample from '../../view/examples/tasks/TaskExample.js';\nimport type {TaskState} from '../../view/examples/tasks/task-state.js';\nimport type TaskModel from './TaskModel.js';\n\n/** Adopt the build-time task root and reuse the same tagged template for model updates. */\nexport function createTaskView(parentElement: HTMLElement, model: TaskModel): View {\n const element = parentElement.querySelector<HTMLElement>('[data-task-app]');\n if (!element) {throw new TypeError('Task example requires its static task-app root');}\n\n return new View({\n element,\n model,\n parentElement,\n template(data) {\n return TaskExample({state: data as TaskState});\n }\n }).initialize();\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { JSX } from 'white-label-view/jsx-runtime';
1
+ import { type HTMLMarkup } from 'white-label-view/html';
2
2
  export declare const syntax: {
3
3
  keyword: string;
4
4
  type: string;
@@ -7,5 +7,5 @@ export declare const syntax: {
7
7
  };
8
8
  /** Render a readable code example with stable line numbers and caller-supplied syntax spans. */
9
9
  export default function CodeBlock({ lines }: {
10
- lines: JSX.Element[];
11
- }): import("white-label-view/jsx-runtime").JSXMarkup;
10
+ lines: HTMLMarkup[];
11
+ }): HTMLMarkup;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "white-label-view/jsx-runtime";
1
+ import { html } from 'white-label-view/html';
2
2
  export const syntax = {
3
3
  keyword: 'code-syntax-keyword',
4
4
  type: 'code-syntax-type',
@@ -7,6 +7,8 @@ export const syntax = {
7
7
  };
8
8
  /** Render a readable code example with stable line numbers and caller-supplied syntax spans. */
9
9
  export default function CodeBlock({ lines }) {
10
- return (_jsx("pre", { className: "code-block", children: _jsx("code", { children: lines.map((line, index) => (_jsxs("span", { className: "code-block__line", children: [_jsx("span", { "aria-hidden": "true", className: "code-block__number", children: index + 1 }), _jsx("span", { children: line })] }))) }) }));
10
+ return html `<pre class="code-block"><code>${lines.map((line, index) => html `<span class="code-block__line">
11
+ <span aria-hidden="true" class="code-block__number">${index + 1}</span><span>${line}</span>
12
+ </span>`)}</code></pre>`;
11
13
  }
12
14
  //# sourceMappingURL=CodeBlock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock.js","sourceRoot":"","sources":["../../../../app/assets/view/CodeBlock.tsx"],"names":[],"mappings":";AAEA,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,OAAO,EAAE,qBAAqB;IAC9B,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,mBAAmB;IAC1B,KAAK,EAAE,mBAAmB;CAC7B,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAC,KAAK,EAAyB;IAC7D,OAAO,CACH,cAAK,SAAS,EAAC,YAAY,YAAC,yBAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1D,gBAAM,SAAS,EAAC,kBAAkB,aAAC,8BAAkB,MAAM,EAAC,SAAS,EAAC,oBAAoB,YAAE,KAAK,GAAG,CAAC,GAAQ,EAAA,yBAAO,IAAI,GAAQ,IAAO,CAC1I,CAAC,GAAQ,GAAM,CACnB,CAAC;AACN,CAAC","sourcesContent":["import type {JSX} from 'white-label-view/jsx-runtime';\n\nexport const syntax = {\n keyword: 'code-syntax-keyword',\n type: 'code-syntax-type',\n value: 'code-syntax-value',\n muted: 'code-syntax-muted'\n};\n\n/** Render a readable code example with stable line numbers and caller-supplied syntax spans. */\nexport default function CodeBlock({lines}: {lines: JSX.Element[]}) {\n return (\n <pre className=\"code-block\"><code>{lines.map((line, index) => (\n <span className=\"code-block__line\"><span aria-hidden=\"true\" className=\"code-block__number\">{index + 1}</span><span>{line}</span></span>\n ))}</code></pre>\n );\n}\n"]}
1
+ {"version":3,"file":"CodeBlock.js","sourceRoot":"","sources":["../../../../app/assets/view/CodeBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAkB,MAAM,uBAAuB,CAAC;AAE5D,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,OAAO,EAAE,qBAAqB;IAC9B,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,mBAAmB;IAC1B,KAAK,EAAE,mBAAmB;CAC7B,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAC,KAAK,EAAwB;IAC5D,OAAO,IAAI,CAAA,iCAAiC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAA;sEACT,KAAK,GAAG,CAAC,gBAAgB,IAAI;oBAC/E,CAAC,eAAe,CAAC;AACrC,CAAC","sourcesContent":["import {html, type HTMLMarkup} from 'white-label-view/html';\n\nexport const syntax = {\n keyword: 'code-syntax-keyword',\n type: 'code-syntax-type',\n value: 'code-syntax-value',\n muted: 'code-syntax-muted'\n};\n\n/** Render a readable code example with stable line numbers and caller-supplied syntax spans. */\nexport default function CodeBlock({lines}: {lines: HTMLMarkup[]}) {\n return html`<pre class=\"code-block\"><code>${lines.map((line, index) => html`<span class=\"code-block__line\">\n <span aria-hidden=\"true\" class=\"code-block__number\">${index + 1}</span><span>${line}</span>\n </span>`)}</code></pre>`;\n}\n"]}
@@ -1,11 +1,5 @@
1
1
  import { type TaskState } from './task-state.js';
2
- /**
3
- * Render the task example from state only.
4
- *
5
- * The same JSX function is used twice: once while generating the initial HTML
6
- * and again by white-label-view in the browser. Sharing one renderer keeps the
7
- * static and interactive versions of the example from drifting apart.
8
- */
2
+ /** Render the task example from state for both build-time HTML and browser updates. */
9
3
  export default function TaskExample({ state }: {
10
4
  state: TaskState;
11
- }): import("white-label-view/jsx-runtime").JSXMarkup;
5
+ }): import("white-label-view/html").HTMLMarkup;
@@ -1,16 +1,48 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "white-label-view/jsx-runtime";
1
+ import { attributes, html } from 'white-label-view/html';
2
2
  import { getVisibleTasks } from './task-state.js';
3
3
  const filters = ['all', 'active', 'completed'];
4
- /**
5
- * Render the task example from state only.
6
- *
7
- * The same JSX function is used twice: once while generating the initial HTML
8
- * and again by white-label-view in the browser. Sharing one renderer keeps the
9
- * static and interactive versions of the example from drifting apart.
10
- */
4
+ /** Render the task example from state for both build-time HTML and browser updates. */
11
5
  export default function TaskExample({ state }) {
12
6
  const visibleTasks = getVisibleTasks(state);
13
7
  const completed = state.tasks.filter(task => task.complete).length;
14
- return (_jsxs("section", { className: "task-app", "data-task-app": true, "aria-label": "Interactive task example", children: [_jsxs("form", { className: "task-form", "data-task-form": true, action: "#example", method: "get", children: [_jsx("label", { htmlFor: "task-title", children: "New task" }), _jsxs("div", { className: "task-form__controls", children: [_jsx("input", { id: "task-title", name: "task", type: "text", autocomplete: "off", required: true }), _jsx("button", { type: "submit", children: "Add task" })] })] }), _jsx("nav", { className: "task-filters", "aria-label": "Filter tasks", children: filters.map(filter => (_jsxs("a", { href: `/?tasks=${filter}#example`, "data-task-filter": filter, "data-pushstate": true, "aria-current": state.filter === filter ? 'page' : 'false', children: [filter.charAt(0).toUpperCase(), filter.slice(1)] }))) }), _jsx("ul", { className: "task-list", children: visibleTasks.map(task => (_jsx("li", { children: _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: task.complete, "data-task-toggle": true, "data-task-id": task.id }), _jsx("span", { children: task.title })] }) }))) }), _jsxs("dl", { className: "demo__trace", children: [_jsxs("div", { children: [_jsx("dt", { children: "Router" }), _jsxs("dd", { children: ["/?tasks=", state.filter] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Mediator" }), _jsx("dd", { children: "task:* application events" })] }), _jsxs("div", { children: [_jsx("dt", { children: "Model" }), _jsxs("dd", { children: [state.tasks.length, " tasks \u00B7 ", completed, " complete"] })] }), _jsxs("div", { children: [_jsx("dt", { children: "View" }), _jsxs("dd", { children: [visibleTasks.length, " tasks rendered"] })] })] })] }));
8
+ return html `<section class="task-app" data-task-app aria-label="Interactive task example">
9
+ <form class="task-form" data-task-form action="#example" method="get">
10
+ <label for="task-title">New task</label>
11
+ <div class="task-form__controls">
12
+ <input id="task-title" name="task" type="text" autocomplete="off" required>
13
+ <button type="submit">Add task</button>
14
+ </div>
15
+ </form>
16
+
17
+ <nav class="task-filters" aria-label="Filter tasks">
18
+ ${filters.map(filter => html `<a ${attributes({
19
+ href: `/?tasks=${filter}#example`,
20
+ 'data-task-filter': filter,
21
+ 'data-pushstate': true,
22
+ 'aria-current': state.filter === filter ? 'page' : 'false'
23
+ })}>${filter.charAt(0).toUpperCase()}${filter.slice(1)}</a>`)}
24
+ </nav>
25
+
26
+ <ul class="task-list">
27
+ ${visibleTasks.map(task => html `<li>
28
+ <label>
29
+ <input ${attributes({
30
+ type: 'checkbox',
31
+ checked: task.complete,
32
+ 'data-task-toggle': true,
33
+ 'data-task-id': task.id
34
+ })}>
35
+ <span>${task.title}</span>
36
+ </label>
37
+ </li>`)}
38
+ </ul>
39
+
40
+ <dl class="demo__trace">
41
+ <div><dt>Router</dt><dd>/?tasks=${state.filter}</dd></div>
42
+ <div><dt>Mediator</dt><dd>task:* application events</dd></div>
43
+ <div><dt>Model</dt><dd>${state.tasks.length} tasks · ${completed} complete</dd></div>
44
+ <div><dt>View</dt><dd>${visibleTasks.length} tasks rendered</dd></div>
45
+ </dl>
46
+ </section>`;
15
47
  }
16
48
  //# sourceMappingURL=TaskExample.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TaskExample.js","sourceRoot":"","sources":["../../../../../../app/assets/view/examples/tasks/TaskExample.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAkC,MAAM,iBAAiB,CAAC;AAEjF,MAAM,OAAO,GAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAC,KAAK,EAAqB;IAC3D,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAEnE,OAAO,CACH,mBAAS,SAAS,EAAC,UAAU,uCAA0B,0BAA0B,aAC7E,gBAAM,SAAS,EAAC,WAAW,0BAAgB,MAAM,EAAC,UAAU,EAAC,MAAM,EAAC,KAAK,aACrE,gBAAO,OAAO,EAAC,YAAY,yBAAiB,EAC5C,eAAK,SAAS,EAAC,qBAAqB,aAChC,gBAAO,EAAE,EAAC,YAAY,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,YAAY,EAAC,KAAK,EAAC,QAAQ,SAAG,EAC7E,iBAAQ,IAAI,EAAC,QAAQ,yBAAkB,IACrC,IACH,EAEP,cAAK,SAAS,EAAC,cAAc,gBAAY,cAAc,YAClD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CACnB,aACI,IAAI,EAAE,WAAW,MAAM,UAAU,sBACf,MAAM,0CAEV,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,aAEvD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAChD,CACP,CAAC,GACA,EAEN,aAAI,SAAS,EAAC,WAAW,YACpB,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CACtB,uBACI,4BACI,gBACI,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,IAAI,CAAC,QAAQ,4CAER,IAAI,CAAC,EAAE,GACvB,EACF,yBAAO,IAAI,CAAC,KAAK,GAAQ,IACrB,GACP,CACR,CAAC,GACD,EAEL,cAAI,SAAS,EAAC,aAAa,aACvB,0BAAK,kCAAe,EAAA,qCAAa,KAAK,CAAC,MAAM,IAAM,IAAM,EACzD,0BAAK,oCAAiB,EAAA,qDAAkC,IAAM,EAC9D,0BAAK,iCAAc,EAAA,yBAAK,KAAK,CAAC,KAAK,CAAC,MAAM,oBAAW,SAAS,iBAAe,IAAM,EACnF,0BAAK,gCAAa,EAAA,yBAAK,YAAY,CAAC,MAAM,uBAAqB,IAAM,IACpE,IACC,CACb,CAAC;AACN,CAAC","sourcesContent":["import {getVisibleTasks, type TaskFilter, type TaskState} from './task-state.js';\n\nconst filters: TaskFilter[] = ['all', 'active', 'completed'];\n\n/**\n * Render the task example from state only.\n *\n * The same JSX function is used twice: once while generating the initial HTML\n * and again by white-label-view in the browser. Sharing one renderer keeps the\n * static and interactive versions of the example from drifting apart.\n */\nexport default function TaskExample({state}: {state: TaskState}) {\n const visibleTasks = getVisibleTasks(state);\n const completed = state.tasks.filter(task => task.complete).length;\n\n return (\n <section className=\"task-app\" data-task-app aria-label=\"Interactive task example\">\n <form className=\"task-form\" data-task-form action=\"#example\" method=\"get\">\n <label htmlFor=\"task-title\">New task</label>\n <div className=\"task-form__controls\">\n <input id=\"task-title\" name=\"task\" type=\"text\" autocomplete=\"off\" required />\n <button type=\"submit\">Add task</button>\n </div>\n </form>\n\n <nav className=\"task-filters\" aria-label=\"Filter tasks\">\n {filters.map(filter => (\n <a\n href={`/?tasks=${filter}#example`}\n data-task-filter={filter}\n data-pushstate\n aria-current={state.filter === filter ? 'page' : 'false'}\n >\n {filter.charAt(0).toUpperCase()}{filter.slice(1)}\n </a>\n ))}\n </nav>\n\n <ul className=\"task-list\">\n {visibleTasks.map(task => (\n <li>\n <label>\n <input\n type=\"checkbox\"\n checked={task.complete}\n data-task-toggle\n data-task-id={task.id}\n />\n <span>{task.title}</span>\n </label>\n </li>\n ))}\n </ul>\n\n <dl className=\"demo__trace\">\n <div><dt>Router</dt><dd>/?tasks={state.filter}</dd></div>\n <div><dt>Mediator</dt><dd>task:* application events</dd></div>\n <div><dt>Model</dt><dd>{state.tasks.length} tasks · {completed} complete</dd></div>\n <div><dt>View</dt><dd>{visibleTasks.length} tasks rendered</dd></div>\n </dl>\n </section>\n );\n}\n"]}
1
+ {"version":3,"file":"TaskExample.js","sourceRoot":"","sources":["../../../../../../app/assets/view/examples/tasks/TaskExample.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,IAAI,EAAC,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAC,eAAe,EAAkC,MAAM,iBAAiB,CAAC;AAEjF,MAAM,OAAO,GAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE7D,uFAAuF;AACvF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAC,KAAK,EAAqB;IAC3D,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAEnE,OAAO,IAAI,CAAA;;;;;;;;;;kBAUG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAA,MAAM,UAAU,CAAC;QACzC,IAAI,EAAE,WAAW,MAAM,UAAU;QACjC,kBAAkB,EAAE,MAAM;QAC1B,gBAAgB,EAAE,IAAI;QACtB,cAAc,EAAE,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KAC7D,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;;;;kBAI3D,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA;;qCAEV,UAAU,CAAC;QAChB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,kBAAkB,EAAE,IAAI;QACxB,cAAc,EAAE,IAAI,CAAC,EAAE;KAC1B,CAAC;oCACM,IAAI,CAAC,KAAK;;0BAEpB,CAAC;;;;kDAIuB,KAAK,CAAC,MAAM;;yCAErB,KAAK,CAAC,KAAK,CAAC,MAAM,YAAY,SAAS;wCACxC,YAAY,CAAC,MAAM;;mBAExC,CAAC;AACpB,CAAC","sourcesContent":["import {attributes, html} from 'white-label-view/html';\nimport {getVisibleTasks, type TaskFilter, type TaskState} from './task-state.js';\n\nconst filters: TaskFilter[] = ['all', 'active', 'completed'];\n\n/** Render the task example from state for both build-time HTML and browser updates. */\nexport default function TaskExample({state}: {state: TaskState}) {\n const visibleTasks = getVisibleTasks(state);\n const completed = state.tasks.filter(task => task.complete).length;\n\n return html`<section class=\"task-app\" data-task-app aria-label=\"Interactive task example\">\n <form class=\"task-form\" data-task-form action=\"#example\" method=\"get\">\n <label for=\"task-title\">New task</label>\n <div class=\"task-form__controls\">\n <input id=\"task-title\" name=\"task\" type=\"text\" autocomplete=\"off\" required>\n <button type=\"submit\">Add task</button>\n </div>\n </form>\n\n <nav class=\"task-filters\" aria-label=\"Filter tasks\">\n ${filters.map(filter => html`<a ${attributes({\n href: `/?tasks=${filter}#example`,\n 'data-task-filter': filter,\n 'data-pushstate': true,\n 'aria-current': state.filter === filter ? 'page' : 'false'\n })}>${filter.charAt(0).toUpperCase()}${filter.slice(1)}</a>`)}\n </nav>\n\n <ul class=\"task-list\">\n ${visibleTasks.map(task => html`<li>\n <label>\n <input ${attributes({\n type: 'checkbox',\n checked: task.complete,\n 'data-task-toggle': true,\n 'data-task-id': task.id\n })}>\n <span>${task.title}</span>\n </label>\n </li>`)}\n </ul>\n\n <dl class=\"demo__trace\">\n <div><dt>Router</dt><dd>/?tasks=${state.filter}</dd></div>\n <div><dt>Mediator</dt><dd>task:* application events</dd></div>\n <div><dt>Model</dt><dd>${state.tasks.length} tasks · ${completed} complete</dd></div>\n <div><dt>View</dt><dd>${visibleTasks.length} tasks rendered</dd></div>\n </dl>\n </section>`;\n}\n"]}
@@ -1,4 +1,4 @@
1
- /** Shared state shape used by build-time JSX and the browser application. */
1
+ /** Shared state shape used by build-time tagged HTML and the browser application. */
2
2
  export type TaskFilter = 'all' | 'active' | 'completed';
3
3
  export interface TaskItem {
4
4
  complete: boolean;