@zenithbuild/core 0.1.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 (101) hide show
  1. package/.eslintignore +15 -0
  2. package/.gitattributes +2 -0
  3. package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
  4. package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
  5. package/.github/pull_request_template.md +15 -0
  6. package/.github/workflows/discord-changelog.yml +141 -0
  7. package/.github/workflows/discord-notify.yml +242 -0
  8. package/.github/workflows/discord-version.yml +195 -0
  9. package/.prettierignore +13 -0
  10. package/.prettierrc +21 -0
  11. package/.zen.d.ts +15 -0
  12. package/LICENSE +21 -0
  13. package/README.md +55 -0
  14. package/app/components/Button.zen +46 -0
  15. package/app/components/Link.zen +11 -0
  16. package/app/favicon.ico +0 -0
  17. package/app/layouts/Main.zen +59 -0
  18. package/app/pages/about.zen +23 -0
  19. package/app/pages/blog/[id].zen +53 -0
  20. package/app/pages/blog/index.zen +32 -0
  21. package/app/pages/dynamic-dx.zen +712 -0
  22. package/app/pages/dynamic-primitives.zen +453 -0
  23. package/app/pages/index.zen +154 -0
  24. package/app/pages/navigation-demo.zen +229 -0
  25. package/app/pages/posts/[...slug].zen +61 -0
  26. package/app/pages/primitives-demo.zen +273 -0
  27. package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
  28. package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
  29. package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
  30. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
  31. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
  32. package/assets/logos/README.md +54 -0
  33. package/assets/logos/zen.icns +0 -0
  34. package/bun.lock +39 -0
  35. package/compiler/README.md +380 -0
  36. package/compiler/errors/compilerError.ts +24 -0
  37. package/compiler/finalize/finalizeOutput.ts +163 -0
  38. package/compiler/finalize/generateFinalBundle.ts +82 -0
  39. package/compiler/index.ts +44 -0
  40. package/compiler/ir/types.ts +83 -0
  41. package/compiler/legacy/binding.ts +254 -0
  42. package/compiler/legacy/bindings.ts +338 -0
  43. package/compiler/legacy/component-process.ts +1208 -0
  44. package/compiler/legacy/component.ts +301 -0
  45. package/compiler/legacy/event.ts +50 -0
  46. package/compiler/legacy/expression.ts +1149 -0
  47. package/compiler/legacy/mutation.ts +280 -0
  48. package/compiler/legacy/parse.ts +299 -0
  49. package/compiler/legacy/split.ts +608 -0
  50. package/compiler/legacy/types.ts +32 -0
  51. package/compiler/output/types.ts +34 -0
  52. package/compiler/parse/detectMapExpressions.ts +102 -0
  53. package/compiler/parse/parseScript.ts +22 -0
  54. package/compiler/parse/parseTemplate.ts +425 -0
  55. package/compiler/parse/parseZenFile.ts +66 -0
  56. package/compiler/parse/trackLoopContext.ts +82 -0
  57. package/compiler/runtime/dataExposure.ts +291 -0
  58. package/compiler/runtime/generateDOM.ts +144 -0
  59. package/compiler/runtime/generateHydrationBundle.ts +383 -0
  60. package/compiler/runtime/hydration.ts +309 -0
  61. package/compiler/runtime/navigation.ts +432 -0
  62. package/compiler/runtime/thinRuntime.ts +160 -0
  63. package/compiler/runtime/transformIR.ts +256 -0
  64. package/compiler/runtime/wrapExpression.ts +84 -0
  65. package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
  66. package/compiler/spa-build.ts +1000 -0
  67. package/compiler/test/validate-test.ts +104 -0
  68. package/compiler/transform/generateBindings.ts +47 -0
  69. package/compiler/transform/generateHTML.ts +28 -0
  70. package/compiler/transform/transformNode.ts +126 -0
  71. package/compiler/transform/transformTemplate.ts +38 -0
  72. package/compiler/validate/validateExpressions.ts +168 -0
  73. package/core/index.ts +135 -0
  74. package/core/lifecycle/index.ts +49 -0
  75. package/core/lifecycle/zen-mount.ts +182 -0
  76. package/core/lifecycle/zen-unmount.ts +88 -0
  77. package/core/reactivity/index.ts +54 -0
  78. package/core/reactivity/tracking.ts +167 -0
  79. package/core/reactivity/zen-batch.ts +57 -0
  80. package/core/reactivity/zen-effect.ts +139 -0
  81. package/core/reactivity/zen-memo.ts +146 -0
  82. package/core/reactivity/zen-ref.ts +52 -0
  83. package/core/reactivity/zen-signal.ts +121 -0
  84. package/core/reactivity/zen-state.ts +180 -0
  85. package/core/reactivity/zen-untrack.ts +44 -0
  86. package/docs/COMMENTS.md +111 -0
  87. package/docs/COMMITS.md +36 -0
  88. package/docs/CONTRIBUTING.md +116 -0
  89. package/docs/STYLEGUIDE.md +62 -0
  90. package/package.json +44 -0
  91. package/router/index.ts +76 -0
  92. package/router/manifest.ts +314 -0
  93. package/router/navigation/ZenLink.zen +231 -0
  94. package/router/navigation/index.ts +78 -0
  95. package/router/navigation/zen-link.ts +584 -0
  96. package/router/runtime.ts +458 -0
  97. package/router/types.ts +168 -0
  98. package/runtime/build.ts +17 -0
  99. package/runtime/serve.ts +93 -0
  100. package/scripts/webhook-proxy.ts +213 -0
  101. package/tsconfig.json +28 -0
