zero-query 0.6.3 → 0.8.6
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/README.md +39 -29
- package/cli/commands/build.js +113 -4
- package/cli/commands/bundle.js +392 -29
- package/cli/commands/create.js +1 -1
- package/cli/commands/dev/devtools/index.js +56 -0
- package/cli/commands/dev/devtools/js/components.js +49 -0
- package/cli/commands/dev/devtools/js/core.js +409 -0
- package/cli/commands/dev/devtools/js/elements.js +413 -0
- package/cli/commands/dev/devtools/js/network.js +166 -0
- package/cli/commands/dev/devtools/js/performance.js +73 -0
- package/cli/commands/dev/devtools/js/router.js +105 -0
- package/cli/commands/dev/devtools/js/source.js +132 -0
- package/cli/commands/dev/devtools/js/stats.js +35 -0
- package/cli/commands/dev/devtools/js/tabs.js +79 -0
- package/cli/commands/dev/devtools/panel.html +95 -0
- package/cli/commands/dev/devtools/styles.css +244 -0
- package/cli/commands/dev/index.js +29 -4
- package/cli/commands/dev/logger.js +6 -1
- package/cli/commands/dev/overlay.js +428 -2
- package/cli/commands/dev/server.js +42 -5
- package/cli/commands/dev/watcher.js +59 -1
- package/cli/help.js +8 -5
- package/cli/scaffold/{scripts → app}/app.js +16 -23
- package/cli/scaffold/{scripts → app}/components/about.js +4 -4
- package/cli/scaffold/{scripts → app}/components/api-demo.js +1 -1
- package/cli/scaffold/{scripts → app}/components/contacts/contacts.css +0 -7
- package/cli/scaffold/{scripts → app}/components/contacts/contacts.html +3 -3
- package/cli/scaffold/app/components/home.js +137 -0
- package/cli/scaffold/{scripts → app}/routes.js +1 -1
- package/cli/scaffold/{scripts → app}/store.js +6 -6
- package/cli/scaffold/assets/.gitkeep +0 -0
- package/cli/scaffold/{styles/styles.css → global.css} +4 -2
- package/cli/scaffold/index.html +12 -11
- package/cli/utils.js +111 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +1122 -158
- package/dist/zquery.min.js +3 -16
- package/index.d.ts +129 -1290
- package/index.js +15 -10
- package/package.json +7 -6
- package/src/component.js +172 -49
- package/src/core.js +359 -18
- package/src/diff.js +256 -58
- package/src/expression.js +33 -3
- package/src/reactive.js +37 -5
- package/src/router.js +243 -7
- package/tests/component.test.js +886 -0
- package/tests/core.test.js +977 -0
- package/tests/diff.test.js +525 -0
- package/tests/errors.test.js +162 -0
- package/tests/expression.test.js +482 -0
- package/tests/http.test.js +289 -0
- package/tests/reactive.test.js +339 -0
- package/tests/router.test.js +649 -0
- package/tests/store.test.js +379 -0
- package/tests/utils.test.js +512 -0
- package/types/collection.d.ts +383 -0
- package/types/component.d.ts +217 -0
- package/types/errors.d.ts +103 -0
- package/types/http.d.ts +81 -0
- package/types/misc.d.ts +179 -0
- package/types/reactive.d.ts +76 -0
- package/types/router.d.ts +161 -0
- package/types/ssr.d.ts +49 -0
- package/types/store.d.ts +107 -0
- package/types/utils.d.ts +142 -0
- package/cli/commands/dev.old.js +0 -520
- package/cli/scaffold/scripts/components/home.js +0 -137
- /package/cli/scaffold/{scripts → app}/components/contacts/contacts.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/counter.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/not-found.js +0 -0
- /package/cli/scaffold/{scripts → app}/components/todos.js +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// app/app.js — application entry point
|
|
2
2
|
//
|
|
3
3
|
// This file bootstraps the zQuery app: imports all components,
|
|
4
4
|
// sets up routing, wires the responsive nav, and demonstrates
|
|
@@ -27,7 +27,7 @@ const router = $.router({
|
|
|
27
27
|
// Highlight the active nav link on every route change
|
|
28
28
|
router.onChange((to) => {
|
|
29
29
|
$.all('.nav-link').removeClass('active');
|
|
30
|
-
|
|
30
|
+
$(`.nav-link[z-link="${to.path}"]`).addClass('active');
|
|
31
31
|
|
|
32
32
|
// Close mobile menu on navigate
|
|
33
33
|
closeMobileMenu();
|
|
@@ -36,26 +36,20 @@ router.onChange((to) => {
|
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
37
|
// Responsive sidebar toggle
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
|
-
const sidebar =
|
|
40
|
-
const overlay =
|
|
41
|
-
const toggle =
|
|
39
|
+
const $sidebar = $('#sidebar');
|
|
40
|
+
const $overlay = $('#overlay');
|
|
41
|
+
const $toggle = $('#menu-toggle');
|
|
42
42
|
|
|
43
|
-
function
|
|
44
|
-
sidebar.
|
|
45
|
-
overlay.
|
|
46
|
-
toggle.
|
|
43
|
+
function toggleMobileMenu(open) {
|
|
44
|
+
$sidebar.toggleClass('open', open);
|
|
45
|
+
$overlay.toggleClass('visible', open);
|
|
46
|
+
$toggle.toggleClass('active', open);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function closeMobileMenu() {
|
|
50
|
-
sidebar.classList.remove('open');
|
|
51
|
-
overlay.classList.remove('visible');
|
|
52
|
-
toggle.classList.remove('active');
|
|
53
|
-
}
|
|
49
|
+
function closeMobileMenu() { toggleMobileMenu(false); }
|
|
54
50
|
|
|
55
51
|
// $.on — global delegated event listeners
|
|
56
|
-
$.on('click', '#menu-toggle', () =>
|
|
57
|
-
sidebar.classList.contains('open') ? closeMobileMenu() : openMobileMenu();
|
|
58
|
-
});
|
|
52
|
+
$.on('click', '#menu-toggle', () => toggleMobileMenu(!$sidebar.hasClass('open')));
|
|
59
53
|
$.on('click', '#overlay', closeMobileMenu);
|
|
60
54
|
|
|
61
55
|
// Close sidebar on Escape key — using $.on direct (no selector needed)
|
|
@@ -69,14 +63,13 @@ $.on('keydown', (e) => {
|
|
|
69
63
|
// Any component can emit: $.bus.emit('toast', { message, type })
|
|
70
64
|
// Types: 'success', 'error', 'info'
|
|
71
65
|
$.bus.on('toast', ({ message, type = 'info' }) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
container.appendChild(toast);
|
|
66
|
+
const toast = $.create('div')
|
|
67
|
+
.addClass('toast', `toast-${type}`)
|
|
68
|
+
.text(message)
|
|
69
|
+
.appendTo('#toasts');
|
|
77
70
|
// Auto-remove after 3 seconds
|
|
78
71
|
setTimeout(() => {
|
|
79
|
-
toast.
|
|
72
|
+
toast.addClass('toast-exit');
|
|
80
73
|
setTimeout(() => toast.remove(), 300);
|
|
81
74
|
}, 3000);
|
|
82
75
|
});
|
|
@@ -31,16 +31,16 @@ $.component('about-page', {
|
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
33
|
<div class="card">
|
|
34
|
-
<h3
|
|
34
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42"/></svg> Theme</h3>
|
|
35
35
|
<p>Toggle between dark and light mode. Persisted to <code>localStorage</code> via <code>$.storage</code>.</p>
|
|
36
36
|
<div class="theme-toggle">
|
|
37
37
|
<span>Current: <strong>${this.state.theme}</strong></span>
|
|
38
|
-
<button class="btn btn-outline" @click="toggleTheme">${this.state.theme === 'dark' ? '
|
|
38
|
+
<button class="btn btn-outline" @click="toggleTheme">${this.state.theme === 'dark' ? '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"/></svg> Light Mode' : '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"/></svg> Dark Mode'}</button>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
42
|
<div class="card">
|
|
43
|
-
<h3
|
|
43
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z"/></svg> Features Used in This App</h3>
|
|
44
44
|
<div class="feature-grid">
|
|
45
45
|
<div class="feature-item">
|
|
46
46
|
<strong>$.component()</strong>
|
|
@@ -118,7 +118,7 @@ $.component('about-page', {
|
|
|
118
118
|
</div>
|
|
119
119
|
|
|
120
120
|
<div class="card card-muted">
|
|
121
|
-
<h3
|
|
121
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"/></svg> Next Steps</h3>
|
|
122
122
|
<ul class="next-steps">
|
|
123
123
|
<li>Read the <a href="https://z-query.com/docs" target="_blank" rel="noopener">full documentation</a></li>
|
|
124
124
|
<li>Explore the <a href="https://github.com/tonywied17/zero-query" target="_blank" rel="noopener">source on GitHub</a></li>
|
|
@@ -58,7 +58,7 @@ $.component('api-demo', {
|
|
|
58
58
|
<p class="subtitle">Fetching data with <code>$.get()</code>. Directives: <code>z-if</code>, <code>z-show</code>, <code>z-for</code>, <code>z-text</code>.</p>
|
|
59
59
|
</div>
|
|
60
60
|
|
|
61
|
-
<div class="card card-error" z-show="error"><p
|
|
61
|
+
<div class="card card-error" z-show="error"><p><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:16px;height:16px;vertical-align:-3px;margin-right:0.15rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/></svg> <span z-text="error"></span></p></div>
|
|
62
62
|
<div class="loading-bar" z-show="loading"></div>
|
|
63
63
|
|
|
64
64
|
<div z-if="!selectedUser">
|
|
@@ -216,13 +216,6 @@
|
|
|
216
216
|
font-weight: 500;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
@keyframes slide-in {
|
|
220
|
-
from { opacity: 0; transform: translateY(-6px); }
|
|
221
|
-
to { opacity: 1; transform: translateY(0); }
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/* -- Animation -- */
|
|
226
219
|
@keyframes slide-in {
|
|
227
220
|
from { opacity: 0; transform: translateY(-6px); }
|
|
228
221
|
to { opacity: 1; transform: translateY(0); }
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
|
|
70
70
|
<!-- Contact count -->
|
|
71
|
-
<div class="card" z-if="contacts.length > 0">
|
|
71
|
+
<div class="card" z-if="contacts.length > 0" z-key="contacts-list">
|
|
72
72
|
<!-- Contacts list — z-for renders each item -->
|
|
73
73
|
<ul class="contacts-list">
|
|
74
74
|
<li
|
|
@@ -102,14 +102,14 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
104
|
<!-- Empty state -->
|
|
105
|
-
<div class="card" z-else>
|
|
105
|
+
<div class="card" z-else z-key="contacts-empty">
|
|
106
106
|
<div class="empty-state">
|
|
107
107
|
<p>No contacts yet — add one above!</p>
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<!-- Selected contact detail panel — z-if conditional rendering -->
|
|
112
|
-
<div class="card contact-detail" z-if="selectedId !== null">
|
|
112
|
+
<div class="card contact-detail" z-if="selectedId !== null" z-key="contact-detail">
|
|
113
113
|
<div class="detail-header">
|
|
114
114
|
<div>
|
|
115
115
|
<h3 z-text="selectedName"></h3>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// scripts/components/home.js — dashboard / landing page
|
|
2
|
+
//
|
|
3
|
+
// Demonstrates: $.component, state, render, mounted lifecycle,
|
|
4
|
+
// signal + computed + effect (reactive primitives),
|
|
5
|
+
// $.store integration, $.bus, template rendering
|
|
6
|
+
|
|
7
|
+
$.component('home-page', {
|
|
8
|
+
state: () => ({
|
|
9
|
+
greeting: '',
|
|
10
|
+
signalDemo: 0,
|
|
11
|
+
}),
|
|
12
|
+
|
|
13
|
+
mounted() {
|
|
14
|
+
// $.signal() — fine-grained reactive primitive
|
|
15
|
+
const count = $.signal(0);
|
|
16
|
+
|
|
17
|
+
// $.computed() — derived reactive value that auto-updates
|
|
18
|
+
const doubled = $.computed(() => count.value * 2);
|
|
19
|
+
|
|
20
|
+
// $.effect() — runs whenever its dependencies change
|
|
21
|
+
$.effect(() => {
|
|
22
|
+
this.state.signalDemo = doubled.value;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Store the signal setter so the button can use it
|
|
26
|
+
this._signalCount = count;
|
|
27
|
+
|
|
28
|
+
// Greet based on time of day
|
|
29
|
+
const hour = new Date().getHours();
|
|
30
|
+
this.state.greeting = hour < 12 ? 'Good morning'
|
|
31
|
+
: hour < 18 ? 'Good afternoon'
|
|
32
|
+
: 'Good evening';
|
|
33
|
+
|
|
34
|
+
// Track page visit via the global store
|
|
35
|
+
const store = $.getStore('main');
|
|
36
|
+
store.dispatch('incrementVisits');
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
incrementSignal() {
|
|
40
|
+
if (this._signalCount) {
|
|
41
|
+
this._signalCount.value++;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
render() {
|
|
46
|
+
const store = $.getStore('main');
|
|
47
|
+
return `
|
|
48
|
+
<div class="page-header">
|
|
49
|
+
<h1>${this.state.greeting}</h1>
|
|
50
|
+
<p class="subtitle">Welcome to your new <strong>zQuery</strong> app. Explore the pages to see different features in action.</p>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="card-grid">
|
|
54
|
+
<div class="card card-accent">
|
|
55
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"/></svg> Reactive Signals</h3>
|
|
56
|
+
<p>Fine-grained reactivity with <code>signal()</code>, <code>computed()</code>, and <code>effect()</code>.</p>
|
|
57
|
+
<div class="signal-demo">
|
|
58
|
+
<span class="signal-value">Doubled: ${this.state.signalDemo}</span>
|
|
59
|
+
<button class="btn btn-sm" @click="incrementSignal">Increment Signal</button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="card">
|
|
64
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5"/></svg> Counter</h3>
|
|
65
|
+
<p><code>computed</code> properties, <code>watch</code> callbacks, and <code>z-for</code> with <code>z-key</code> diffing.</p>
|
|
66
|
+
<a z-link="/counter" class="btn btn-outline">Try It →</a>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="card">
|
|
70
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg> Todos</h3>
|
|
71
|
+
<p>Global store, <code>z-key</code> keyed lists, DOM diffing. <strong>${store.getters.todoCount}</strong> items, <strong>${store.getters.doneCount}</strong> done.</p>
|
|
72
|
+
<a z-link="/todos" class="btn btn-outline">Try It →</a>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="card">
|
|
76
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Zm6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0Zm1.294 6.336a6.721 6.721 0 0 1-3.17.789 6.721 6.721 0 0 1-3.168-.789 3.376 3.376 0 0 1 6.338 0Z"/></svg> Contacts</h3>
|
|
77
|
+
<p>External templates, scoped styles, and <code>z-key</code> keyed lists. <strong>${store.getters.contactCount}</strong> contacts, <strong>${store.getters.favoriteCount}</strong> ★ favorited.</p>
|
|
78
|
+
<a z-link="/contacts" class="btn btn-outline">Try It →</a>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="card">
|
|
82
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418"/></svg> API Demo</h3>
|
|
83
|
+
<p>Fetch data with <code>$.get()</code>, loading states, and <code>$.escapeHtml()</code>.</p>
|
|
84
|
+
<a z-link="/api" class="btn btn-outline">Try It →</a>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="card card-muted">
|
|
89
|
+
<h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z"/></svg> App Stats</h3>
|
|
90
|
+
<div class="stats-grid">
|
|
91
|
+
<div class="stat-group">
|
|
92
|
+
<span class="stat-group-title"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:14px;height:14px;vertical-align:-2px;margin-right:0.2rem;"><path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955a1.126 1.126 0 0 1 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/></svg> General</span>
|
|
93
|
+
<div class="stat-group-values">
|
|
94
|
+
<div class="stat">
|
|
95
|
+
<span class="stat-value">${store.state.visits}</span>
|
|
96
|
+
<span class="stat-label">Page Views</span>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="stat-group">
|
|
102
|
+
<span class="stat-group-title"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:14px;height:14px;vertical-align:-2px;margin-right:0.2rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg> Todos</span>
|
|
103
|
+
<div class="stat-group-values">
|
|
104
|
+
<div class="stat">
|
|
105
|
+
<span class="stat-value">${store.getters.todoCount}</span>
|
|
106
|
+
<span class="stat-label">Total</span>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="stat">
|
|
109
|
+
<span class="stat-value">${store.getters.pendingCount}</span>
|
|
110
|
+
<span class="stat-label">Pending</span>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="stat">
|
|
113
|
+
<span class="stat-value">${store.getters.doneCount}</span>
|
|
114
|
+
<span class="stat-label">Done</span>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="stat-group">
|
|
120
|
+
<span class="stat-group-title"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width:14px;height:14px;vertical-align:-2px;margin-right:0.2rem;"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Zm6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0Zm1.294 6.336a6.721 6.721 0 0 1-3.17.789 6.721 6.721 0 0 1-3.168-.789 3.376 3.376 0 0 1 6.338 0Z"/></svg> Contacts</span>
|
|
121
|
+
<div class="stat-group-values">
|
|
122
|
+
<div class="stat">
|
|
123
|
+
<span class="stat-value">${store.getters.contactCount}</span>
|
|
124
|
+
<span class="stat-label">Total</span>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="stat">
|
|
127
|
+
<span class="stat-value">${store.getters.favoriteCount}</span>
|
|
128
|
+
<span class="stat-label">★ Favorited</span>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<small class="muted">Stats powered by <code>$.store()</code> getters — visit count tracked globally.</small>
|
|
134
|
+
</div>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// app/store.js — global state management
|
|
2
2
|
//
|
|
3
3
|
// $.store() creates a centralized store with state, actions, and getters.
|
|
4
4
|
// Components can dispatch actions and subscribe to changes.
|
|
@@ -11,11 +11,11 @@ export const store = $.store('main', {
|
|
|
11
11
|
|
|
12
12
|
// Contacts
|
|
13
13
|
contacts: [
|
|
14
|
-
{ id: 1, name: '
|
|
15
|
-
{ id: 2, name: '
|
|
16
|
-
{ id: 3, name: '
|
|
17
|
-
{ id: 4, name: '
|
|
18
|
-
{ id: 5, name: '
|
|
14
|
+
{ id: 1, name: 'Tony Wiedman', email: 'tony@z-query.com', role: 'Developer', status: 'online', favorite: true },
|
|
15
|
+
{ id: 2, name: 'Robert Baratheon', email: 'robert@stormlands.io', role: 'Manager', status: 'offline', favorite: false },
|
|
16
|
+
{ id: 3, name: 'Terry A. Davis', email: 'terry@templeos.net', role: 'Developer', status: 'online', favorite: true },
|
|
17
|
+
{ id: 4, name: 'Trevor Moore', email: 'trevor@wkuk.tv', role: 'Designer', status: 'away', favorite: false },
|
|
18
|
+
{ id: 5, name: 'Carlo Acutis', email: 'carlo@vatican.va', role: 'Developer', status: 'online', favorite: false },
|
|
19
19
|
],
|
|
20
20
|
contactsAdded: 0,
|
|
21
21
|
},
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* global.css — responsive scaffold styles
|
|
2
2
|
*
|
|
3
3
|
* Uses CSS custom properties for easy theming.
|
|
4
4
|
* Dark theme by default, light theme via [data-theme="light"].
|
|
@@ -103,6 +103,7 @@ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-s
|
|
|
103
103
|
transition: all 0.15s ease;
|
|
104
104
|
border-left: 3px solid transparent;
|
|
105
105
|
cursor: pointer;
|
|
106
|
+
user-select: none;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
.nav-link:hover {
|
|
@@ -116,7 +117,8 @@ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-s
|
|
|
116
117
|
border-left-color: var(--accent);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
.nav-icon {
|
|
120
|
+
.nav-icon { display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; }
|
|
121
|
+
.nav-icon svg { width: 18px; height: 18px; flex-shrink: 0; }
|
|
120
122
|
|
|
121
123
|
.sidebar-footer {
|
|
122
124
|
padding: 0.75rem 1.25rem;
|
package/cli/scaffold/index.html
CHANGED
|
@@ -5,35 +5,36 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{{NAME}}</title>
|
|
7
7
|
<base href="/">
|
|
8
|
-
<link rel="stylesheet" href="
|
|
9
|
-
<
|
|
10
|
-
<script
|
|
8
|
+
<link rel="stylesheet" href="global.css">
|
|
9
|
+
<link rel="icon" type="image/png" href="favicon.ico">
|
|
10
|
+
<script src="zquery.min.js"></script>
|
|
11
|
+
<script type="module" src="app/app.js"></script>
|
|
11
12
|
</head>
|
|
12
13
|
<body>
|
|
13
14
|
|
|
14
15
|
<!-- Sidebar Navigation -->
|
|
15
16
|
<aside class="sidebar" id="sidebar">
|
|
16
17
|
<div class="sidebar-header">
|
|
17
|
-
<span class="brand"
|
|
18
|
+
<span class="brand"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--accent)" style="width:18px;height:18px;vertical-align:-3px;margin-right:0.2rem;"><path fill-rule="evenodd" d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" clip-rule="evenodd"/></svg> {{NAME}}</span>
|
|
18
19
|
</div>
|
|
19
20
|
<nav class="sidebar-nav">
|
|
20
21
|
<a z-link="/" class="nav-link">
|
|
21
|
-
<span class="nav-icon"
|
|
22
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955a1.126 1.126 0 0 1 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/></svg></span> Home
|
|
22
23
|
</a>
|
|
23
24
|
<a z-link="/counter" class="nav-link">
|
|
24
|
-
<span class="nav-icon"
|
|
25
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5"/></svg></span> Counter
|
|
25
26
|
</a>
|
|
26
27
|
<a z-link="/todos" class="nav-link">
|
|
27
|
-
<span class="nav-icon"
|
|
28
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg></span> Todos
|
|
28
29
|
</a>
|
|
29
30
|
<a z-link="/contacts" class="nav-link">
|
|
30
|
-
<span class="nav-icon"
|
|
31
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Zm6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0Zm1.294 6.336a6.721 6.721 0 0 1-3.17.789 6.721 6.721 0 0 1-3.168-.789 3.376 3.376 0 0 1 6.338 0Z"/></svg></span> Contacts
|
|
31
32
|
</a>
|
|
32
33
|
<a z-link="/api" class="nav-link">
|
|
33
|
-
<span class="nav-icon"
|
|
34
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418"/></svg></span> API Demo
|
|
34
35
|
</a>
|
|
35
36
|
<a z-link="/about" class="nav-link">
|
|
36
|
-
<span class="nav-icon"
|
|
37
|
+
<span class="nav-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"/></svg></span> About
|
|
37
38
|
</a>
|
|
38
39
|
</nav>
|
|
39
40
|
<div class="sidebar-footer">
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
<button class="hamburger" id="menu-toggle" aria-label="Toggle menu">
|
|
47
48
|
<span></span><span></span><span></span>
|
|
48
49
|
</button>
|
|
49
|
-
<span class="topbar-brand"
|
|
50
|
+
<span class="topbar-brand"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--accent)" style="width:16px;height:16px;vertical-align:-2px;margin-right:0.15rem;"><path fill-rule="evenodd" d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" clip-rule="evenodd"/></svg> {{NAME}}</span>
|
|
50
51
|
</header>
|
|
51
52
|
|
|
52
53
|
<!-- Overlay for mobile menu -->
|
package/cli/utils.js
CHANGED
|
@@ -83,15 +83,120 @@ function stripComments(code) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// ---------------------------------------------------------------------------
|
|
86
|
-
// minify —
|
|
86
|
+
// minify — single-pass minification
|
|
87
|
+
// Strips comments, collapses whitespace to the minimum required,
|
|
88
|
+
// and preserves string / template-literal / regex content verbatim.
|
|
87
89
|
// ---------------------------------------------------------------------------
|
|
88
90
|
|
|
89
91
|
function minify(code, banner) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
return banner + '\n' + _minifyBody(code.replace(banner, ''));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Single-pass minifier: walks character-by-character, skips strings/regex,
|
|
97
|
+
* strips comments, and emits a space only when both neighbours are
|
|
98
|
+
* identifier-like characters (or when collapsing would create ++, --, // or /*).
|
|
99
|
+
*/
|
|
100
|
+
function _minifyBody(code) {
|
|
101
|
+
let out = '';
|
|
102
|
+
let i = 0;
|
|
103
|
+
|
|
104
|
+
while (i < code.length) {
|
|
105
|
+
const ch = code[i];
|
|
106
|
+
const nx = code[i + 1];
|
|
107
|
+
|
|
108
|
+
// ── String / template literal: copy verbatim ────────────────
|
|
109
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
110
|
+
const q = ch;
|
|
111
|
+
out += ch; i++;
|
|
112
|
+
while (i < code.length) {
|
|
113
|
+
if (code[i] === '\\') { out += code[i] + (code[i + 1] || ''); i += 2; continue; }
|
|
114
|
+
out += code[i];
|
|
115
|
+
if (code[i] === q) { i++; break; }
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Block comment: skip ─────────────────────────────────────
|
|
122
|
+
if (ch === '/' && nx === '*') {
|
|
123
|
+
i += 2;
|
|
124
|
+
while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) i++;
|
|
125
|
+
i += 2;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── Line comment: skip ──────────────────────────────────────
|
|
130
|
+
if (ch === '/' && nx === '/') {
|
|
131
|
+
i += 2;
|
|
132
|
+
while (i < code.length && code[i] !== '\n') i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Regex literal: copy verbatim ────────────────────────────
|
|
137
|
+
if (ch === '/') {
|
|
138
|
+
if (_isRegexCtx(out)) {
|
|
139
|
+
out += ch; i++;
|
|
140
|
+
let inCC = false;
|
|
141
|
+
while (i < code.length) {
|
|
142
|
+
const rc = code[i];
|
|
143
|
+
if (rc === '\\') { out += rc + (code[i + 1] || ''); i += 2; continue; }
|
|
144
|
+
if (rc === '[') inCC = true;
|
|
145
|
+
if (rc === ']') inCC = false;
|
|
146
|
+
out += rc; i++;
|
|
147
|
+
if (rc === '/' && !inCC) {
|
|
148
|
+
while (i < code.length && /[gimsuy]/.test(code[i])) { out += code[i]; i++; }
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Whitespace: collapse ────────────────────────────────────
|
|
157
|
+
if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
|
|
158
|
+
while (i < code.length && (code[i] === ' ' || code[i] === '\t' || code[i] === '\n' || code[i] === '\r')) i++;
|
|
159
|
+
const before = out[out.length - 1];
|
|
160
|
+
const after = code[i];
|
|
161
|
+
if (_needsSpace(before, after)) out += ' ';
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
out += ch;
|
|
166
|
+
i++;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** True when removing the space between a and b would change semantics. */
|
|
173
|
+
function _needsSpace(a, b) {
|
|
174
|
+
if (!a || !b) return false;
|
|
175
|
+
const idA = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9') || a === '_' || a === '$';
|
|
176
|
+
const idB = (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b === '_' || b === '$';
|
|
177
|
+
if (idA && idB) return true; // e.g. const x, return value
|
|
178
|
+
if (a === '+' && b === '+') return true; // prevent ++
|
|
179
|
+
if (a === '-' && b === '-') return true; // prevent --
|
|
180
|
+
if (a === '/' && (b === '/' || b === '*')) return true; // prevent // or /*
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Heuristic: is the next '/' a regex start (vs division)? */
|
|
185
|
+
function _isRegexCtx(out) {
|
|
186
|
+
let end = out.length - 1;
|
|
187
|
+
while (end >= 0 && out[end] === ' ') end--;
|
|
188
|
+
if (end < 0) return true;
|
|
189
|
+
const last = out[end];
|
|
190
|
+
if ('=({[,;:!&|?~+-*/%^>'.includes(last)) return true;
|
|
191
|
+
const tail = out.substring(Math.max(0, end - 7), end + 1);
|
|
192
|
+
const kws = ['return', 'typeof', 'case', 'in', 'delete', 'void', 'throw', 'new'];
|
|
193
|
+
for (const kw of kws) {
|
|
194
|
+
if (tail.endsWith(kw)) {
|
|
195
|
+
const pos = end - kw.length;
|
|
196
|
+
if (pos < 0 || !((out[pos] >= 'a' && out[pos] <= 'z') || (out[pos] >= 'A' && out[pos] <= 'Z') || (out[pos] >= '0' && out[pos] <= '9') || out[pos] === '_' || out[pos] === '$')) return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
95
200
|
}
|
|
96
201
|
|
|
97
202
|
// ---------------------------------------------------------------------------
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|