zero-query 0.7.5 → 0.8.7

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 (65) hide show
  1. package/README.md +39 -30
  2. package/cli/commands/build.js +110 -1
  3. package/cli/commands/bundle.js +127 -50
  4. package/cli/commands/create.js +1 -1
  5. package/cli/commands/dev/devtools/index.js +56 -0
  6. package/cli/commands/dev/devtools/js/components.js +49 -0
  7. package/cli/commands/dev/devtools/js/core.js +409 -0
  8. package/cli/commands/dev/devtools/js/elements.js +413 -0
  9. package/cli/commands/dev/devtools/js/network.js +166 -0
  10. package/cli/commands/dev/devtools/js/performance.js +73 -0
  11. package/cli/commands/dev/devtools/js/router.js +105 -0
  12. package/cli/commands/dev/devtools/js/source.js +132 -0
  13. package/cli/commands/dev/devtools/js/stats.js +35 -0
  14. package/cli/commands/dev/devtools/js/tabs.js +79 -0
  15. package/cli/commands/dev/devtools/panel.html +95 -0
  16. package/cli/commands/dev/devtools/styles.css +244 -0
  17. package/cli/commands/dev/index.js +28 -3
  18. package/cli/commands/dev/logger.js +6 -1
  19. package/cli/commands/dev/overlay.js +377 -0
  20. package/cli/commands/dev/server.js +8 -0
  21. package/cli/commands/dev/watcher.js +26 -1
  22. package/cli/help.js +8 -5
  23. package/cli/scaffold/{scripts → app}/app.js +1 -1
  24. package/cli/scaffold/{scripts → app}/components/about.js +4 -4
  25. package/cli/scaffold/{scripts → app}/components/api-demo.js +1 -1
  26. package/cli/scaffold/app/components/home.js +137 -0
  27. package/cli/scaffold/{scripts → app}/routes.js +1 -1
  28. package/cli/scaffold/{scripts → app}/store.js +6 -6
  29. package/cli/scaffold/assets/.gitkeep +0 -0
  30. package/cli/scaffold/{styles/styles.css → global.css} +3 -2
  31. package/cli/scaffold/index.html +11 -11
  32. package/dist/zquery.dist.zip +0 -0
  33. package/dist/zquery.js +740 -226
  34. package/dist/zquery.min.js +2 -2
  35. package/index.d.ts +11 -11
  36. package/index.js +15 -10
  37. package/package.json +3 -2
  38. package/src/component.js +154 -139
  39. package/src/core.js +57 -11
  40. package/src/diff.js +256 -58
  41. package/src/expression.js +33 -3
  42. package/src/reactive.js +37 -5
  43. package/src/router.js +196 -7
  44. package/src/ssr.js +1 -1
  45. package/tests/component.test.js +582 -0
  46. package/tests/core.test.js +251 -0
  47. package/tests/diff.test.js +333 -2
  48. package/tests/expression.test.js +148 -0
  49. package/tests/http.test.js +108 -0
  50. package/tests/reactive.test.js +148 -0
  51. package/tests/router.test.js +317 -0
  52. package/tests/store.test.js +126 -0
  53. package/tests/utils.test.js +161 -2
  54. package/types/collection.d.ts +17 -2
  55. package/types/component.d.ts +10 -34
  56. package/types/misc.d.ts +13 -0
  57. package/types/router.d.ts +30 -1
  58. package/cli/commands/dev.old.js +0 -520
  59. package/cli/scaffold/scripts/components/home.js +0 -137
  60. /package/cli/scaffold/{scripts → app}/components/contacts/contacts.css +0 -0
  61. /package/cli/scaffold/{scripts → app}/components/contacts/contacts.html +0 -0
  62. /package/cli/scaffold/{scripts → app}/components/contacts/contacts.js +0 -0
  63. /package/cli/scaffold/{scripts → app}/components/counter.js +0 -0
  64. /package/cli/scaffold/{scripts → app}/components/not-found.js +0 -0
  65. /package/cli/scaffold/{scripts → app}/components/todos.js +0 -0
