zero-query 0.8.6 → 0.8.8
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 +4 -5
- package/cli/commands/bundle.js +20 -28
- package/cli/commands/dev/devtools/panel.html +1 -1
- package/cli/commands/dev/overlay.js +132 -44
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +12 -110
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +1 -3
- package/package.json +1 -1
- package/src/component.js +8 -106
- package/src/router.js +1 -1
- package/src/ssr.js +1 -1
- package/types/component.d.ts +3 -34
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ The dev server includes a **full-screen error overlay** that surfaces errors dir
|
|
|
65
65
|
|
|
66
66
|
#### Floating Toolbar & Inspector
|
|
67
67
|
|
|
68
|
-
A compact toolbar appears in the bottom-right corner
|
|
68
|
+
A compact expandable toolbar appears in the bottom-right corner. In its **collapsed** state it shows live render and request counters. Click the chevron to **expand** and reveal the route indicator (color-coded by the last navigation event — navigate, pop, replace, hashchange, substate), registered component count, and DOM element count. Click any stat to open a **dark-themed DevTools inspector** as a popup — or visit `http://localhost:<port>/_devtools` for a standalone split-view panel with five tabs: **Router** (live route state, guards, history timeline), **Components** (live state cards), **Performance** (render timeline with timing metrics), **Network** (fetch log with JSON viewer), and **Elements** (live DOM tree with component badges, source viewer, expand/collapse).
|
|
69
69
|
|
|
70
70
|
### Alternative: Manual Setup (No npm)
|
|
71
71
|
|
|
@@ -189,13 +189,12 @@ Output goes to `dist/` next to your `index.html`:
|
|
|
189
189
|
dist/
|
|
190
190
|
server/ ← deploy to your web server (<base href="/"> for SPA routes)
|
|
191
191
|
index.html
|
|
192
|
-
z-app.<hash>.js
|
|
193
192
|
z-app.<hash>.min.js
|
|
194
193
|
global.<hash>.min.css
|
|
195
194
|
assets/
|
|
196
195
|
local/ ← open from disk (file://) — no server needed
|
|
197
196
|
index.html
|
|
198
|
-
z-app.<hash>.js
|
|
197
|
+
z-app.<hash>.min.js
|
|
199
198
|
...
|
|
200
199
|
```
|
|
201
200
|
|
|
@@ -217,7 +216,7 @@ dist/
|
|
|
217
216
|
4. **Convention fallbacks** — `app/app.js`, `scripts/app.js`, `src/app.js`, `js/app.js`, `app.js`, `main.js`.
|
|
218
217
|
2. Resolves all `import` statements and topologically sorts dependencies
|
|
219
218
|
3. Strips `import`/`export` syntax, wraps in an IIFE
|
|
220
|
-
4. Embeds zQuery library and inlines `templateUrl` / `styleUrl`
|
|
219
|
+
4. Embeds zQuery library and inlines `templateUrl` / `styleUrl` files
|
|
221
220
|
5. Rewrites HTML, copies assets, produces hashed filenames
|
|
222
221
|
|
|
223
222
|
---
|
|
@@ -277,7 +276,7 @@ location / {
|
|
|
277
276
|
| CLI Command | Description |
|
|
278
277
|
| --- | --- |
|
|
279
278
|
| `zquery create [dir]` | Scaffold a new project (index.html, components, store, styles) |
|
|
280
|
-
| `zquery dev [root]` | Dev server with live-reload, CSS hot-swap, error overlay, floating toolbar & inspector panel (port 3100). Visit `/_devtools` for the standalone panel. `--index` for custom HTML, `--bundle` for bundled mode, `--no-intercept` to skip CDN intercept. |
|
|
279
|
+
| `zquery dev [root]` | Dev server with live-reload, CSS hot-swap, error overlay, expandable floating toolbar & five-tab inspector panel (port 3100). Visit `/_devtools` for the standalone panel. `--index` for custom HTML, `--bundle` for bundled mode, `--no-intercept` to skip CDN intercept. |
|
|
281
280
|
| `zquery bundle [dir\|file]` | Bundle app into a single IIFE file. Accepts dir or direct entry file. |
|
|
282
281
|
| `zquery build` | Build the zQuery library (`dist/zquery.min.js`) |
|
|
283
282
|
| `zquery --help` | Show CLI usage |
|
package/cli/commands/bundle.js
CHANGED
|
@@ -353,7 +353,7 @@ function _collapseTemplateCSS(tpl) {
|
|
|
353
353
|
|
|
354
354
|
/**
|
|
355
355
|
* Scan bundled source files for external resource references
|
|
356
|
-
* (
|
|
356
|
+
* (templateUrl, styleUrl) and return a map of
|
|
357
357
|
* { relativePath: fileContent } for inlining.
|
|
358
358
|
*/
|
|
359
359
|
function collectInlineResources(files, projectRoot) {
|
|
@@ -363,33 +363,6 @@ function collectInlineResources(files, projectRoot) {
|
|
|
363
363
|
const code = fs.readFileSync(file, 'utf-8');
|
|
364
364
|
const fileDir = path.dirname(file);
|
|
365
365
|
|
|
366
|
-
// pages: config
|
|
367
|
-
const pagesMatch = code.match(/pages\s*:\s*\{[^}]*dir\s*:\s*['"]([^'"]+)['"]/s);
|
|
368
|
-
if (pagesMatch) {
|
|
369
|
-
const pagesDir = pagesMatch[1];
|
|
370
|
-
const ext = (code.match(/pages\s*:\s*\{[^}]*ext\s*:\s*['"]([^'"]+)['"]/s) || [])[1] || '.html';
|
|
371
|
-
const itemsMatch = code.match(/items\s*:\s*\[([\s\S]*?)\]/);
|
|
372
|
-
if (itemsMatch) {
|
|
373
|
-
const itemsBlock = itemsMatch[1];
|
|
374
|
-
const ids = [];
|
|
375
|
-
let m;
|
|
376
|
-
const strRe = /['"]([^'"]+)['"]/g;
|
|
377
|
-
while ((m = strRe.exec(itemsBlock)) !== null) {
|
|
378
|
-
const before = itemsBlock.substring(Math.max(0, m.index - 20), m.index);
|
|
379
|
-
if (/label\s*:\s*$/.test(before)) continue;
|
|
380
|
-
ids.push(m[1]);
|
|
381
|
-
}
|
|
382
|
-
const absPagesDir = path.join(fileDir, pagesDir);
|
|
383
|
-
for (const id of ids) {
|
|
384
|
-
const pagePath = path.join(absPagesDir, id + ext);
|
|
385
|
-
if (fs.existsSync(pagePath)) {
|
|
386
|
-
const relKey = path.relative(projectRoot, pagePath).replace(/\\/g, '/');
|
|
387
|
-
inlineMap[relKey] = fs.readFileSync(pagePath, 'utf-8');
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
366
|
// styleUrl:
|
|
394
367
|
const styleUrlRe = /styleUrl\s*:\s*['"]([^'"]+)['"]/g;
|
|
395
368
|
const styleMatch = styleUrlRe.exec(code);
|
|
@@ -409,6 +382,25 @@ function collectInlineResources(files, projectRoot) {
|
|
|
409
382
|
const relKey = path.relative(projectRoot, tmplPath).replace(/\\/g, '/');
|
|
410
383
|
inlineMap[relKey] = fs.readFileSync(tmplPath, 'utf-8');
|
|
411
384
|
}
|
|
385
|
+
} else if (/templateUrl\s*:/.test(code)) {
|
|
386
|
+
// Dynamic templateUrl (e.g. Object.fromEntries, computed map) —
|
|
387
|
+
// inline all .html files in the component's directory tree so
|
|
388
|
+
// the runtime __zqInline lookup can resolve them by suffix.
|
|
389
|
+
(function scanHtml(dir) {
|
|
390
|
+
try {
|
|
391
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
392
|
+
const full = path.join(dir, entry.name);
|
|
393
|
+
if (entry.isFile() && entry.name.endsWith('.html')) {
|
|
394
|
+
const relKey = path.relative(projectRoot, full).replace(/\\/g, '/');
|
|
395
|
+
if (!inlineMap[relKey]) {
|
|
396
|
+
inlineMap[relKey] = fs.readFileSync(full, 'utf-8');
|
|
397
|
+
}
|
|
398
|
+
} else if (entry.isDirectory()) {
|
|
399
|
+
scanHtml(full);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} catch { /* permission error — skip */ }
|
|
403
|
+
})(fileDir);
|
|
412
404
|
}
|
|
413
405
|
}
|
|
414
406
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<button class="viewport-btn" data-width="768" title="Tablet (768px)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="2" width="16" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12" y2="18"/></svg></button>
|
|
10
10
|
<button class="viewport-btn active" data-width="0" title="Desktop (fill)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg></button>
|
|
11
11
|
<span class="divider-sep"></span>
|
|
12
|
-
<button id="btn-route" title="Current route"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="
|
|
12
|
+
<button id="btn-route" title="Current route"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="17" y1="2" x2="7" y2="22"/></svg></button>
|
|
13
13
|
<span class="route-label" id="route-label">/</span>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
@@ -558,6 +558,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
558
558
|
if (__zqChannel) {
|
|
559
559
|
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
560
560
|
}
|
|
561
|
+
updateDevBar();
|
|
561
562
|
};
|
|
562
563
|
|
|
563
564
|
var _origReplaceState = history.replaceState;
|
|
@@ -576,6 +577,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
576
577
|
if (__zqChannel) {
|
|
577
578
|
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
578
579
|
}
|
|
580
|
+
updateDevBar();
|
|
579
581
|
};
|
|
580
582
|
|
|
581
583
|
window.addEventListener('popstate', function(e) {
|
|
@@ -593,6 +595,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
593
595
|
if (__zqChannel) {
|
|
594
596
|
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
595
597
|
}
|
|
598
|
+
updateDevBar();
|
|
596
599
|
});
|
|
597
600
|
|
|
598
601
|
window.addEventListener('hashchange', function() {
|
|
@@ -606,36 +609,62 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
606
609
|
if (__zqChannel) {
|
|
607
610
|
try { __zqChannel.postMessage({ type: 'router', data: evt }); } catch(e) {}
|
|
608
611
|
}
|
|
612
|
+
updateDevBar();
|
|
609
613
|
});
|
|
610
614
|
|
|
611
615
|
// =====================================================================
|
|
612
|
-
// Dev Toolbar — floating bar with
|
|
616
|
+
// Dev Toolbar — expandable floating bar with stats
|
|
613
617
|
// =====================================================================
|
|
614
618
|
var devBar;
|
|
619
|
+
var __zqBarExpanded = false;
|
|
620
|
+
var __zqRouteColors = {
|
|
621
|
+
navigate: { bg: 'rgba(63,185,80,0.12)', fg: '#3fb950' },
|
|
622
|
+
replace: { bg: 'rgba(210,153,34,0.12)', fg: '#d29922' },
|
|
623
|
+
pop: { bg: 'rgba(248,81,73,0.12)', fg: '#f85149' },
|
|
624
|
+
'pop-substate':{ bg: 'rgba(248,81,73,0.12)', fg: '#f85149' },
|
|
625
|
+
substate: { bg: 'rgba(168,130,255,0.12)', fg: '#a882ff' },
|
|
626
|
+
hashchange: { bg: 'rgba(88,166,255,0.12)', fg: '#58a6ff' }
|
|
627
|
+
};
|
|
628
|
+
var __zqRouteDefault = { bg: 'rgba(227,155,55,0.12)', fg: '#e39b37' };
|
|
629
|
+
var __zqRouteFadeTimer = null;
|
|
615
630
|
|
|
616
631
|
function createDevBar() {
|
|
617
632
|
devBar = document.createElement('div');
|
|
618
633
|
devBar.id = '__zq_devbar';
|
|
619
634
|
devBar.setAttribute('style',
|
|
620
635
|
'position:fixed;bottom:12px;right:12px;z-index:2147483646;' +
|
|
621
|
-
'display:flex;align-items:center;gap:
|
|
636
|
+
'display:flex;align-items:center;gap:4px;' +
|
|
622
637
|
'background:rgba(22,27,34,0.92);border:1px solid rgba(48,54,61,0.8);' +
|
|
623
638
|
'border-radius:8px;padding:4px 6px;backdrop-filter:blur(8px);' +
|
|
624
639
|
'font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;' +
|
|
625
640
|
'font-size:11px;color:#8b949e;user-select:none;cursor:default;' +
|
|
626
|
-
'box-shadow:0 4px 12px rgba(0,0,0,0.4);'
|
|
641
|
+
'box-shadow:0 4px 12px rgba(0,0,0,0.4);transition:all .25s cubic-bezier(.22,1,.36,1);'
|
|
627
642
|
);
|
|
643
|
+
|
|
644
|
+
var statStyle = 'padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;cursor:pointer;white-space:nowrap;';
|
|
645
|
+
var expandedStyle = statStyle + 'display:none;transform:scale(0);opacity:0;transform-origin:left center;will-change:transform,opacity;transition:transform .25s cubic-bezier(.22,1,.36,1),opacity .2s ease;';
|
|
646
|
+
|
|
628
647
|
devBar.innerHTML =
|
|
629
648
|
'<span style="color:#58a6ff;font-weight:700;padding:0 4px;font-size:10px;letter-spacing:.5px">zQ</span>' +
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
'
|
|
638
|
-
'"
|
|
649
|
+
// Expanded-only stats
|
|
650
|
+
'<span id="__zq_bar_route" class="__zq_ex" title="Current route" style="' + expandedStyle +
|
|
651
|
+
'background:rgba(227,155,55,0.12);color:#e39b37;outline-offset:1px;transition:transform .25s cubic-bezier(.22,1,.36,1),opacity .2s ease,background .3s ease,color .3s ease,outline-color .6s ease;">/</span>' +
|
|
652
|
+
'<span id="__zq_bar_comps" class="__zq_ex" title="Registered components" style="' + expandedStyle +
|
|
653
|
+
'background:rgba(168,130,255,0.1);color:#a882ff;">0 comps</span>' +
|
|
654
|
+
// Always-visible stats
|
|
655
|
+
'<span id="__zq_bar_morphs" title="Render operations" style="' + statStyle +
|
|
656
|
+
'background:rgba(188,140,255,0.1);color:#bc8cff;">0 render</span>' +
|
|
657
|
+
'<span id="__zq_bar_reqs" title="Network requests" style="' + statStyle +
|
|
658
|
+
'background:rgba(88,166,255,0.1);color:#58a6ff;">0 req</span>' +
|
|
659
|
+
// Expanded-only stats
|
|
660
|
+
'<span id="__zq_bar_els" class="__zq_ex" title="DOM elements" style="' + expandedStyle +
|
|
661
|
+
'background:rgba(63,185,80,0.1);color:#3fb950;">0 els</span>' +
|
|
662
|
+
// Toggle expand/collapse
|
|
663
|
+
'<button id="__zq_bar_toggle" title="Expand toolbar" style="' +
|
|
664
|
+
'padding:2px 6px;border-radius:4px;font-size:10px;font-weight:600;' +
|
|
665
|
+
'background:rgba(88,166,255,0.08);color:#58a6ff;border:1px solid rgba(88,166,255,0.2);' +
|
|
666
|
+
'cursor:pointer;font-family:inherit;transition:all .15s;line-height:1;' +
|
|
667
|
+
'"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 6 9 12 15 18"/></svg></button>' +
|
|
639
668
|
'<button id="__zq_bar_close" title="Close toolbar" style="' +
|
|
640
669
|
'padding:0 4px;color:#484f58;cursor:pointer;font-size:14px;border:none;' +
|
|
641
670
|
'background:none;font-family:inherit;line-height:1;' +
|
|
@@ -657,28 +686,9 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
657
686
|
}
|
|
658
687
|
}
|
|
659
688
|
|
|
660
|
-
//
|
|
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
|
|
689
|
+
// Open devtools popup
|
|
679
690
|
var __zqPopup = null;
|
|
680
691
|
function openDevToolsPopup(tab) {
|
|
681
|
-
// If popup is already open, just switch the tab via BroadcastChannel
|
|
682
692
|
if (__zqPopup && !__zqPopup.closed) {
|
|
683
693
|
switchDevTab(tab);
|
|
684
694
|
__zqPopup.focus();
|
|
@@ -693,26 +703,60 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
693
703
|
',resizable=yes,scrollbars=yes');
|
|
694
704
|
}
|
|
695
705
|
|
|
696
|
-
|
|
697
|
-
if (isInSplitFrame()) {
|
|
698
|
-
|
|
706
|
+
function openTab(tab) {
|
|
707
|
+
if (isInSplitFrame()) { switchDevTab(tab); } else { openDevToolsPopup(tab); }
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Stat click handlers → open relevant devtools tab
|
|
711
|
+
document.getElementById('__zq_bar_route').addEventListener('click', function() { openTab('router'); });
|
|
712
|
+
document.getElementById('__zq_bar_comps').addEventListener('click', function() { openTab('components'); });
|
|
713
|
+
document.getElementById('__zq_bar_morphs').addEventListener('click', function() { openTab('perf'); });
|
|
714
|
+
document.getElementById('__zq_bar_reqs').addEventListener('click', function() { openTab('network'); });
|
|
715
|
+
document.getElementById('__zq_bar_els').addEventListener('click', function() { openTab('dom'); });
|
|
716
|
+
|
|
717
|
+
// Expand / collapse toggle
|
|
718
|
+
document.getElementById('__zq_bar_toggle').addEventListener('click', function() {
|
|
719
|
+
__zqBarExpanded = !__zqBarExpanded;
|
|
720
|
+
var items = devBar.querySelectorAll('.__zq_ex');
|
|
721
|
+
var btn = this;
|
|
722
|
+
if (__zqBarExpanded) {
|
|
723
|
+
btn.innerHTML = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 6 15 12 9 18"/></svg>';
|
|
724
|
+
btn.title = 'Collapse toolbar';
|
|
725
|
+
for (var i = 0; i < items.length; i++) {
|
|
726
|
+
items[i].style.display = 'inline';
|
|
727
|
+
items[i].offsetWidth; // reflow
|
|
728
|
+
items[i].style.transform = 'scale(1)';
|
|
729
|
+
items[i].style.opacity = '1';
|
|
730
|
+
}
|
|
731
|
+
updateDevBar();
|
|
699
732
|
} else {
|
|
700
|
-
|
|
733
|
+
btn.innerHTML = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 6 9 12 15 18"/></svg>';
|
|
734
|
+
btn.title = 'Expand toolbar';
|
|
735
|
+
for (var i = 0; i < items.length; i++) {
|
|
736
|
+
items[i].style.transform = 'scale(0)';
|
|
737
|
+
items[i].style.opacity = '0';
|
|
738
|
+
}
|
|
739
|
+
setTimeout(function() {
|
|
740
|
+
if (!__zqBarExpanded) {
|
|
741
|
+
var items = devBar.querySelectorAll('.__zq_ex');
|
|
742
|
+
for (var i = 0; i < items.length; i++) items[i].style.display = 'none';
|
|
743
|
+
}
|
|
744
|
+
}, 250);
|
|
701
745
|
}
|
|
702
746
|
});
|
|
703
747
|
|
|
748
|
+
// Toggle hover
|
|
749
|
+
document.getElementById('__zq_bar_toggle').addEventListener('mouseover', function() {
|
|
750
|
+
this.style.background = 'rgba(88,166,255,0.18)';
|
|
751
|
+
});
|
|
752
|
+
document.getElementById('__zq_bar_toggle').addEventListener('mouseout', function() {
|
|
753
|
+
this.style.background = 'rgba(88,166,255,0.08)';
|
|
754
|
+
});
|
|
755
|
+
|
|
704
756
|
// Close button
|
|
705
757
|
document.getElementById('__zq_bar_close').addEventListener('click', function() {
|
|
706
758
|
devBar.style.display = 'none';
|
|
707
759
|
});
|
|
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
760
|
}
|
|
717
761
|
|
|
718
762
|
function updateDevBar() {
|
|
@@ -721,6 +765,50 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
721
765
|
var morphEl = document.getElementById('__zq_bar_morphs');
|
|
722
766
|
if (reqEl) reqEl.textContent = __zqRequests.length + ' req';
|
|
723
767
|
if (morphEl) morphEl.textContent = __zqMorphCount + ' render';
|
|
768
|
+
|
|
769
|
+
if (__zqBarExpanded) {
|
|
770
|
+
var routeEl = document.getElementById('__zq_bar_route');
|
|
771
|
+
var compsEl = document.getElementById('__zq_bar_comps');
|
|
772
|
+
var elsEl = document.getElementById('__zq_bar_els');
|
|
773
|
+
if (routeEl) {
|
|
774
|
+
var lastEvt = __zqRouterEvents.length ? __zqRouterEvents[__zqRouterEvents.length - 1] : null;
|
|
775
|
+
var action = lastEvt ? lastEvt.action : '';
|
|
776
|
+
var path = location.pathname + location.hash;
|
|
777
|
+
if (path.length > 16) path = path.substring(0, 14) + '…';
|
|
778
|
+
var colors = __zqRouteColors[action] || __zqRouteDefault;
|
|
779
|
+
routeEl.style.background = colors.bg;
|
|
780
|
+
routeEl.style.color = colors.fg;
|
|
781
|
+
if (action) {
|
|
782
|
+
var label = action === 'pop-substate' ? 'pop' : action;
|
|
783
|
+
routeEl.textContent = label + ' ' + path;
|
|
784
|
+
routeEl.title = action + ' → ' + location.pathname + location.hash;
|
|
785
|
+
} else {
|
|
786
|
+
routeEl.textContent = path;
|
|
787
|
+
}
|
|
788
|
+
// Flash brightness on fresh events
|
|
789
|
+
if (lastEvt && (Date.now() - lastEvt.timestamp) < 2000) {
|
|
790
|
+
routeEl.style.outline = '1px solid ' + colors.fg;
|
|
791
|
+
clearTimeout(__zqRouteFadeTimer);
|
|
792
|
+
__zqRouteFadeTimer = setTimeout(function() {
|
|
793
|
+
var el = document.getElementById('__zq_bar_route');
|
|
794
|
+
if (el) el.style.outline = 'none';
|
|
795
|
+
}, 1800);
|
|
796
|
+
} else {
|
|
797
|
+
routeEl.style.outline = 'none';
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (compsEl) {
|
|
801
|
+
var count = 0;
|
|
802
|
+
try {
|
|
803
|
+
if (window.$ && $.components) count = Object.keys($.components()).length;
|
|
804
|
+
else if (window.$ && $._components) count = Object.keys($._components).length;
|
|
805
|
+
} catch(e) {}
|
|
806
|
+
compsEl.textContent = count + ' comps';
|
|
807
|
+
}
|
|
808
|
+
if (elsEl) {
|
|
809
|
+
elsEl.textContent = document.querySelectorAll('*').length + ' els';
|
|
810
|
+
}
|
|
811
|
+
}
|
|
724
812
|
}
|
|
725
813
|
|
|
726
814
|
// Expose for devtools popup
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|
package/dist/zquery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.8.
|
|
2
|
+
* zQuery (zeroQuery) v0.8.8
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman - MIT License
|
|
@@ -2553,7 +2553,7 @@ function _getKey(node) {
|
|
|
2553
2553
|
* - Scoped styles (inline or via styleUrl)
|
|
2554
2554
|
* - External templates via templateUrl (with {{expression}} interpolation)
|
|
2555
2555
|
* - External styles via styleUrl (fetched & scoped automatically)
|
|
2556
|
-
* - Relative path resolution — templateUrl
|
|
2556
|
+
* - Relative path resolution — templateUrl and styleUrl
|
|
2557
2557
|
* resolve relative to the component file automatically
|
|
2558
2558
|
*/
|
|
2559
2559
|
|
|
@@ -2623,16 +2623,6 @@ function _fetchResource(url) {
|
|
|
2623
2623
|
return promise;
|
|
2624
2624
|
}
|
|
2625
2625
|
|
|
2626
|
-
/**
|
|
2627
|
-
* Convert a kebab-case id to Title Case.
|
|
2628
|
-
* 'getting-started' → 'Getting Started'
|
|
2629
|
-
* @param {string} id
|
|
2630
|
-
* @returns {string}
|
|
2631
|
-
*/
|
|
2632
|
-
function _titleCase(id) {
|
|
2633
|
-
return id.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
2626
|
/**
|
|
2637
2627
|
* Resolve a relative URL against a base.
|
|
2638
2628
|
*
|
|
@@ -2851,47 +2841,10 @@ class Component {
|
|
|
2851
2841
|
// - string → single stylesheet
|
|
2852
2842
|
// - string[] → array of URLs → all fetched & concatenated
|
|
2853
2843
|
//
|
|
2854
|
-
// pages config (shorthand for multi-template + route-param page switching):
|
|
2855
|
-
// pages: {
|
|
2856
|
-
// dir: 'pages', // relative to component file (or base)
|
|
2857
|
-
// param: 'section', // route param name → this.activePage
|
|
2858
|
-
// default: 'getting-started', // fallback when param is absent
|
|
2859
|
-
// ext: '.html', // file extension (default '.html')
|
|
2860
|
-
// items: ['page-a', { id: 'page-b', label: 'Page B' }, ...]
|
|
2861
|
-
// }
|
|
2862
|
-
// Exposes this.pages (array of {id,label}), this.activePage (current id)
|
|
2863
|
-
// Pages are lazy-loaded: only the active page is fetched on first render,
|
|
2864
|
-
// remaining pages are prefetched in the background for instant navigation.
|
|
2865
|
-
//
|
|
2866
2844
|
async _loadExternals() {
|
|
2867
2845
|
const def = this._def;
|
|
2868
2846
|
const base = def._base; // auto-detected or explicit
|
|
2869
2847
|
|
|
2870
|
-
// -- Pages config ---------------------------------------------
|
|
2871
|
-
if (def.pages && !def._pagesNormalized) {
|
|
2872
|
-
const p = def.pages;
|
|
2873
|
-
const ext = p.ext || '.html';
|
|
2874
|
-
const dir = _resolveUrl((p.dir || '').replace(/\/+$/, ''), base);
|
|
2875
|
-
|
|
2876
|
-
// Normalize items → [{id, label}, …]
|
|
2877
|
-
def._pages = (p.items || []).map(item => {
|
|
2878
|
-
if (typeof item === 'string') return { id: item, label: _titleCase(item) };
|
|
2879
|
-
return { ...item, label: item.label || _titleCase(item.id) };
|
|
2880
|
-
});
|
|
2881
|
-
|
|
2882
|
-
// Build URL map for lazy per-page loading.
|
|
2883
|
-
// Pages are fetched on demand (active page first, rest prefetched in
|
|
2884
|
-
// the background) so the component renders as soon as the visible
|
|
2885
|
-
// page is ready instead of waiting for every page to download.
|
|
2886
|
-
def._pageUrls = {};
|
|
2887
|
-
for (const { id } of def._pages) {
|
|
2888
|
-
def._pageUrls[id] = `${dir}/${id}${ext}`;
|
|
2889
|
-
}
|
|
2890
|
-
if (!def._externalTemplates) def._externalTemplates = {};
|
|
2891
|
-
|
|
2892
|
-
def._pagesNormalized = true;
|
|
2893
|
-
}
|
|
2894
|
-
|
|
2895
2848
|
// -- External templates --------------------------------------
|
|
2896
2849
|
if (def.templateUrl && !def._templateLoaded) {
|
|
2897
2850
|
const tu = def.templateUrl;
|
|
@@ -2904,9 +2857,8 @@ class Component {
|
|
|
2904
2857
|
results.forEach((html, i) => { def._externalTemplates[i] = html; });
|
|
2905
2858
|
} else if (typeof tu === 'object') {
|
|
2906
2859
|
const entries = Object.entries(tu);
|
|
2907
|
-
// Pages config already resolved; plain objects still need resolving
|
|
2908
2860
|
const results = await Promise.all(
|
|
2909
|
-
entries.map(([, url]) => _fetchResource(
|
|
2861
|
+
entries.map(([, url]) => _fetchResource(_resolveUrl(url, base)))
|
|
2910
2862
|
);
|
|
2911
2863
|
def._externalTemplates = {};
|
|
2912
2864
|
entries.forEach(([key], i) => { def._externalTemplates[key] = results[i]; });
|
|
@@ -2935,8 +2887,7 @@ class Component {
|
|
|
2935
2887
|
_render() {
|
|
2936
2888
|
// If externals haven't loaded yet, trigger async load then re-render
|
|
2937
2889
|
if ((this._def.templateUrl && !this._def._templateLoaded) ||
|
|
2938
|
-
(this._def.styleUrl && !this._def._styleLoaded)
|
|
2939
|
-
(this._def.pages && !this._def._pagesNormalized)) {
|
|
2890
|
+
(this._def.styleUrl && !this._def._styleLoaded)) {
|
|
2940
2891
|
this._loadExternals().then(() => {
|
|
2941
2892
|
if (!this._destroyed) this._render();
|
|
2942
2893
|
});
|
|
@@ -2948,43 +2899,6 @@ class Component {
|
|
|
2948
2899
|
this.templates = this._def._externalTemplates;
|
|
2949
2900
|
}
|
|
2950
2901
|
|
|
2951
|
-
// Expose pages metadata and active page (derived from route param)
|
|
2952
|
-
if (this._def._pages) {
|
|
2953
|
-
this.pages = this._def._pages;
|
|
2954
|
-
const pc = this._def.pages;
|
|
2955
|
-
let active = (pc.param && this.props.$params?.[pc.param]) || pc.default || this._def._pages[0]?.id || '';
|
|
2956
|
-
|
|
2957
|
-
// Fall back to default if the param doesn't match any known page
|
|
2958
|
-
if (this._def._pageUrls && !(active in this._def._pageUrls)) {
|
|
2959
|
-
active = pc.default || this._def._pages[0]?.id || '';
|
|
2960
|
-
}
|
|
2961
|
-
this.activePage = active;
|
|
2962
|
-
|
|
2963
|
-
// Lazy-load: fetch only the active page's template on demand
|
|
2964
|
-
if (this._def._pageUrls && !(active in this._def._externalTemplates)) {
|
|
2965
|
-
const url = this._def._pageUrls[active];
|
|
2966
|
-
if (url) {
|
|
2967
|
-
_fetchResource(url).then(html => {
|
|
2968
|
-
this._def._externalTemplates[active] = html;
|
|
2969
|
-
if (!this._destroyed) this._render();
|
|
2970
|
-
});
|
|
2971
|
-
return; // Wait for active page before rendering
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
|
|
2975
|
-
// Prefetch remaining pages in background (once, after active page is ready)
|
|
2976
|
-
if (this._def._pageUrls && !this._def._pagesPrefetched) {
|
|
2977
|
-
this._def._pagesPrefetched = true;
|
|
2978
|
-
for (const [id, url] of Object.entries(this._def._pageUrls)) {
|
|
2979
|
-
if (!(id in this._def._externalTemplates)) {
|
|
2980
|
-
_fetchResource(url).then(html => {
|
|
2981
|
-
this._def._externalTemplates[id] = html;
|
|
2982
|
-
});
|
|
2983
|
-
}
|
|
2984
|
-
}
|
|
2985
|
-
}
|
|
2986
|
-
}
|
|
2987
|
-
|
|
2988
2902
|
// Determine HTML content
|
|
2989
2903
|
let html;
|
|
2990
2904
|
if (this._def.render) {
|
|
@@ -3687,7 +3601,7 @@ class Component {
|
|
|
3687
3601
|
// Reserved definition keys (not user methods)
|
|
3688
3602
|
const _reservedKeys = new Set([
|
|
3689
3603
|
'state', 'render', 'styles', 'init', 'mounted', 'updated', 'destroyed', 'props',
|
|
3690
|
-
'templateUrl', 'styleUrl', 'templates', '
|
|
3604
|
+
'templateUrl', 'styleUrl', 'templates', 'base',
|
|
3691
3605
|
'computed', 'watch'
|
|
3692
3606
|
]);
|
|
3693
3607
|
|
|
@@ -3710,8 +3624,8 @@ function component(name, definition) {
|
|
|
3710
3624
|
}
|
|
3711
3625
|
definition._name = name;
|
|
3712
3626
|
|
|
3713
|
-
// Auto-detect the calling module's URL so that relative templateUrl
|
|
3714
|
-
//
|
|
3627
|
+
// Auto-detect the calling module's URL so that relative templateUrl
|
|
3628
|
+
// and styleUrl paths resolve relative to the component file.
|
|
3715
3629
|
// An explicit `base` string on the definition overrides auto-detection.
|
|
3716
3630
|
if (definition.base !== undefined) {
|
|
3717
3631
|
definition._base = definition.base; // explicit override
|
|
@@ -3838,23 +3752,11 @@ async function prefetch(name) {
|
|
|
3838
3752
|
const def = _registry.get(name);
|
|
3839
3753
|
if (!def) return;
|
|
3840
3754
|
|
|
3841
|
-
// Load templateUrl
|
|
3755
|
+
// Load templateUrl and styleUrl if not already loaded.
|
|
3842
3756
|
if ((def.templateUrl && !def._templateLoaded) ||
|
|
3843
|
-
(def.styleUrl && !def._styleLoaded)
|
|
3844
|
-
(def.pages && !def._pagesNormalized)) {
|
|
3757
|
+
(def.styleUrl && !def._styleLoaded)) {
|
|
3845
3758
|
await Component.prototype._loadExternals.call({ _def: def });
|
|
3846
3759
|
}
|
|
3847
|
-
|
|
3848
|
-
// For pages-based components, prefetch ALL page templates so any
|
|
3849
|
-
// active page renders instantly on mount.
|
|
3850
|
-
if (def._pageUrls && def._externalTemplates) {
|
|
3851
|
-
const missing = Object.entries(def._pageUrls)
|
|
3852
|
-
.filter(([id]) => !(id in def._externalTemplates));
|
|
3853
|
-
if (missing.length) {
|
|
3854
|
-
const results = await Promise.all(missing.map(([, url]) => _fetchResource(url)));
|
|
3855
|
-
missing.forEach(([id], i) => { def._externalTemplates[id] = results[i]; });
|
|
3856
|
-
}
|
|
3857
|
-
}
|
|
3858
3760
|
}
|
|
3859
3761
|
|
|
3860
3762
|
|
|
@@ -4098,7 +4000,7 @@ class Router {
|
|
|
4098
4000
|
|
|
4099
4001
|
// Per-route fallback: register an alias path for the same component.
|
|
4100
4002
|
// e.g. { path: '/docs/:section', fallback: '/docs', component: 'docs-page' }
|
|
4101
|
-
// When matched via fallback, missing params are undefined
|
|
4003
|
+
// When matched via fallback, missing params are undefined.
|
|
4102
4004
|
if (route.fallback) {
|
|
4103
4005
|
const fbKeys = [];
|
|
4104
4006
|
const fbPattern = route.fallback
|
|
@@ -5362,8 +5264,8 @@ $.guardCallback = guardCallback;
|
|
|
5362
5264
|
$.validate = validate;
|
|
5363
5265
|
|
|
5364
5266
|
// --- Meta ------------------------------------------------------------------
|
|
5365
|
-
$.version = '0.8.
|
|
5366
|
-
$.libSize = '~
|
|
5267
|
+
$.version = '0.8.8';
|
|
5268
|
+
$.libSize = '~89 KB';
|
|
5367
5269
|
$.meta = {}; // populated at build time by CLI bundler
|
|
5368
5270
|
|
|
5369
5271
|
$.noConflict = () => {
|