@@ -0,0 +1,229 @@
1
+ <script>
2
+ // Navigation Demo Page
3
+ // Phase 7: Demonstrates prefetch, navigation, and explicit data exposure
4
+
5
+ // Sample loader data (simulated)
6
+ state loaderData = {
7
+ user: { name: 'Alice', id: 1 },
8
+ posts: [
9
+ { id: 1, title: 'Welcome to Zenith', slug: 'welcome' },
10
+ { id: 2, title: 'Navigation Guide', slug: 'navigation' }
11
+ ]
12
+ };
13
+
14
+ // Sample page props
15
+ state pageProps = {
16
+ title: 'Navigation Demo',
17
+ showBreadcrumbs: true
18
+ };
19
+
20
+ // Sample stores
21
+ state storeData = {
22
+ cart: { items: 3, total: 99.99 },
23
+ notifications: [{ id: 1, message: 'Welcome!' }]
24
+ };
25
+
26
+ // Navigation handlers
27
+ function navigateToAbout(event) {
28
+ console.log('[Navigation Demo] Navigate to About clicked');
29
+ if (typeof window !== 'undefined') {
30
+ if (window.__zenith_router && window.__zenith_router.navigate) {
31
+ console.log('[Navigation Demo] Using router.navigate');
32
+ window.__zenith_router.navigate('/about');
33
+ } else if (window.navigate) {
34
+ console.log('[Navigation Demo] Using window.navigate');
35
+ window.navigate('/about');
36
+ } else {
37
+ console.warn('[Navigation Demo] Navigation not available');
38
+ }
39
+ }
40
+ }
41
+
42
+ function navigateToBlog(event) {
43
+ console.log('[Navigation Demo] Navigate to Blog clicked');
44
+ if (typeof window !== 'undefined') {
45
+ if (window.__zenith_router && window.__zenith_router.navigate) {
46
+ console.log('[Navigation Demo] Using router.navigate');
47
+ window.__zenith_router.navigate('/blog');
48
+ } else if (window.navigate) {
49
+ console.log('[Navigation Demo] Using window.navigate');
50
+ window.navigate('/blog');
51
+ } else {
52
+ console.warn('[Navigation Demo] Navigation not available');
53
+ }
54
+ }
55
+ }
56
+
57
+ // Initialize - expose data for navigation
58
+ if (typeof window !== 'undefined') {
59
+ console.log('[Navigation Demo] Initializing page');
60
+ console.log('[Navigation Demo] Exposing data:', {
61
+ loaderData: loaderData,
62
+ props: pageProps,
63
+ stores: storeData
64
+ });
65
+
66
+ window.__ZENITH_LOADER_DATA__ = loaderData;
67
+ window.__ZENITH_PROPS__ = pageProps;
68
+ window.__ZENITH_STORES__ = storeData;
69
+
70
+ // Listen for navigation events
71
+ window.addEventListener('zenith:navigate', function(event) {
72
+ console.log('[Navigation Demo] Navigation event received:', event.detail);
73
+ });
74
+
75
+ // Check if router navigation is available
76
+ if (window.__zenith_router && window.__zenith_router.navigate) {
77
+ console.log('[Navigation Demo] Router navigation available:', typeof window.__zenith_router.navigate);
78
+ } else if (window.navigate) {
79
+ console.log('[Navigation Demo] Navigation function available:', typeof window.navigate);
80
+ } else {
81
+ console.warn('[Navigation Demo] Navigation not available');
82
+ }
83
+
84
+ // Check if router prefetch is available
85
+ if (window.__zenith_router && window.__zenith_router.prefetch) {
86
+ console.log('[Navigation Demo] Router prefetch available:', typeof window.__zenith_router.prefetch);
87
+ } else {
88
+ console.warn('[Navigation Demo] Router prefetch not available');
89
+ }
90
+
91
+ console.log('[Navigation Demo] Current route:', window.location.pathname);
92
+ console.log('[Navigation Demo] Initialization complete');
93
+ }
94
+ </script>
95
+
96
+ <Main title="Navigation & Prefetch Demo">
97
+ <div style="max-width: 800px; margin: 0 auto; padding: 20px;">
98
+ <h1>Phase 7: Navigation & Prefetch Demo</h1>
99
+
100
+ <div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0;">
101
+ <h2>Current Route</h2>
102
+ <p><strong>Path:</strong> <code id="current-route-display">/navigation-demo</code></p>
103
+ <p style="color: #666; font-size: 0.9em;">Navigate using the links below to see route changes.</p>
104
+ </div>
105
+
106
+ <div style="background: #e8f4f8; padding: 20px; border-radius: 8px; margin: 20px 0;">
107
+ <h2>Explicit Data Exposure</h2>
108
+ <div style="margin: 10px 0;">
109
+ <h3>Loader Data</h3>
110
+ <pre style="background: white; padding: 10px; border-radius: 4px; overflow-x: auto;">{JSON.stringify(loaderData, null, 2)}</pre>
111
+ </div>
112
+ <div style="margin: 10px 0;">
113
+ <h3>Props</h3>
114
+ <pre style="background: white; padding: 10px; border-radius: 4px; overflow-x: auto;">{JSON.stringify(pageProps, null, 2)}</pre>
115
+ </div>
116
+ <div style="margin: 10px 0;">
117
+ <h3>Stores</h3>
118
+ <pre style="background: white; padding: 10px; border-radius: 4px; overflow-x: auto;">{JSON.stringify(storeData, null, 2)}</pre>
119
+ </div>
120
+ </div>
121
+
122
+ <div style="background: #fff4e6; padding: 20px; border-radius: 8px; margin: 20px 0;">
123
+ <h2>Navigation Examples</h2>
124
+
125
+ <div style="margin: 15px 0;">
126
+ <h3>ZenLink with Prefetch</h3>
127
+ <p>These links prefetch on hover:</p>
128
+ <ul style="list-style: none; padding: 0;">
129
+ <li style="margin: 10px 0;">
130
+ <ZenLink href="/" preload={true}>Home (with prefetch)</ZenLink>
131
+ </li>
132
+ <li style="margin: 10px 0;">
133
+ <ZenLink href="/about" preload={true}>About (with prefetch)</ZenLink>
134
+ </li>
135
+ <li style="margin: 10px 0;">
136
+ <ZenLink href="/blog" preload={true}>Blog (with prefetch)</ZenLink>
137
+ </li>
138
+ </ul>
139
+ </div>
140
+
141
+ <div style="margin: 15px 0;">
142
+ <h3>Programmatic Navigation</h3>
143
+ <p>Navigate with explicit data (check console for navigation logs):</p>
144
+ <button
145
+ onclick="navigateToAbout"
146
+ style="padding: 10px 20px; margin: 5px; background: #0066cc; color: white; border: none; border-radius: 4px; cursor: pointer;"
147
+ >
148
+ Navigate to About
149
+ </button>
150
+ <button
151
+ onclick="navigateToBlog"
152
+ style="padding: 10px 20px; margin: 5px; background: #0066cc; color: white; border: none; border-radius: 4px; cursor: pointer;"
153
+ >
154
+ Navigate to Blog
155
+ </button>
156
+ </div>
157
+
158
+ <div style="margin: 15px 0;">
159
+ <h3>Browser History</h3>
160
+ <p>Use browser back/forward buttons to test history handling.</p>
161
+ <p style="color: #666; font-size: 0.9em;">
162
+ The navigation system handles popstate events and restores DOM state correctly.
163
+ </p>
164
+ </div>
165
+ </div>
166
+
167
+ <div style="background: #f0f0f0; padding: 20px; border-radius: 8px; margin: 20px 0;">
168
+ <h2>Features Demonstrated</h2>
169
+ <ul>
170
+ <li>✅ Prefetch compiled output (HTML + JS)</li>
171
+ <li>✅ Safe SPA navigation with explicit data</li>
172
+ <li>✅ Browser back/forward support</li>
173
+ <li>✅ Route caching</li>
174
+ <li>✅ Explicit data exposure (loaderData, props, stores)</li>
175
+ <li>✅ No raw .zen files in browser</li>
176
+ </ul>
177
+ </div>
178
+
179
+ <div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;">
180
+ <nav>
181
+ <h3>Quick Navigation</h3>
182
+ <ul style="list-style: none; padding: 0;">
183
+ <li style="margin: 5px 0;"><ZenLink preload={true} href="/">Home</ZenLink></li>
184
+ <li style="margin: 5px 0;"><ZenLink href="/about">About</ZenLink></li>
185
+ <li style="margin: 5px 0;"><ZenLink href="/blog">Blog</ZenLink></li>
186
+ <li style="margin: 5px 0;"><ZenLink href="/navigation-demo">This Page</ZenLink></li>
187
+ </ul>
188
+ </nav>
189
+ </div>
190
+ </div>
191
+ </Main>
192
+
193
+ <style>
194
+ h1 {
195
+ color: #333;
196
+ margin-bottom: 10px;
197
+ }
198
+
199
+ h2 {
200
+ color: #555;
201
+ margin-top: 0;
202
+ margin-bottom: 15px;
203
+ }
204
+
205
+ h3 {
206
+ color: #666;
207
+ margin-top: 10px;
208
+ margin-bottom: 10px;
209
+ font-size: 1.1em;
210
+ }
211
+
212
+ code {
213
+ background: #f0f0f0;
214
+ padding: 2px 6px;
215
+ border-radius: 3px;
216
+ font-family: 'Courier New', monospace;
217
+ font-size: 0.9em;
218
+ }
219
+
220
+ pre {
221
+ font-size: 0.85em;
222
+ line-height: 1.4;
223
+ }
224
+
225
+ button:hover {
226
+ background: #0052a3 !important;
227
+ }
228
+ </style>
229
+
@@ -0,0 +1,61 @@
1
+ <script>
2
+ // Access route params from the global route state
3
+ const route = window.__zenith_route || { params: {} };
4
+ const slug = route.params.slug || '';
5
+ const slugParts = slug.split('/');
6
+
7
+ // Update the display when route changes
8
+ if (window.__zenith_router) {
9
+ window.__zenith_router.onRouteChange(function(newRoute) {
10
+ const slugEl = document.getElementById('catch-all-slug');
11
+ const partsEl = document.getElementById('slug-parts');
12
+ if (slugEl) {
13
+ slugEl.textContent = newRoute.params.slug || '(empty)';
14
+ }
15
+ if (partsEl) {
16
+ const parts = (newRoute.params.slug || '').split('/');
17
+ partsEl.innerHTML = parts.map(function(p, i) {
18
+ return '<li>Part ' + (i + 1) + ': <code>' + p + '</code></li>';
19
+ }).join('');
20
+ }
21
+ });
22
+ }
23
+ </script>
24
+
25
+ <Main title="Posts - Catch All">
26
+ <div>
27
+ <h1>Catch-All Route</h1>
28
+
29
+ <div style="margin: 20px 0; padding: 20px; background: #f8e8f4; border-radius: 8px;">
30
+ <h2>Slug: <span id="catch-all-slug"></span></h2>
31
+ <h3>Slug Parts:</h3>
32
+ <ul id="slug-parts"></ul>
33
+ </div>
34
+
35
+ <div style="margin: 20px 0; padding: 20px; border: 1px solid #ccc; border-radius: 8px;">
36
+ <h3>Route Information</h3>
37
+ <p>This page is rendered from <code>pages/posts/[...slug].zen</code></p>
38
+ <p>Route pattern: <code>/posts/*slug</code></p>
39
+ <p>This catches all paths starting with <code>/posts/</code></p>
40
+ </div>
41
+
42
+ <div style="margin: 20px 0;">
43
+ <h3>Try Different Paths</h3>
44
+ <ul>
45
+ <li><a href="/posts/hello">posts/hello</a></li>
46
+ <li><a href="/posts/hello/world">posts/hello/world</a></li>
47
+ <li><a href="/posts/2024/01/my-post">posts/2024/01/my-post</a></li>
48
+ <li><a href="/posts/a/b/c/d/e">posts/a/b/c/d/e</a></li>
49
+ </ul>
50
+ </div>
51
+
52
+ <nav style="margin-top: 20px;">
53
+ <h3>Navigation</h3>
54
+ <ul style="list-style: none; padding: 0;">
55
+ <li><a href="/">Home</a></li>
56
+ <li><a href="/about">About</a></li>
57
+ <li><a href="/blog">Blog</a></li>
58
+ </ul>
59
+ </nav>
60
+ </div>
61
+ </Main>
@@ -0,0 +1,273 @@
1
+ <script>
2
+ // ============================================
3
+ // Zen* Primitives Demo
4
+ // ============================================
5
+ // These primitives are AUTO-IMPORTED - no import statement needed!
6
+ // The compiler detects usage and injects them automatically.
7
+
8
+ // zenSignal - Atomic reactive value
9
+ const count = zenSignal(0)
10
+ const multiplier = zenSignal(2)
11
+
12
+ function incrementCount() {
13
+ count(count() + 1)
14
+ }
15
+
16
+ function decrementCount() {
17
+ count(count() - 1)
18
+ }
19
+
20
+ // zenMemo - Computed/cached value
21
+ const doubled = zenMemo(() => count() * 2)
22
+ const multiplied = zenMemo(() => count() * multiplier())
23
+
24
+ // zenEffect - Auto-tracked side effect
25
+ zenEffect(() => {
26
+ console.log('[zenEffect] Count changed to:', count())
27
+ // Update display
28
+ const el = document.getElementById('count-display')
29
+ if (el) el.textContent = String(count())
30
+
31
+ const dblEl = document.getElementById('doubled-display')
32
+ if (dblEl) dblEl.textContent = String(doubled())
33
+
34
+ const mulEl = document.getElementById('multiplied-display')
35
+ if (mulEl) mulEl.textContent = String(multiplied())
36
+ })
37
+
38
+ // zenRef - DOM reference
39
+ const inputRef = zenRef()
40
+
41
+ function focusInput() {
42
+ if (inputRef.current) {
43
+ inputRef.current.focus()
44
+ inputRef.current.select()
45
+ }
46
+ }
47
+
48
+ // zenBatch - Batch updates
49
+ function batchUpdate() {
50
+ zenBatch(() => {
51
+ count(count() + 1)
52
+ multiplier(multiplier() + 1)
53
+ })
54
+ }
55
+
56
+ // zenOnMount - Lifecycle hook
57
+ zenOnMount(() => {
58
+ console.log('[zenOnMount] Page mounted!')
59
+ // Focus input on mount
60
+ setTimeout(() => {
61
+ if (inputRef.current) inputRef.current.focus()
62
+ }, 100)
63
+
64
+ // Return cleanup function
65
+ return () => {
66
+ console.log('[zenOnMount cleanup] Page unmounting...')
67
+ }
68
+ })
69
+
70
+ // zenOnUnmount - Cleanup hook
71
+ zenOnUnmount(() => {
72
+ console.log('[zenOnUnmount] Page is unmounting!')
73
+ })
74
+ </script>
75
+
76
+ <style>
77
+ body {
78
+ font-family: system-ui, -apple-system, sans-serif;
79
+ max-width: 900px;
80
+ margin: 0 auto;
81
+ padding: 20px;
82
+ background: #f5f5f5;
83
+ }
84
+
85
+ .demo-section {
86
+ background: white;
87
+ border-radius: 8px;
88
+ padding: 20px;
89
+ margin-bottom: 20px;
90
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
91
+ }
92
+
93
+ .demo-section h2 {
94
+ margin-top: 0;
95
+ color: #333;
96
+ border-bottom: 2px solid #6366f1;
97
+ padding-bottom: 10px;
98
+ }
99
+
100
+ .value-display {
101
+ background: #f8f9fa;
102
+ border: 1px solid #dee2e6;
103
+ border-radius: 4px;
104
+ padding: 15px;
105
+ margin: 10px 0;
106
+ font-family: 'Monaco', 'Courier New', monospace;
107
+ font-size: 16px;
108
+ }
109
+
110
+ .button-group {
111
+ display: flex;
112
+ gap: 10px;
113
+ flex-wrap: wrap;
114
+ margin: 10px 0;
115
+ }
116
+
117
+ button {
118
+ background: #6366f1;
119
+ color: white;
120
+ border: none;
121
+ border-radius: 4px;
122
+ padding: 10px 20px;
123
+ cursor: pointer;
124
+ font-size: 14px;
125
+ transition: background 0.2s, transform 0.1s;
126
+ }
127
+
128
+ button:hover {
129
+ background: #4f46e5;
130
+ transform: translateY(-1px);
131
+ }
132
+
133
+ button:active {
134
+ transform: translateY(0);
135
+ }
136
+
137
+ .success { background: #22c55e; }
138
+ .success:hover { background: #16a34a; }
139
+
140
+ code {
141
+ background: #e5e7eb;
142
+ padding: 2px 6px;
143
+ border-radius: 3px;
144
+ font-family: 'Monaco', 'Courier New', monospace;
145
+ font-size: 13px;
146
+ color: #6366f1;
147
+ }
148
+
149
+ .highlight {
150
+ color: #6366f1;
151
+ font-weight: 600;
152
+ }
153
+
154
+ input {
155
+ padding: 10px;
156
+ border: 2px solid #e5e7eb;
157
+ border-radius: 4px;
158
+ font-size: 14px;
159
+ width: 200px;
160
+ }
161
+
162
+ input:focus {
163
+ border-color: #6366f1;
164
+ outline: none;
165
+ }
166
+
167
+ .nav-link {
168
+ display: inline-block;
169
+ padding: 8px 16px;
170
+ background: #6366f1;
171
+ color: white;
172
+ text-decoration: none;
173
+ border-radius: 4px;
174
+ margin-right: 10px;
175
+ transition: background 0.2s;
176
+ }
177
+
178
+ .nav-link:hover {
179
+ background: #4f46e5;
180
+ }
181
+ </style>
182
+
183
+ <Main title="Zen* Primitives Demo">
184
+ <div>
185
+ <h1>🎯 Zen* Primitives - Live Demo</h1>
186
+ <p>All primitives are <span class="highlight">auto-imported</span> - no import statements needed!</p>
187
+
188
+ <!-- zenSignal Demo -->
189
+ <div class="demo-section">
190
+ <h2>📊 zenSignal - Reactive Counter</h2>
191
+ <p>Atomic reactive values with getter/setter pattern</p>
192
+
193
+ <div class="value-display">
194
+ count() = <strong id="count-display">0</strong>
195
+ </div>
196
+ <div class="value-display">
197
+ doubled (zenMemo) = <strong id="doubled-display">0</strong>
198
+ </div>
199
+ <div class="value-display">
200
+ count × multiplier (zenMemo) = <strong id="multiplied-display">0</strong>
201
+ </div>
202
+
203
+ <div class="button-group">
204
+ <button onclick="incrementCount">+1</button>
205
+ <button onclick="decrementCount">-1</button>
206
+ <button onclick="batchUpdate" class="success">Batch +1 Both</button>
207
+ </div>
208
+
209
+ <p style="font-size: 13px; color: #666;">
210
+ <code>zenSignal(0)</code> creates a reactive value.
211
+ <code>zenMemo</code> creates cached computed values.
212
+ <code>zenEffect</code> automatically updates the DOM when dependencies change.
213
+ </p>
214
+ </div>
215
+
216
+ <!-- zenRef Demo -->
217
+ <div class="demo-section">
218
+ <h2>🎯 zenRef - DOM Reference</h2>
219
+ <p>Non-reactive container for DOM elements</p>
220
+
221
+ <input ref="inputRef" type="text" placeholder="Type something here..." />
222
+ <div class="button-group" style="margin-top: 10px;">
223
+ <button onclick="focusInput">Focus & Select Input</button>
224
+ </div>
225
+
226
+ <p style="font-size: 13px; color: #666;">
227
+ <code>zenRef()</code> creates a mutable container.
228
+ Use <code>ref="inputRef"</code> in HTML to bind.
229
+ </p>
230
+ </div>
231
+
232
+ <!-- Lifecycle Demo -->
233
+ <div class="demo-section">
234
+ <h2>🔄 Lifecycle Hooks</h2>
235
+ <p>Check the browser console for lifecycle logs!</p>
236
+
237
+ <ul style="font-size: 14px; line-height: 1.8;">
238
+ <li><code>zenOnMount</code> - runs after page renders (check console)</li>
239
+ <li><code>zenOnUnmount</code> - runs when navigating away (check console)</li>
240
+ </ul>
241
+
242
+ <p style="font-size: 13px; color: #666;">
243
+ Navigate to another page to see unmount logs.
244
+ </p>
245
+ </div>
246
+
247
+ <!-- Navigation -->
248
+ <div class="demo-section">
249
+ <h2>🔗 Navigation</h2>
250
+ <p>Test SPA navigation (unmount hooks will fire):</p>
251
+ <ZenLink href="/" class="nav-link">← Home</ZenLink>
252
+ <ZenLink href="/about" class="nav-link">About</ZenLink>
253
+ <ZenLink href="/blog" class="nav-link">Blog</ZenLink>
254
+ </div>
255
+
256
+ <!-- API Reference -->
257
+ <div class="demo-section">
258
+ <h2>📖 Auto-Imported Primitives</h2>
259
+ <p>These are automatically available in any <code>.zen</code> file:</p>
260
+ <ul style="font-size: 14px; line-height: 2;">
261
+ <li><code>zenSignal(value)</code> / <code>signal(value)</code></li>
262
+ <li><code>zenState(obj)</code> / <code>state(obj)</code></li>
263
+ <li><code>zenMemo(fn)</code> / <code>memo(fn)</code></li>
264
+ <li><code>zenEffect(fn)</code> / <code>effect(fn)</code></li>
265
+ <li><code>zenRef()</code> / <code>ref()</code></li>
266
+ <li><code>zenBatch(fn)</code> / <code>batch(fn)</code></li>
267
+ <li><code>zenUntrack(fn)</code> / <code>untrack(fn)</code></li>
268
+ <li><code>zenOnMount(fn)</code> / <code>onMount(fn)</code></li>
269
+ <li><code>zenOnUnmount(fn)</code> / <code>onUnmount(fn)</code></li>
270
+ </ul>
271
+ </div>
272
+ </div>
273
+ </Main>