@@ -360,6 +360,383 @@ const OVERLAY_SCRIPT = `<script>
360
360
  }
361
361
 
362
362
  connect();
363
+
364
+ // =====================================================================
365
+ // Fetch / $.http Interceptor — pretty console logging
366
+ // =====================================================================
367
+ var __zqChannel;
368
+ try { __zqChannel = new BroadcastChannel('__zq_devtools'); } catch(e) {}
369
+
370
+ var __zqRequests = [];
371
+ var __zqMorphEvents = [];
372
+ var __zqMorphCount = 0;
373
+ var __zqRenderCount = 0;
374
+ var __zqReqId = 0;
375
+ var _origFetch = window.fetch;
376
+
377
+ window.fetch = function(input, init) {
378
+ var url = typeof input === 'string' ? input : (input && input.url ? input.url : String(input));
379
+ var method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
380
+ var id = ++__zqReqId;
381
+ var start = performance.now();
382
+
383
+ // Skip internal dev-server requests
384
+ if (url.indexOf('__zq_') !== -1 || url.indexOf('/_devtools') !== -1) {
385
+ return _origFetch.apply(this, arguments);
386
+ }
387
+
388
+ return _origFetch.apply(this, arguments).then(function(response) {
389
+ var elapsed = Math.round(performance.now() - start);
390
+ var status = response.status;
391
+ var cloned = response.clone();
392
+
393
+ cloned.text().then(function(bodyText) {
394
+ var entry = {
395
+ id: id, method: method, url: url, status: status,
396
+ elapsed: elapsed, bodyPreview: bodyText.slice(0, 5000),
397
+ timestamp: Date.now()
398
+ };
399
+ __zqRequests.push(entry);
400
+ if (__zqRequests.length > 500) __zqRequests.shift();
401
+ updateDevBar();
402
+
403
+ // Pretty console log
404
+ var isOk = status >= 200 && status < 300;
405
+ var color = isOk ? '#2ecc71' : status < 400 ? '#f39c12' : '#e74c3c';
406
+
407
+ console.groupCollapsed(
408
+ '%c ' + method + ' %c' + status + '%c ' + url + ' %c' + elapsed + 'ms',
409
+ 'background:' + color + ';color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
410
+ 'color:' + color + ';font-weight:700;margin-left:8px',
411
+ 'color:inherit;margin-left:4px',
412
+ 'color:#888;margin-left:8px;font-size:11px'
413
+ );
414
+
415
+ // Response body
416
+ try {
417
+ var parsed = JSON.parse(bodyText);
418
+ console.log('%c Response ', 'background:#1e1e2e;color:#8be9fd;padding:2px 6px;border-radius:2px;font-weight:600', parsed);
419
+ } catch(pe) {
420
+ if (bodyText.length > 0) {
421
+ console.log('%c Response ', 'background:#1e1e2e;color:#8be9fd;padding:2px 6px;border-radius:2px;font-weight:600',
422
+ bodyText.length > 500 ? bodyText.slice(0, 500) + '... (' + bodyText.length + ' chars)' : bodyText);
423
+ }
424
+ }
425
+
426
+ // Headers
427
+ try {
428
+ console.log('%c Headers ', 'background:#1e1e2e;color:#bd93f9;padding:2px 6px;border-radius:2px;font-weight:600',
429
+ Object.fromEntries(response.headers.entries()));
430
+ } catch(he) {}
431
+
432
+ // Request body (if sent)
433
+ if (init && init.body) {
434
+ try {
435
+ console.log('%c Request ', 'background:#1e1e2e;color:#f1fa8c;padding:2px 6px;border-radius:2px;font-weight:600',
436
+ JSON.parse(init.body));
437
+ } catch(re) {
438
+ console.log('%c Request ', 'background:#1e1e2e;color:#f1fa8c;padding:2px 6px;border-radius:2px;font-weight:600',
439
+ String(init.body).slice(0, 500));
440
+ }
441
+ }
442
+
443
+ console.groupEnd();
444
+
445
+ // Broadcast to devtools
446
+ if (__zqChannel) {
447
+ try { __zqChannel.postMessage({ type: 'http', data: entry }); } catch(ce) {}
448
+ }
449
+ }).catch(function() {});
450
+
451
+ return response;
452
+ }, function(err) {
453
+ var elapsed = Math.round(performance.now() - start);
454
+ console.groupCollapsed(
455
+ '%c ' + method + ' %cERR%c ' + url + ' %c' + elapsed + 'ms',
456
+ 'background:#e74c3c;color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
457
+ 'color:#e74c3c;font-weight:700;margin-left:8px',
458
+ 'color:inherit;margin-left:4px',
459
+ 'color:#888;margin-left:8px;font-size:11px'
460
+ );
461
+ console.error(err);
462
+ console.groupEnd();
463
+
464
+ var entry = { id: id, method: method, url: url, status: 0, elapsed: elapsed, bodyPreview: err.message, timestamp: Date.now() };
465
+ __zqRequests.push(entry);
466
+ updateDevBar();
467
+ if (__zqChannel) {
468
+ try { __zqChannel.postMessage({ type: 'http', data: entry }); } catch(ce) {}
469
+ }
470
+
471
+ throw err;
472
+ });
473
+ };
474
+
475
+ // =====================================================================
476
+ // Morph instrumentation — hook via window.__zqMorphHook (set by diff.js)
477
+ // =====================================================================
478
+ window.__zqMorphHook = function(el, elapsed) {
479
+ __zqMorphCount++;
480
+ updateDevBar();
481
+
482
+ var evt = { target: el.id || el.tagName.toLowerCase(), elapsed: elapsed, kind: 'morph', timestamp: Date.now() };
483
+ __zqMorphEvents.push(evt);
484
+ if (__zqMorphEvents.length > 200) __zqMorphEvents.shift();
485
+
486
+ // Console timing for slow morphs (> 4ms)
487
+ if (elapsed > 4) {
488
+ console.log(
489
+ '%c morph %c' + elapsed.toFixed(2) + 'ms%c ' + (el.id || el.tagName.toLowerCase()),
490
+ 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
491
+ 'color:' + (elapsed > 16 ? '#e74c3c' : '#f39c12') + ';font-weight:700;margin-left:8px',
492
+ 'color:#888;margin-left:4px'
493
+ );
494
+ }
495
+
496
+ // Broadcast to devtools
497
+ if (__zqChannel) {
498
+ try {
499
+ __zqChannel.postMessage({
500
+ type: 'morph-detail',
501
+ data: evt
502
+ });
503
+ } catch(ce) {}
504
+ }
505
+ };
506
+
507
+ // =====================================================================
508
+ // Render instrumentation — hook for first-renders & route swaps
509
+ // =====================================================================
510
+ window.__zqRenderHook = function(el, elapsed, kind, name) {
511
+ __zqRenderCount++;
512
+ __zqMorphCount++; // count renders in the morph total for the toolbar
513
+ updateDevBar();
514
+
515
+ var evt = { target: name || el.id || el.tagName.toLowerCase(), elapsed: elapsed, kind: kind, timestamp: Date.now() };
516
+ __zqMorphEvents.push(evt);
517
+ if (__zqMorphEvents.length > 200) __zqMorphEvents.shift();
518
+
519
+ // Console log for route/mount renders
520
+ var label = kind === 'route' ? ' route ' : ' mount ';
521
+ var bg = kind === 'route' ? '#d29922' : '#3fb950';
522
+ console.log(
523
+ '%c' + label + '%c' + elapsed.toFixed(2) + 'ms%c ' + (name || el.id || el.tagName.toLowerCase()),
524
+ 'background:' + bg + ';color:#fff;padding:2px 6px;border-radius:3px;font-weight:700;font-size:11px',
525
+ 'color:' + (elapsed > 16 ? '#e74c3c' : '#888') + ';font-weight:700;margin-left:8px',
526
+ 'color:#888;margin-left:4px'
527
+ );
528
+
529
+ // Broadcast to devtools
530
+ if (__zqChannel) {
531
+ try {
532
+ __zqChannel.postMessage({
533
+ type: 'render-detail',
534
+ data: evt
535
+ });
536
+ } catch(ce) {}
537
+ }
538
+ };
539
+
540
+ // =====================================================================
541
+ // Router instrumentation — history state tracking for devtools
542
+ // =====================================================================
543
+ var __zqRouterEvents = [];
544
+
545
+ var _origPushState = history.pushState;
546
+ history.pushState = function(state, title, url) {
547
+ _origPushState.apply(this, arguments);
548
+ var isSubstate = state && state.__zq === 'substate';
549
+ var evt = {
550
+ action: isSubstate ? 'substate' : 'navigate',
551
+ url: String(url || location.href).replace(location.origin, ''),
552
+ key: isSubstate ? state.key : null,
553
+ data: isSubstate ? state.data : null,
554
+ timestamp: Date.now()
555
+ };
556
+ __zqRouterEvents.push(evt);
557
+ if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
558
+ if (__zqChannel) {
559
+ try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
560
+ }
561
+ };
562
+
563
+ var _origReplaceState = history.replaceState;
564
+ history.replaceState = function(state, title, url) {
565
+ _origReplaceState.apply(this, arguments);
566
+ var isSubstate = state && state.__zq === 'substate';
567
+ var evt = {
568
+ action: 'replace',
569
+ url: String(url || location.href).replace(location.origin, ''),
570
+ key: isSubstate ? state.key : null,
571
+ data: isSubstate ? state.data : null,
572
+ timestamp: Date.now()
573
+ };
574
+ __zqRouterEvents.push(evt);
575
+ if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
576
+ if (__zqChannel) {
577
+ try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
578
+ }
579
+ };
580
+
581
+ window.addEventListener('popstate', function(e) {
582
+ var state = e.state;
583
+ var isSubstate = state && state.__zq === 'substate';
584
+ var evt = {
585
+ action: isSubstate ? 'pop-substate' : 'pop',
586
+ url: location.pathname + location.hash,
587
+ key: isSubstate ? state.key : null,
588
+ data: isSubstate ? state.data : null,
589
+ timestamp: Date.now()
590
+ };
591
+ __zqRouterEvents.push(evt);
592
+ if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
593
+ if (__zqChannel) {
594
+ try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
595
+ }
596
+ });
597
+
598
+ window.addEventListener('hashchange', function() {
599
+ var evt = {
600
+ action: 'hashchange',
601
+ url: location.hash,
602
+ timestamp: Date.now()
603
+ };
604
+ __zqRouterEvents.push(evt);
605
+ if (__zqRouterEvents.length > 200) __zqRouterEvents.shift();
606
+ if (__zqChannel) {
607
+ try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
608
+ }
609
+ });
610
+
611
+ // =====================================================================
612
+ // Dev Toolbar — floating bar with DOM viewer button & request counter
613
+ // =====================================================================
614
+ var devBar;
615
+
616
+ function createDevBar() {
617
+ devBar = document.createElement('div');
618
+ devBar.id = '__zq_devbar';
619
+ devBar.setAttribute('style',
620
+ 'position:fixed;bottom:12px;right:12px;z-index:2147483646;' +
621
+ 'display:flex;align-items:center;gap:6px;' +
622
+ 'background:rgba(22,27,34,0.92);border:1px solid rgba(48,54,61,0.8);' +
623
+ 'border-radius:8px;padding:4px 6px;backdrop-filter:blur(8px);' +
624
+ 'font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;' +
625
+ 'font-size:11px;color:#8b949e;user-select:none;cursor:default;' +
626
+ 'box-shadow:0 4px 12px rgba(0,0,0,0.4);'
627
+ );
628
+ devBar.innerHTML =
629
+ '<span style="color:#58a6ff;font-weight:700;padding:0 4px;font-size:10px;letter-spacing:.5px">zQ</span>' +
630
+ '<span id="__zq_bar_reqs" title="Network requests" style="padding:2px 6px;border-radius:4px;' +
631
+ 'background:rgba(88,166,255,0.1);color:#58a6ff;cursor:pointer;font-size:10px;font-weight:600;">0 req</span>' +
632
+ '<span id="__zq_bar_morphs" title="Render operations" style="padding:2px 6px;border-radius:4px;' +
633
+ 'background:rgba(188,140,255,0.1);color:#bc8cff;cursor:pointer;font-size:10px;font-weight:600;">0 render</span>' +
634
+ '<button id="__zq_bar_dom" title="Open DevTools (/_devtools)" style="' +
635
+ 'padding:3px 8px;border-radius:4px;font-size:10px;font-weight:700;' +
636
+ 'background:rgba(63,185,80,0.15);color:#3fb950;border:1px solid rgba(63,185,80,0.3);' +
637
+ 'cursor:pointer;font-family:inherit;transition:all .15s;' +
638
+ '">DOM</button>' +
639
+ '<button id="__zq_bar_close" title="Close toolbar" style="' +
640
+ 'padding:0 4px;color:#484f58;cursor:pointer;font-size:14px;border:none;' +
641
+ 'background:none;font-family:inherit;line-height:1;' +
642
+ '">&times;</button>';
643
+
644
+ document.body.appendChild(devBar);
645
+ updateDevBar();
646
+
647
+ // Check if we're inside a devtools split-view iframe
648
+ function isInSplitFrame() {
649
+ try { return window.parent !== window && window.parent.document.getElementById('app-frame'); }
650
+ catch(e) { return false; }
651
+ }
652
+
653
+ // Switch tab in devtools (works for both split iframe and popup)
654
+ function switchDevTab(tab) {
655
+ if (__zqChannel) {
656
+ __zqChannel.postMessage({ type: 'switch-tab', tab: tab });
657
+ }
658
+ }
659
+
660
+ // Req counter → Network tab
661
+ document.getElementById('__zq_bar_reqs').addEventListener('click', function() {
662
+ if (isInSplitFrame()) {
663
+ switchDevTab('network');
664
+ } else {
665
+ openDevToolsPopup('network');
666
+ }
667
+ });
668
+
669
+ // Render counter → Performance tab
670
+ document.getElementById('__zq_bar_morphs').addEventListener('click', function() {
671
+ if (isInSplitFrame()) {
672
+ switchDevTab('perf');
673
+ } else {
674
+ openDevToolsPopup('perf');
675
+ }
676
+ });
677
+
678
+ // DOM button → Elements tab (in split) or open popup
679
+ var __zqPopup = null;
680
+ function openDevToolsPopup(tab) {
681
+ // If popup is already open, just switch the tab via BroadcastChannel
682
+ if (__zqPopup && !__zqPopup.closed) {
683
+ switchDevTab(tab);
684
+ __zqPopup.focus();
685
+ return;
686
+ }
687
+ var w = 1080, h = 800;
688
+ var left = window.screenX + window.outerWidth - w - 20;
689
+ var top = window.screenY + 60;
690
+ var url = '/_devtools' + (tab ? '#' + tab : '');
691
+ __zqPopup = window.open(url, '__zq_devtools',
692
+ 'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top +
693
+ ',resizable=yes,scrollbars=yes');
694
+ }
695
+
696
+ document.getElementById('__zq_bar_dom').addEventListener('click', function() {
697
+ if (isInSplitFrame()) {
698
+ switchDevTab('dom');
699
+ } else {
700
+ openDevToolsPopup('dom');
701
+ }
702
+ });
703
+
704
+ // Close button
705
+ document.getElementById('__zq_bar_close').addEventListener('click', function() {
706
+ devBar.style.display = 'none';
707
+ });
708
+
709
+ // Hover effects
710
+ document.getElementById('__zq_bar_dom').addEventListener('mouseover', function() {
711
+ this.style.background = 'rgba(63,185,80,0.3)';
712
+ });
713
+ document.getElementById('__zq_bar_dom').addEventListener('mouseout', function() {
714
+ this.style.background = 'rgba(63,185,80,0.15)';
715
+ });
716
+ }
717
+
718
+ function updateDevBar() {
719
+ if (!devBar) return;
720
+ var reqEl = document.getElementById('__zq_bar_reqs');
721
+ var morphEl = document.getElementById('__zq_bar_morphs');
722
+ if (reqEl) reqEl.textContent = __zqRequests.length + ' req';
723
+ if (morphEl) morphEl.textContent = __zqMorphCount + ' render';
724
+ }
725
+
726
+ // Expose for devtools popup
727
+ window.__zqDevTools = {
728
+ get requests() { return __zqRequests; },
729
+ get morphEvents() { return __zqMorphEvents; },
730
+ get morphCount() { return __zqMorphCount; },
731
+ get renderCount() { return __zqRenderCount; },
732
+ get routerEvents() { return __zqRouterEvents; }
733
+ };
734
+
735
+ if (document.readyState === 'loading') {
736
+ document.addEventListener('DOMContentLoaded', createDevBar);
737
+ } else {
738
+ createDevBar();
739
+ }
363
740
  })();
364
741
  </script>`;
