@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.
- package/.eslintignore +15 -0
- package/.gitattributes +2 -0
- package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
- package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
- package/.github/pull_request_template.md +15 -0
- package/.github/workflows/discord-changelog.yml +141 -0
- package/.github/workflows/discord-notify.yml +242 -0
- package/.github/workflows/discord-version.yml +195 -0
- package/.prettierignore +13 -0
- package/.prettierrc +21 -0
- package/.zen.d.ts +15 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/app/components/Button.zen +46 -0
- package/app/components/Link.zen +11 -0
- package/app/favicon.ico +0 -0
- package/app/layouts/Main.zen +59 -0
- package/app/pages/about.zen +23 -0
- package/app/pages/blog/[id].zen +53 -0
- package/app/pages/blog/index.zen +32 -0
- package/app/pages/dynamic-dx.zen +712 -0
- package/app/pages/dynamic-primitives.zen +453 -0
- package/app/pages/index.zen +154 -0
- package/app/pages/navigation-demo.zen +229 -0
- package/app/pages/posts/[...slug].zen +61 -0
- package/app/pages/primitives-demo.zen +273 -0
- package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
- package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
- package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
- package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
- package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
- package/assets/logos/README.md +54 -0
- package/assets/logos/zen.icns +0 -0
- package/bun.lock +39 -0
- package/compiler/README.md +380 -0
- package/compiler/errors/compilerError.ts +24 -0
- package/compiler/finalize/finalizeOutput.ts +163 -0
- package/compiler/finalize/generateFinalBundle.ts +82 -0
- package/compiler/index.ts +44 -0
- package/compiler/ir/types.ts +83 -0
- package/compiler/legacy/binding.ts +254 -0
- package/compiler/legacy/bindings.ts +338 -0
- package/compiler/legacy/component-process.ts +1208 -0
- package/compiler/legacy/component.ts +301 -0
- package/compiler/legacy/event.ts +50 -0
- package/compiler/legacy/expression.ts +1149 -0
- package/compiler/legacy/mutation.ts +280 -0
- package/compiler/legacy/parse.ts +299 -0
- package/compiler/legacy/split.ts +608 -0
- package/compiler/legacy/types.ts +32 -0
- package/compiler/output/types.ts +34 -0
- package/compiler/parse/detectMapExpressions.ts +102 -0
- package/compiler/parse/parseScript.ts +22 -0
- package/compiler/parse/parseTemplate.ts +425 -0
- package/compiler/parse/parseZenFile.ts +66 -0
- package/compiler/parse/trackLoopContext.ts +82 -0
- package/compiler/runtime/dataExposure.ts +291 -0
- package/compiler/runtime/generateDOM.ts +144 -0
- package/compiler/runtime/generateHydrationBundle.ts +383 -0
- package/compiler/runtime/hydration.ts +309 -0
- package/compiler/runtime/navigation.ts +432 -0
- package/compiler/runtime/thinRuntime.ts +160 -0
- package/compiler/runtime/transformIR.ts +256 -0
- package/compiler/runtime/wrapExpression.ts +84 -0
- package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
- package/compiler/spa-build.ts +1000 -0
- package/compiler/test/validate-test.ts +104 -0
- package/compiler/transform/generateBindings.ts +47 -0
- package/compiler/transform/generateHTML.ts +28 -0
- package/compiler/transform/transformNode.ts +126 -0
- package/compiler/transform/transformTemplate.ts +38 -0
- package/compiler/validate/validateExpressions.ts +168 -0
- package/core/index.ts +135 -0
- package/core/lifecycle/index.ts +49 -0
- package/core/lifecycle/zen-mount.ts +182 -0
- package/core/lifecycle/zen-unmount.ts +88 -0
- package/core/reactivity/index.ts +54 -0
- package/core/reactivity/tracking.ts +167 -0
- package/core/reactivity/zen-batch.ts +57 -0
- package/core/reactivity/zen-effect.ts +139 -0
- package/core/reactivity/zen-memo.ts +146 -0
- package/core/reactivity/zen-ref.ts +52 -0
- package/core/reactivity/zen-signal.ts +121 -0
- package/core/reactivity/zen-state.ts +180 -0
- package/core/reactivity/zen-untrack.ts +44 -0
- package/docs/COMMENTS.md +111 -0
- package/docs/COMMITS.md +36 -0
- package/docs/CONTRIBUTING.md +116 -0
- package/docs/STYLEGUIDE.md +62 -0
- package/package.json +44 -0
- package/router/index.ts +76 -0
- package/router/manifest.ts +314 -0
- package/router/navigation/ZenLink.zen +231 -0
- package/router/navigation/index.ts +78 -0
- package/router/navigation/zen-link.ts +584 -0
- package/router/runtime.ts +458 -0
- package/router/types.ts +168 -0
- package/runtime/build.ts +17 -0
- package/runtime/serve.ts +93 -0
- package/scripts/webhook-proxy.ts +213 -0
- 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>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|