365
742
 
@@ -11,6 +11,7 @@
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
13
  const OVERLAY_SCRIPT = require('./overlay');
14
+ const DEVTOOLS_HTML = require('./devtools');
14
15
 
15
16
  // ---------------------------------------------------------------------------
16
17
  // SSE client pool
@@ -102,6 +103,13 @@ async function createServer({ root, htmlEntry, port, noIntercept }) {
102
103
  pool.add(sse);
103
104
  });
104
105
 
106
+ // ---- DevTools panel ----
107
+ app.get('/_devtools', (req, res) => {
108
+ res.set('Content-Type', 'text/html; charset=utf-8');
109
+ res.set('Cache-Control', 'no-cache');
110
+ res.send(DEVTOOLS_HTML);
111
+ });
112
+
105
113
  // ---- Auto-resolve zquery.min.js ----
106
114
  const pkgRoot = path.resolve(__dirname, '..', '..', '..');
107
115
 
@@ -60,12 +60,13 @@ function collectWatchDirs(dir) {
60
60
  * @param {SSEPool} opts.pool — SSE broadcast pool
61
61
  * @returns {{ dirs: string[], destroy: Function }}
62
62
  */
63
- function startWatcher({ root, pool }) {
63
+ function startWatcher({ root, pool, bundleMode, serveRoot }) {
64
64
  const watchDirs = collectWatchDirs(root);
65
65
  const watchers = [];
66
66
 
67
67
  let debounceTimer;
68
68
  let currentError = null; // track which file has an active error
69
+ let bundleTimer = null; // debounce for bundle rebuilds
69
70
 
70
71
  // Track file mtimes so we only react to genuine writes.
71
72
  // On Windows, fs.watch fires on reads/access too, which causes
@@ -128,6 +129,30 @@ function startWatcher({ root, pool }) {
128
129
  }
129
130
 
130
131
  // ---- Full reload ----
132
+ if (bundleMode) {
133
+ // Debounce bundle rebuilds (500ms) so rapid saves don't spam
134
+ clearTimeout(bundleTimer);
135
+ bundleTimer = setTimeout(() => {
136
+ try {
137
+ const bundleFn = require('../bundle');
138
+ const { args: cliArgs } = require('../../args');
139
+ const savedArgs = cliArgs.slice();
140
+ cliArgs.length = 0;
141
+ cliArgs.push('bundle');
142
+ const prevCwd = process.cwd();
143
+ process.chdir(root);
144
+ bundleFn();
145
+ process.chdir(prevCwd);
146
+ cliArgs.length = 0;
147
+ savedArgs.forEach(a => cliArgs.push(a));
148
+ logReload(rel + ' (rebuilt)');
149
+ pool.broadcast('reload', rel);
150
+ } catch (err) {
151
+ console.error(' Bundle rebuild failed:', err.message);
152
+ }
153
+ }, 500);
154
+ return;
155
+ }
131
156
  logReload(rel);
132
157
  pool.broadcast('reload', rel);
133
158
  }, 100);
package/cli/help.js CHANGED
@@ -7,7 +7,7 @@ function showHelp() {
7
7
  COMMANDS
8
8
 
9
9
  create [dir] Scaffold a new zQuery project
10
- Creates index.html, scripts/, styles/ in the target directory
10
+ Creates index.html, global.css, app/, assets/ in the target directory
11
11
  (defaults to the current directory)
12
12
 
13
13
  dev [root] Start a dev server with live-reload
@@ -15,6 +15,8 @@ function showHelp() {
15
15
  --index, -i <file> Index HTML file (default: index.html)
16
16
  --no-intercept Disable auto-resolution of zquery.min.js
17
17
  (serve the on-disk vendor copy instead)
18
+ --bundle, -b Serve the bundled build (runs bundler first,
19
+ serves from dist/server/, auto-rebuilds on save)
18
20
 
19
21
  Includes error overlay: syntax errors are
20
22
  caught on save and shown as a full-screen
@@ -24,7 +26,8 @@ function showHelp() {
24
26
  bundle [dir|file] Bundle app ES modules into a single file
25
27
  --out, -o <path> Output directory (default: dist/ next to HTML file)
26
28
  --index, -i <file> Index HTML file (default: auto-detected)
27
- --minimal, -m Only output HTML + bundled JS (skip static assets)
29
+ --minimal, -m Only output HTML, bundled JS, and global CSS (skip static assets)
30
+ --global-css <path> Override global CSS input (default: first <link> in HTML)
28
31
 
29
32
  build Build the zQuery library \u2192 dist/ --watch, -w Watch src/ and rebuild on changes (must be run from the project root where src/ lives)
30
33
 
@@ -35,7 +38,7 @@ function showHelp() {
35
38
  1. index.html first, then other .html files
36
39
  2. Within HTML: module script pointing to app.js, else first module script
37
40
  3. JS scan: $.router( first (entry point), then $.mount( / $.store(
38
- 4. Convention fallbacks (scripts/app.js, app.js, etc.)
41
+ 4. Convention fallbacks (app/app.js, scripts/app.js, app.js, etc.)
39
42
  • Passing a directory auto-detects the entry; passing a file uses it directly
40
43
  • zquery.min.js is always embedded (auto-built from source if not found)
41
44
  • HTML file is auto-detected (any .html, not just index.html)
@@ -83,12 +86,12 @@ function showHelp() {
83
86
  zquery bundle my-app/
84
87
 
85
88
  # Pass a direct entry file (skip auto-detection)
86
- zquery bundle my-app/scripts/main.js
89
+ zquery bundle my-app/app/main.js
87
90
 
88
91
  # Custom output directory
89
92
  zquery bundle my-app/ -o build/
90
93
 
91
- # Minimal build (only HTML + bundled JS, no static assets)
94
+ # Minimal build (HTML + JS + global CSS, no static asset copying)
92
95
  zquery bundle my-app/ --minimal
93
96
 
94
97
  # Dev server with a custom index page
@@ -1,4 +1,4 @@
1
- // scripts/app.js — application entry point
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
@@ -31,16 +31,16 @@ $.component('about-page', {
31
31
  </div>
32
32
 
33
33
  <div class="card">
34
- <h3>🎨 Theme</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' ? '☀️ Light Mode' : '🌙 Dark Mode'}</button>
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>🧰 Features Used in This App</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>📚 Next Steps</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>⚠ <span z-text="error"></span></p></div>
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">