volaro 0.1.0-alpha.8 → 0.1.0-alpha.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Volaro
2
2
 
3
- **Limited alpha** (this is `0.1.0-alpha.8`), published under both the
3
+ **Limited alpha** (this is `0.1.0-alpha.9`), published under both the
4
4
  `latest` and `alpha` dist-tags: `npm install volaro`.
5
5
 
6
6
  Volaro is an experimental application language intended for AI authoring and
@@ -1,6 +1,6 @@
1
1
  {
2
- "revision": "d2c835d1882b9ba794a8577752882c0e34dce872",
2
+ "revision": "3e7b1df04576aba57a758dbb1dac2011429c6b81",
3
3
  "source": "git",
4
4
  "dirty": false,
5
- "content_sha256": "35f3e74ec054f9136bf783eed2f30674cb73db5bc9e4aed9a5d0e131186f7713"
5
+ "content_sha256": "e0503b3cc060876e66caa2dfbe3aa60b093163367f5753d7219762a052c5dca9"
6
6
  }
@@ -1 +1 @@
1
- d2c835d1882b9ba794a8577752882c0e34dce872
1
+ 3e7b1df04576aba57a758dbb1dac2011429c6b81
@@ -142,6 +142,7 @@
142
142
  this._renderedUrl = null; // pathname + search of the route on screen
143
143
  this._nav = null; // the navigation in progress: {gen, isInitial, loadingShown}
144
144
  this._pending = null; // a page waiting for its first data (see _beginLoadingWait)
145
+ this._shellCleared = false; // the shell's #vl-root placeholder is gone (see _attach)
145
146
  this._announceTimer = null;
146
147
  this._announcer = makeAnnouncer(global.document);
147
148
  var self = this;
@@ -232,6 +233,20 @@
232
233
  this.stack.length = index;
233
234
  };
234
235
 
236
+ // Every level, loading page and boundary goes into the DOM through here.
237
+ // The shell's #vl-root starts with a no-JS placeholder ("loading…"). A
238
+ // single-file build's VL.mount reconciles #vl-root and so replaces it;
239
+ // the router appends each level beside what is already there, so the
240
+ // first time it puts anything into #vl-root it clears the placeholder.
241
+ // Left in place, the text sat above every page, outside any landmark.
242
+ Router.prototype._attach = function (parentEl, el) {
243
+ if (!this._shellCleared && parentEl.id === "vl-root") {
244
+ this._shellCleared = true;
245
+ while (parentEl.firstChild) parentEl.removeChild(parentEl.firstChild);
246
+ }
247
+ parentEl.appendChild(el);
248
+ };
249
+
235
250
  Router.prototype._childrenSlotOf = function (el) {
236
251
  return el ? el.querySelector("[data-vl-children]") : null;
237
252
  };
@@ -452,7 +467,7 @@
452
467
  // A page with a loading boundary stays hidden until its first data
453
468
  // arrives (see _beginLoadingWait).
454
469
  if (waiter) el.hidden = true;
455
- parentEl.appendChild(el);
470
+ self._attach(parentEl, el);
456
471
  var host;
457
472
  try {
458
473
  host = VL.mount(entry.render, el, want.params);
@@ -546,7 +561,7 @@
546
561
  VL.loadModule(loadingId, this._moduleUrl(loadingId)).then(function (entry) {
547
562
  if (w.done || w.gen !== self.generation) return;
548
563
  var el = document.createElement("div");
549
- parentEl.appendChild(el);
564
+ self._attach(parentEl, el);
550
565
  // VL.mount records the last mounted element as the app root the
551
566
  // dialog layer makes inert. The loading page is transient, so keep
552
567
  // whatever was recorded before it.
@@ -601,7 +616,7 @@
601
616
  } else {
602
617
  el.textContent = fallback.text;
603
618
  }
604
- parentEl.appendChild(el);
619
+ this._attach(parentEl, el);
605
620
  this.stack.push({ moduleId: null, kind: "boundary", host: null, el: el, params: {},
606
621
  entryTitle: fallback.title });
607
622
  VL.wireSkipLink();
@@ -622,7 +637,7 @@
622
637
  return VL.loadModule(boundaryId, this._moduleUrl(boundaryId)).then(function (entry) {
623
638
  if (myGen !== self.generation || !parentEl) return;
624
639
  var el = document.createElement("div");
625
- parentEl.appendChild(el);
640
+ self._attach(parentEl, el);
626
641
  var host;
627
642
  try {
628
643
  host = VL.mount(entry.render, el, {});
@@ -1,2 +1,2 @@
1
1
  /* minified vlrouter.js; readable source in the volaro package */
2
- !function(t){"use strict";var n=t.VL;var e={text:function(t){return t.length>0},int:function(t){return/^-?[0-9]+$/.test(t)},uuid:function(t){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)},slug:function(t){return/^[a-z0-9]+(-[a-z0-9]+)*$/.test(t)}};function r(t,n){return(e[t]||e.text)(n)?"int"===t?{ok:!0,value:parseInt(n,10)}:{ok:!0,value:n}:{ok:!1}}function o(t,n){var e=function(t){var n="/"===t?"/":t.replace(/\/+$/,"");if(""===n||"/"===n)return[];for(var e=n.replace(/^\/+/,"").split("/"),r=[],o=0;o<e.length;o++){var a;try{a=decodeURIComponent(e[o])}catch(t){return null}if(-1!==a.indexOf("/")||-1!==a.indexOf("\\"))return null;r.push(a)}return r}(n);if(null===e)return null;for(var r=0;r<t.pages.length;r++){var o=t.pages[r];if(o.segments.length===e.length){for(var a=!0,i=0;i<e.length;i++){var l=o.segments[i][0];if("static"===l&&o.segments[i][1]!==e[i]){a=!1;break}if("static"!==l&&"dynamic"!==l){a=!1;break}}if(a)return{page:o,rawSegments:e}}}return null}function a(t,n){for(var e={},r=0;r<n.length;r++)e[n[r]]=t[n[r]];return e}function i(t,n){var e=Object.keys(t),r=Object.keys(n);if(e.length!==r.length)return!1;for(var o=0;o<e.length;o++)if(t[e[o]]!==n[e[o]])return!1;return!0}var l=Date.now().toString(36)+Math.random().toString(36).slice(2,6)+".",u=0;function s(){return l+ ++u}function c(e,r){this.manifest=e,this.base=r||"",this.stack=[],this.generation=0,this.scrollMemory=Object.create(null),this.currentPage=null,this._currentKey=null,this._renderedUrl=null,this._nav=null,this._pending=null,this._announceTimer=null,this._announcer=function(t){if(!t||!t.body)return null;var n=t.getElementById("vl-announcer");return n||((n=t.createElement("div")).id="vl-announcer",n.setAttribute("role","status"),n.setAttribute("aria-live","polite"),n.setAttribute("aria-atomic","true"),n.style.cssText="position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;",t.body.appendChild(n),n)}(t.document);var o=this;n._onUnhandledLoadError=function(t){o._handleUnhandledLoadError(t)},n._onLoadSettled=function(){queueMicrotask(function(){o._checkPending()})}}function d(){var t=document.activeElement;return!t||t===document.body||!document.contains(t)}function h(){var t=document.getElementById("vl-main")||document.querySelector("main");t&&"function"==typeof t.focus&&t.focus({preventScroll:!0})}function f(t){var n=t.hash&&document.getElementById(function(t){try{return decodeURIComponent(t)}catch(n){return t}}(t.hash.slice(1)));n?n.scrollIntoView():window.scrollTo(0,0)}function p(t,n){return n<t.layouts.length?(t.boundary_for_layout_failure||[])[n]:t.boundaries.error}c.prototype._announce=function(t){var n=this._announcer;t=String(t||"").replace(/\s+/g," ").trim(),n&&t&&(clearTimeout(this._announceTimer),n.textContent="",this._announceTimer=setTimeout(function(){n.textContent=t},100))},c.prototype._announcePage=function(t){t&&(n._mainLabel=t,n.wireSkipLink(),this._announce(t))},c.prototype._stackTitle=function(){for(var t=this.stack.length-1;t>=0;t--)if(this.stack[t].entryTitle)return this.stack[t].entryTitle;return null},c.prototype._moduleUrl=function(t){return this.base+t+".js"},c.prototype._unmountFrom=function(t){for(var n=this.stack.length-1;n>=t;n--){var e=this.stack[n];e.host&&e.host._abortController&&e.host._abortController.abort(),e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el)}this.stack.length=t},c.prototype._childrenSlotOf=function(t){return t?t.querySelector("[data-vl-children]"):null},c.prototype._keyCurrentEntry=function(){var t=history.state;if(t&&null!=t.vlNav)this._currentKey=t.vlNav;else{var n=s();try{history.replaceState(Object.assign({},t,{vlNav:n}),"")}catch(t){}this._currentKey=n}},c.prototype._saveScroll=function(){null!=this._currentKey&&(this.scrollMemory[this._currentKey]={x:window.scrollX,y:window.scrollY})},c.prototype._persistScroll=function(){this._saveScroll();var t=history.state;if(t&&t.vlNav===this._currentKey)try{history.replaceState(Object.assign({},t,{vlScroll:{x:window.scrollX,y:window.scrollY}}),"")}catch(t){}},c.prototype._savedScrollFor=function(t){if(null!=t&&this.scrollMemory[t])return this.scrollMemory[t];var n=history.state;return n&&n.vlNav===t&&n.vlScroll||null},c.prototype.navigate=function(t,n){n=n||{};var e=this,a=++this.generation,i=!!n.isPopstate,l=!!n.isInitial,u=new URL(t,location.href),c=u.pathname;l?this._keyCurrentEntry():i||this._persistScroll(),this._cancelPending();var d={gen:a,isInitial:l,loadingShown:!1};this._nav=d;var p,m=o(this.manifest,c);if(null===m)p=this._renderGlobalNotFound(a);else{var g=function(t,n){for(var e={},o=[],a=0;a<t.segments.length;a++){var i=t.segments[a][0],l=t.segments[a][1];if("dynamic"===i){var u=r(t.param_types&&t.param_types[l]||"text",n[a]);u.ok?e[l]=u.value:o.push(l)}}return{params:e,bad:o}}(m.page,m.rawSegments);p=g.bad.length?this._renderParamNotFound(m.page,g,a):this._renderChain(m.page,g.params,a)}return p.then(function(){if(a===e.generation){if(e._nav=null,!l&&!i){var t=s();history.pushState({vlNav:t},"",u.pathname+u.search+u.hash),e._currentKey=t}e._renderedUrl=u.pathname+u.search;var n=e._stackTitle();n&&(document.title=n);var r=i||l?e._savedScrollFor(e._currentKey):null;r?window.scrollTo(r.x,r.y):f(u),h(),l&&!d.loadingShown||e._announcePage(n)}})},c.prototype._renderGlobalNotFound=function(t){var n=this.manifest.global_not_found_layouts||[],e={layouts:n,layout_params:n.map(function(){return[]}),boundary_for_layout_failure:n.map(function(){return null}),boundaries:{error:null}};return this._renderNotFoundIn(e,n.length,{},this.manifest.global_not_found,t)},c.prototype._renderParamNotFound=function(t,n,e){for(var r=Math.min(t.not_found_layouts||0,t.layouts.length),o=0;o<r;){for(var a=t.layout_params[o]||[],i=!0,l=0;l<a.length;l++)if(-1!==n.bad.indexOf(a[l])){i=!1;break}if(!i)break;o++}return this._renderNotFoundIn(t,o,n.params,t.boundaries.not_found,e)},c.prototype._renderNotFoundIn=function(t,n,e,r,o){this.currentPage=t;var i=t.layouts.slice(0,n).map(function(n,r){return{moduleId:n,kind:"layout",params:a(e,t.layout_params[r]||[])}}).concat([{moduleId:r||null,kind:"boundary",params:{},fallback:m}]);return this._renderDesired(i,o,function(e){return e<n?{id:(t.boundary_for_layout_failure||[])[e],fallback:g}:{id:null,fallback:m}})},c.prototype._renderChain=function(t,n,e){this.currentPage=t;var r=t.layouts.map(function(e,r){return{moduleId:e,kind:"layout",params:a(n,t.layout_params[r]||[])}}).concat([{moduleId:t.module,kind:"page",params:n,loading:t.boundaries.loading}]);return this._renderDesired(r,e,function(n){return{id:p(t,n),fallback:g}})},c.prototype._renderDesired=function(t,n,e){for(var r=0;r<t.length&&r<this.stack.length;){var o=this.stack[r],a=t[r];if(o.moduleId!==a.moduleId||o.kind!==a.kind||!i(o.params,a.params)||o.el&&o.el.hidden)break;r++}this._unmountFrom(r);var l=0===r?document.getElementById("vl-root"):this._childrenSlotOf(this.stack[r-1].el);return this._mountFrom(t,r,l,n,e)},c.prototype._mountFrom=function(t,e,r,o,a){var i=this;if(e>=t.length)return Promise.resolve();var l=t[e];if("boundary"===l.kind&&!l.moduleId)return r&&this._mountFallback(r,l.fallback),Promise.resolve();var u="page"===l.kind&&l.loading&&r?this._beginLoadingWait(l.loading,r,o):null,s=function(){u&&u.cancel();var t=a(e);return i._mountBoundaryInto(t.id,r,o,t.fallback)};return n.loadModule(l.moduleId,this._moduleUrl(l.moduleId)).then(function(c){if(o!==i.generation||!r)return null;var d,h=document.createElement("div");u&&(h.hidden=!0),r.appendChild(h);try{d=n.mount(c.render,h,l.params)}catch(t){return r.removeChild(h),s()}var f={moduleId:l.moduleId,kind:l.kind,host:d,el:h,params:l.params,entryTitle:c.title||l.fallback&&l.fallback.title};return i.stack.push(f),u?u.waitFor(f):i._mountFrom(t,e+1,i._childrenSlotOf(h),o,a)},function(){return o!==i.generation?null:s()})},c.prototype._beginLoadingWait=function(t,n,e){var r,o=this,a={gen:e,frame:null,loadingEl:null,loadingHost:null,done:!1,replacing:!1},i=new Promise(function(t){r=t}),l=setTimeout(function(){o._showLoading(a,t,n)},300);function u(){clearTimeout(l),a.done=!0,o._pending===a&&(o._pending=null),a.loadingHost&&a.loadingHost._abortController&&a.loadingHost._abortController.abort(),a.loadingEl&&a.loadingEl.parentNode&&a.loadingEl.parentNode.removeChild(a.loadingEl)}return a.cancel=function(){a.done||(u(),r())},a.reveal=function(){a.done||(u(),a.frame&&a.frame.el&&(a.frame.el.hidden=!1),r())},a.waitFor=function(t){return a.frame=t,o._checkPending(),i},this._pending=a,a},c.prototype._cancelPending=function(){this._pending&&this._pending.cancel()},c.prototype._checkPending=function(){var t=this._pending;t&&!t.done&&t.frame&&!t.replacing&&(t.gen===this.generation?-1!==this.stack.indexOf(t.frame)&&function(t){if(!t||!t._loads)return!1;for(var n in t._loads)if(t._loads[n]&&t._loads[n].pending)return!0;return!1}(t.frame.host)||t.reveal():t.cancel())},c.prototype._showLoading=function(t,e,r){var o=this;t.done||t.gen!==this.generation||n.loadModule(e,this._moduleUrl(e)).then(function(e){if(!t.done&&t.gen===o.generation){var a=document.createElement("div");r.appendChild(a);var i=n._appEl;try{t.loadingHost=n.mount(e.render,a,{})}catch(t){return void r.removeChild(a)}finally{n._appEl=i}t.loadingEl=a;var l=o._nav;l&&l.gen===t.gen&&(l.loadingShown=!0,d()&&h(),l.isInitial||o._announce(a.innerText||a.textContent))}},function(){})};var m={title:"Not found",heading:"Not found"},g={title:"Error",text:"Something went wrong."};c.prototype._mountFallback=function(t,e){var r=t.closest&&t.closest("main"),o=document.createElement(r?"div":"main");if(r||(o.id="vl-main",o.tabIndex=-1),e.heading){var a=document.createElement("h1");a.textContent=e.heading,o.appendChild(a)}else o.textContent=e.text;t.appendChild(o),this.stack.push({moduleId:null,kind:"boundary",host:null,el:o,params:{},entryTitle:e.title}),n.wireSkipLink()},c.prototype._mountBoundaryInto=function(t,e,r,o){var a=this;return t?n.loadModule(t,this._moduleUrl(t)).then(function(i){if(r===a.generation&&e){var l,u=document.createElement("div");e.appendChild(u);try{l=n.mount(i.render,u,{})}catch(t){return e.removeChild(u),void a._mountFallback(e,o)}a.stack.push({moduleId:t,kind:"boundary",host:l,el:u,params:{},entryTitle:i.title||o.title})}},function(){r===a.generation&&e&&a._mountFallback(e,o)}):(e&&this._mountFallback(e,o),Promise.resolve())},c.prototype._handleUnhandledLoadError=function(t){for(var n=this,e=-1,r=0;r<this.stack.length;r++)if(this.stack[r].host===t){e=r;break}if(-1!==e&&this.currentPage){var o=this.generation,a=0===e?document.getElementById("vl-root"):this._childrenSlotOf(this.stack[e-1].el),i=this.currentPage,l=this._pending,u=!!(l&&!l.done&&l.frame&&this.stack.indexOf(l.frame)>=e);u&&(l.replacing=!0),this._unmountFrom(e),this._mountBoundaryInto(p(i,e),a,o,g).then(function(){if(o===n.generation){if(u)return l.replacing=!1,void l.reveal();var t=n._stackTitle();t&&(document.title=t),d()&&h(),n._announcePage(t)}})}},c.prototype.interceptLinks=function(n){var e=this;"scrollRestoration"in history&&(history.scrollRestoration="manual"),(n||document).addEventListener("click",function(t){if(!(t.defaultPrevented||0!==t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)){var n=t.target.closest&&t.target.closest("a");if(n&&n.href){var r;try{r=new URL(n.href,location.href)}catch(t){return}r.origin===location.origin&&(n.target&&""!==n.target&&"_self"!==n.target||(r.hash&&r.pathname===location.pathname&&r.search===location.search?e._persistScroll():(t.preventDefault(),e.navigate(r.pathname+r.search+r.hash))))}}}),t.addEventListener("popstate",function(){if(e._saveScroll(),e._keyCurrentEntry(),location.pathname+location.search!==e._renderedUrl)e.navigate(location.pathname+location.search+location.hash,{isPopstate:!0});else{var t=e._savedScrollFor(e._currentKey);t?window.scrollTo(t.x,t.y):f(new URL(location.href))}}),t.addEventListener("hashchange",function(){e._keyCurrentEntry()}),t.addEventListener("pagehide",function(){e._persistScroll()})},n.Router=c,n.createRouter=function(t,n){return new c(t,n)}}("undefined"!=typeof window?window:globalThis);
2
+ !function(t){"use strict";var e=t.VL;var n={text:function(t){return t.length>0},int:function(t){return/^-?[0-9]+$/.test(t)},uuid:function(t){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)},slug:function(t){return/^[a-z0-9]+(-[a-z0-9]+)*$/.test(t)}};function r(t,e){return(n[t]||n.text)(e)?"int"===t?{ok:!0,value:parseInt(e,10)}:{ok:!0,value:e}:{ok:!1}}function o(t,e){var n=function(t){var e="/"===t?"/":t.replace(/\/+$/,"");if(""===e||"/"===e)return[];for(var n=e.replace(/^\/+/,"").split("/"),r=[],o=0;o<n.length;o++){var a;try{a=decodeURIComponent(n[o])}catch(t){return null}if(-1!==a.indexOf("/")||-1!==a.indexOf("\\"))return null;r.push(a)}return r}(e);if(null===n)return null;for(var r=0;r<t.pages.length;r++){var o=t.pages[r];if(o.segments.length===n.length){for(var a=!0,i=0;i<n.length;i++){var l=o.segments[i][0];if("static"===l&&o.segments[i][1]!==n[i]){a=!1;break}if("static"!==l&&"dynamic"!==l){a=!1;break}}if(a)return{page:o,rawSegments:n}}}return null}function a(t,e){for(var n={},r=0;r<e.length;r++)n[e[r]]=t[e[r]];return n}function i(t,e){var n=Object.keys(t),r=Object.keys(e);if(n.length!==r.length)return!1;for(var o=0;o<n.length;o++)if(t[n[o]]!==e[n[o]])return!1;return!0}var l=Date.now().toString(36)+Math.random().toString(36).slice(2,6)+".",u=0;function s(){return l+ ++u}function c(n,r){this.manifest=n,this.base=r||"",this.stack=[],this.generation=0,this.scrollMemory=Object.create(null),this.currentPage=null,this._currentKey=null,this._renderedUrl=null,this._nav=null,this._pending=null,this._shellCleared=!1,this._announceTimer=null,this._announcer=function(t){if(!t||!t.body)return null;var e=t.getElementById("vl-announcer");return e||((e=t.createElement("div")).id="vl-announcer",e.setAttribute("role","status"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),e.style.cssText="position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;",t.body.appendChild(e),e)}(t.document);var o=this;e._onUnhandledLoadError=function(t){o._handleUnhandledLoadError(t)},e._onLoadSettled=function(){queueMicrotask(function(){o._checkPending()})}}function d(){var t=document.activeElement;return!t||t===document.body||!document.contains(t)}function h(){var t=document.getElementById("vl-main")||document.querySelector("main");t&&"function"==typeof t.focus&&t.focus({preventScroll:!0})}function f(t){var e=t.hash&&document.getElementById(function(t){try{return decodeURIComponent(t)}catch(e){return t}}(t.hash.slice(1)));e?e.scrollIntoView():window.scrollTo(0,0)}function m(t,e){return e<t.layouts.length?(t.boundary_for_layout_failure||[])[e]:t.boundaries.error}c.prototype._announce=function(t){var e=this._announcer;t=String(t||"").replace(/\s+/g," ").trim(),e&&t&&(clearTimeout(this._announceTimer),e.textContent="",this._announceTimer=setTimeout(function(){e.textContent=t},100))},c.prototype._announcePage=function(t){t&&(e._mainLabel=t,e.wireSkipLink(),this._announce(t))},c.prototype._stackTitle=function(){for(var t=this.stack.length-1;t>=0;t--)if(this.stack[t].entryTitle)return this.stack[t].entryTitle;return null},c.prototype._moduleUrl=function(t){return this.base+t+".js"},c.prototype._unmountFrom=function(t){for(var e=this.stack.length-1;e>=t;e--){var n=this.stack[e];n.host&&n.host._abortController&&n.host._abortController.abort(),n.el&&n.el.parentNode&&n.el.parentNode.removeChild(n.el)}this.stack.length=t},c.prototype._attach=function(t,e){if(!this._shellCleared&&"vl-root"===t.id)for(this._shellCleared=!0;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(e)},c.prototype._childrenSlotOf=function(t){return t?t.querySelector("[data-vl-children]"):null},c.prototype._keyCurrentEntry=function(){var t=history.state;if(t&&null!=t.vlNav)this._currentKey=t.vlNav;else{var e=s();try{history.replaceState(Object.assign({},t,{vlNav:e}),"")}catch(t){}this._currentKey=e}},c.prototype._saveScroll=function(){null!=this._currentKey&&(this.scrollMemory[this._currentKey]={x:window.scrollX,y:window.scrollY})},c.prototype._persistScroll=function(){this._saveScroll();var t=history.state;if(t&&t.vlNav===this._currentKey)try{history.replaceState(Object.assign({},t,{vlScroll:{x:window.scrollX,y:window.scrollY}}),"")}catch(t){}},c.prototype._savedScrollFor=function(t){if(null!=t&&this.scrollMemory[t])return this.scrollMemory[t];var e=history.state;return e&&e.vlNav===t&&e.vlScroll||null},c.prototype.navigate=function(t,e){e=e||{};var n=this,a=++this.generation,i=!!e.isPopstate,l=!!e.isInitial,u=new URL(t,location.href),c=u.pathname;l?this._keyCurrentEntry():i||this._persistScroll(),this._cancelPending();var d={gen:a,isInitial:l,loadingShown:!1};this._nav=d;var m,p=o(this.manifest,c);if(null===p)m=this._renderGlobalNotFound(a);else{var g=function(t,e){for(var n={},o=[],a=0;a<t.segments.length;a++){var i=t.segments[a][0],l=t.segments[a][1];if("dynamic"===i){var u=r(t.param_types&&t.param_types[l]||"text",e[a]);u.ok?n[l]=u.value:o.push(l)}}return{params:n,bad:o}}(p.page,p.rawSegments);m=g.bad.length?this._renderParamNotFound(p.page,g,a):this._renderChain(p.page,g.params,a)}return m.then(function(){if(a===n.generation){if(n._nav=null,!l&&!i){var t=s();history.pushState({vlNav:t},"",u.pathname+u.search+u.hash),n._currentKey=t}n._renderedUrl=u.pathname+u.search;var e=n._stackTitle();e&&(document.title=e);var r=i||l?n._savedScrollFor(n._currentKey):null;r?window.scrollTo(r.x,r.y):f(u),h(),l&&!d.loadingShown||n._announcePage(e)}})},c.prototype._renderGlobalNotFound=function(t){var e=this.manifest.global_not_found_layouts||[],n={layouts:e,layout_params:e.map(function(){return[]}),boundary_for_layout_failure:e.map(function(){return null}),boundaries:{error:null}};return this._renderNotFoundIn(n,e.length,{},this.manifest.global_not_found,t)},c.prototype._renderParamNotFound=function(t,e,n){for(var r=Math.min(t.not_found_layouts||0,t.layouts.length),o=0;o<r;){for(var a=t.layout_params[o]||[],i=!0,l=0;l<a.length;l++)if(-1!==e.bad.indexOf(a[l])){i=!1;break}if(!i)break;o++}return this._renderNotFoundIn(t,o,e.params,t.boundaries.not_found,n)},c.prototype._renderNotFoundIn=function(t,e,n,r,o){this.currentPage=t;var i=t.layouts.slice(0,e).map(function(e,r){return{moduleId:e,kind:"layout",params:a(n,t.layout_params[r]||[])}}).concat([{moduleId:r||null,kind:"boundary",params:{},fallback:p}]);return this._renderDesired(i,o,function(n){return n<e?{id:(t.boundary_for_layout_failure||[])[n],fallback:g}:{id:null,fallback:p}})},c.prototype._renderChain=function(t,e,n){this.currentPage=t;var r=t.layouts.map(function(n,r){return{moduleId:n,kind:"layout",params:a(e,t.layout_params[r]||[])}}).concat([{moduleId:t.module,kind:"page",params:e,loading:t.boundaries.loading}]);return this._renderDesired(r,n,function(e){return{id:m(t,e),fallback:g}})},c.prototype._renderDesired=function(t,e,n){for(var r=0;r<t.length&&r<this.stack.length;){var o=this.stack[r],a=t[r];if(o.moduleId!==a.moduleId||o.kind!==a.kind||!i(o.params,a.params)||o.el&&o.el.hidden)break;r++}this._unmountFrom(r);var l=0===r?document.getElementById("vl-root"):this._childrenSlotOf(this.stack[r-1].el);return this._mountFrom(t,r,l,e,n)},c.prototype._mountFrom=function(t,n,r,o,a){var i=this;if(n>=t.length)return Promise.resolve();var l=t[n];if("boundary"===l.kind&&!l.moduleId)return r&&this._mountFallback(r,l.fallback),Promise.resolve();var u="page"===l.kind&&l.loading&&r?this._beginLoadingWait(l.loading,r,o):null,s=function(){u&&u.cancel();var t=a(n);return i._mountBoundaryInto(t.id,r,o,t.fallback)};return e.loadModule(l.moduleId,this._moduleUrl(l.moduleId)).then(function(c){if(o!==i.generation||!r)return null;var d,h=document.createElement("div");u&&(h.hidden=!0),i._attach(r,h);try{d=e.mount(c.render,h,l.params)}catch(t){return r.removeChild(h),s()}var f={moduleId:l.moduleId,kind:l.kind,host:d,el:h,params:l.params,entryTitle:c.title||l.fallback&&l.fallback.title};return i.stack.push(f),u?u.waitFor(f):i._mountFrom(t,n+1,i._childrenSlotOf(h),o,a)},function(){return o!==i.generation?null:s()})},c.prototype._beginLoadingWait=function(t,e,n){var r,o=this,a={gen:n,frame:null,loadingEl:null,loadingHost:null,done:!1,replacing:!1},i=new Promise(function(t){r=t}),l=setTimeout(function(){o._showLoading(a,t,e)},300);function u(){clearTimeout(l),a.done=!0,o._pending===a&&(o._pending=null),a.loadingHost&&a.loadingHost._abortController&&a.loadingHost._abortController.abort(),a.loadingEl&&a.loadingEl.parentNode&&a.loadingEl.parentNode.removeChild(a.loadingEl)}return a.cancel=function(){a.done||(u(),r())},a.reveal=function(){a.done||(u(),a.frame&&a.frame.el&&(a.frame.el.hidden=!1),r())},a.waitFor=function(t){return a.frame=t,o._checkPending(),i},this._pending=a,a},c.prototype._cancelPending=function(){this._pending&&this._pending.cancel()},c.prototype._checkPending=function(){var t=this._pending;t&&!t.done&&t.frame&&!t.replacing&&(t.gen===this.generation?-1!==this.stack.indexOf(t.frame)&&function(t){if(!t||!t._loads)return!1;for(var e in t._loads)if(t._loads[e]&&t._loads[e].pending)return!0;return!1}(t.frame.host)||t.reveal():t.cancel())},c.prototype._showLoading=function(t,n,r){var o=this;t.done||t.gen!==this.generation||e.loadModule(n,this._moduleUrl(n)).then(function(n){if(!t.done&&t.gen===o.generation){var a=document.createElement("div");o._attach(r,a);var i=e._appEl;try{t.loadingHost=e.mount(n.render,a,{})}catch(t){return void r.removeChild(a)}finally{e._appEl=i}t.loadingEl=a;var l=o._nav;l&&l.gen===t.gen&&(l.loadingShown=!0,d()&&h(),l.isInitial||o._announce(a.innerText||a.textContent))}},function(){})};var p={title:"Not found",heading:"Not found"},g={title:"Error",text:"Something went wrong."};c.prototype._mountFallback=function(t,n){var r=t.closest&&t.closest("main"),o=document.createElement(r?"div":"main");if(r||(o.id="vl-main",o.tabIndex=-1),n.heading){var a=document.createElement("h1");a.textContent=n.heading,o.appendChild(a)}else o.textContent=n.text;this._attach(t,o),this.stack.push({moduleId:null,kind:"boundary",host:null,el:o,params:{},entryTitle:n.title}),e.wireSkipLink()},c.prototype._mountBoundaryInto=function(t,n,r,o){var a=this;return t?e.loadModule(t,this._moduleUrl(t)).then(function(i){if(r===a.generation&&n){var l,u=document.createElement("div");a._attach(n,u);try{l=e.mount(i.render,u,{})}catch(t){return n.removeChild(u),void a._mountFallback(n,o)}a.stack.push({moduleId:t,kind:"boundary",host:l,el:u,params:{},entryTitle:i.title||o.title})}},function(){r===a.generation&&n&&a._mountFallback(n,o)}):(n&&this._mountFallback(n,o),Promise.resolve())},c.prototype._handleUnhandledLoadError=function(t){for(var e=this,n=-1,r=0;r<this.stack.length;r++)if(this.stack[r].host===t){n=r;break}if(-1!==n&&this.currentPage){var o=this.generation,a=0===n?document.getElementById("vl-root"):this._childrenSlotOf(this.stack[n-1].el),i=this.currentPage,l=this._pending,u=!!(l&&!l.done&&l.frame&&this.stack.indexOf(l.frame)>=n);u&&(l.replacing=!0),this._unmountFrom(n),this._mountBoundaryInto(m(i,n),a,o,g).then(function(){if(o===e.generation){if(u)return l.replacing=!1,void l.reveal();var t=e._stackTitle();t&&(document.title=t),d()&&h(),e._announcePage(t)}})}},c.prototype.interceptLinks=function(e){var n=this;"scrollRestoration"in history&&(history.scrollRestoration="manual"),(e||document).addEventListener("click",function(t){if(!(t.defaultPrevented||0!==t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)){var e=t.target.closest&&t.target.closest("a");if(e&&e.href){var r;try{r=new URL(e.href,location.href)}catch(t){return}r.origin===location.origin&&(e.target&&""!==e.target&&"_self"!==e.target||(r.hash&&r.pathname===location.pathname&&r.search===location.search?n._persistScroll():(t.preventDefault(),n.navigate(r.pathname+r.search+r.hash))))}}}),t.addEventListener("popstate",function(){if(n._saveScroll(),n._keyCurrentEntry(),location.pathname+location.search!==n._renderedUrl)n.navigate(location.pathname+location.search+location.hash,{isPopstate:!0});else{var t=n._savedScrollFor(n._currentKey);t?window.scrollTo(t.x,t.y):f(new URL(location.href))}}),t.addEventListener("hashchange",function(){n._keyCurrentEntry()}),t.addEventListener("pagehide",function(){n._persistScroll()})},e.Router=c,e.createRouter=function(t,e){return new c(t,e)}}("undefined"!=typeof window?window:globalThis);
@@ -60,6 +60,16 @@ code {
60
60
  .vl-stack { display: grid; }
61
61
  .vl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
62
62
  .vl-text { display: inline-block; }
63
+ /* Inside running text, `text` is part of the sentence. An inline-block has
64
+ * its own line box, and CSS drops the spaces at the start and end of a line,
65
+ * so `text "By Ada on "` before a `time` rendered "on2026-01-05". Parents
66
+ * whose content is phrasing (spec 8.9.2 / 8.9.3) lay `text` out inline, and
67
+ * its leading and trailing spaces render as written. Elsewhere `text` stays
68
+ * an inline-block, so `w:`, `h:` and `pad:` still size it. */
69
+ :is(p, li, dt, dd, blockquote, pre, figcaption, address, span,
70
+ h1, h2, h3, h4, h5, h6, a, button, label, legend, summary, caption, td, th,
71
+ strong, em, b, i, u, s, small, mark, sub, sup, abbr, cite, q, time,
72
+ code, kbd, samp, var, bdi, bdo) > .vl-text { display: inline; }
63
73
  .vl-spacer { flex: 1; }
64
74
  .vl-fragment { display: contents; }
65
75
 
@@ -1,2 +1,2 @@
1
1
  /* minified vlrt.css; readable source in the volaro package */
2
- @layer vl-legacy{:root{--bg:#fbfbfa;--surface:#fff;--border:#e4e4e2;--ink:#1b1b1a;--muted:#6b6b68;--accent:#2f6f4f;--action-bg:#2f6f4f;--danger-bg:#b91c1c;--danger:#b91c1c;--warn:#b45309;color-scheme:light dark;accent-color:var(--accent)}@media (prefers-color-scheme:dark){:root{--bg:#16171a;--surface:#1e2024;--border:#33363c;--ink:#e9e9e7;--muted:#9a9a96;--accent:#6fcaa0;--danger:#e07070;--warn:#d99a3f}}*{box-sizing:border-box}body{background:var(--bg);color:var(--ink);margin:0;padding:24px;font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}code{color:var(--ink);background:var(--border);border-radius:3px;padding:1px 4px;font-size:12px}.vl-col{flex-direction:column;display:flex}.vl-row{flex-flow:wrap;display:flex}.vl-stack{display:grid}.vl-grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));display:grid}.vl-text{display:inline-block}.vl-spacer{flex:1}.vl-fragment{display:contents}a.vl-link,a.vl-a{color:var(--accent);text-decoration:none}a.vl-link:hover,a.vl-a:hover{text-decoration:underline}input.vl-input,select.vl-select,textarea.vl-textarea{font:inherit;border:1px solid var(--border);background:var(--surface);color:var(--ink);border-radius:6px;padding:4px 8px}select.vl-select{min-height:32px}textarea.vl-textarea{resize:vertical;line-height:1.5}input.vl-input[aria-invalid=true],select.vl-select[aria-invalid=true],textarea.vl-textarea[aria-invalid=true]{border-color:var(--danger)}input.vl-input:is([type=checkbox],[type=radio],[type=range],[type=color]){background:0 0;border:0;padding:0}input.vl-input:is([type=checkbox],[type=radio]){width:16px;height:16px;margin:0}:is(input.vl-input,select.vl-select,textarea.vl-textarea,button.vl-button):disabled,fieldset.vl-fieldset:disabled{opacity:.55;cursor:not-allowed}.vl-field:has(>:disabled)>.vl-label{opacity:.55}.vl-field{flex-direction:column;gap:4px;display:flex}.vl-field>.vl-label{font-size:13px;font-weight:600}.vl-field>.vl-help{color:var(--muted);font-size:12px}.vl-field>.vl-error{white-space:normal;color:var(--danger);font-family:inherit;font-size:12px}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio])){grid-template-columns:auto 1fr;align-items:center;gap:2px 8px;display:grid}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>input{grid-area:1/1}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>.vl-label{font-size:inherit;grid-area:1/2;font-weight:400}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>:is(.vl-help,.vl-error){grid-column:2}button.vl-button{font:inherit;border:1px solid var(--border);background:var(--surface);color:var(--ink);cursor:pointer;border-radius:6px;padding:6px 12px}button.vl-button[data-variant=primary]{background:var(--action-bg);color:#fff;border-color:#0000}button.vl-button[data-variant=ghost]{color:var(--accent);background:0 0;border-color:#0000}button.vl-button[data-variant=danger]{background:var(--danger-bg);color:#fff;border-color:#0000}button.vl-button[aria-pressed=true]{border-color:var(--accent);box-shadow:inset 0 0 0 2px var(--accent)}button.vl-button:not(:disabled):hover{border-color:var(--accent)}:is(input.vl-input,select.vl-select,textarea.vl-textarea,button.vl-button,a.vl-link,a.vl-a,summary.vl-summary):focus-visible{outline:3px solid var(--ink);outline-offset:3px}:is(.vl-col,.vl-row,.vl-stack,.vl-grid,.vl-card)>:is(.vl-p,.vl-h1,.vl-h2,.vl-h3,.vl-h4,.vl-h5,.vl-h6,.vl-ul,.vl-ol,.vl-menu,.vl-dl,.vl-table,.vl-figure,.vl-blockquote,.vl-pre,.vl-hr,.vl-fieldset,.vl-form,.vl-details){margin:0}fieldset.vl-fieldset{border:1px solid var(--border);border-radius:8px;flex-direction:column;gap:10px;min-width:0;padding:12px 16px 16px;display:flex}legend.vl-legend{padding:0 4px;font-size:13px;font-weight:600}table.vl-table{border-collapse:collapse;font-variant-numeric:tabular-nums}caption.vl-caption{text-align:start;padding-bottom:6px;font-weight:600}:is(th.vl-th,td.vl-td){text-align:start;border-bottom:1px solid var(--border);padding:6px 12px}thead.vl-thead th.vl-th{border-bottom-width:2px}details.vl-details{border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:8px 12px}summary.vl-summary{cursor:pointer;font-weight:600}details.vl-details[open]>summary.vl-summary{margin-bottom:8px}progress.vl-progress,meter.vl-meter{width:100%;max-width:320px}.vl-overlay{z-index:1000;position:fixed;inset:0}.vl-overlay-dialog{background:#00000073;justify-content:center;align-items:center;padding:24px;display:flex}.vl-dialog{background:var(--surface);color:var(--ink);border:1px solid var(--border);border-radius:8px;width:100%;max-width:420px;padding:20px;box-shadow:0 8px 40px #00000040}.vl-dialog:focus{outline:none}.vl-dialog-title{margin:0 0 8px;font-size:18px;font-weight:700}.vl-tablist{border-bottom:1px solid var(--border);flex-direction:row;gap:4px;display:flex}.vl-tab{font:inherit;color:var(--muted);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;padding:6px 12px}.vl-tab[aria-selected=true]{color:var(--ink);border-bottom-color:var(--accent)}.vl-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.vl-tabpanel{padding:16px 0}.vl-tabpanel:focus{outline:none}.vl-spinner{color:var(--muted);padding:24px}.vl-error{color:var(--danger);white-space:pre-wrap;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}.vl-built-note{color:var(--muted);margin-top:32px;font-size:12px}.vl-skip{z-index:1000;background:var(--action-bg,#2f6f4f);color:#fff;border-radius:6px;padding:8px 14px;text-decoration:none;transition:top .15s ease-in-out;position:absolute;top:-60px;left:8px}.vl-skip:focus{outline:3px solid var(--ink,#111);outline-offset:2px;top:8px}:where(main):focus{outline:none}}@layer theme,utilities;
2
+ @layer vl-legacy{:root{--bg:#fbfbfa;--surface:#fff;--border:#e4e4e2;--ink:#1b1b1a;--muted:#6b6b68;--accent:#2f6f4f;--action-bg:#2f6f4f;--danger-bg:#b91c1c;--danger:#b91c1c;--warn:#b45309;color-scheme:light dark;accent-color:var(--accent)}@media (prefers-color-scheme:dark){:root{--bg:#16171a;--surface:#1e2024;--border:#33363c;--ink:#e9e9e7;--muted:#9a9a96;--accent:#6fcaa0;--danger:#e07070;--warn:#d99a3f}}*{box-sizing:border-box}body{background:var(--bg);color:var(--ink);margin:0;padding:24px;font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}code{color:var(--ink);background:var(--border);border-radius:3px;padding:1px 4px;font-size:12px}.vl-col{flex-direction:column;display:flex}.vl-row{flex-flow:wrap;display:flex}.vl-stack{display:grid}.vl-grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));display:grid}.vl-text{display:inline-block}:is(p,li,dt,dd,blockquote,pre,figcaption,address,span,h1,h2,h3,h4,h5,h6,a,button,label,legend,summary,caption,td,th,strong,em,b,i,u,s,small,mark,sub,sup,abbr,cite,q,time,code,kbd,samp,var,bdi,bdo)>.vl-text{display:inline}.vl-spacer{flex:1}.vl-fragment{display:contents}a.vl-link,a.vl-a{color:var(--accent);text-decoration:none}a.vl-link:hover,a.vl-a:hover{text-decoration:underline}input.vl-input,select.vl-select,textarea.vl-textarea{font:inherit;border:1px solid var(--border);background:var(--surface);color:var(--ink);border-radius:6px;padding:4px 8px}select.vl-select{min-height:32px}textarea.vl-textarea{resize:vertical;line-height:1.5}input.vl-input[aria-invalid=true],select.vl-select[aria-invalid=true],textarea.vl-textarea[aria-invalid=true]{border-color:var(--danger)}input.vl-input:is([type=checkbox],[type=radio],[type=range],[type=color]){background:0 0;border:0;padding:0}input.vl-input:is([type=checkbox],[type=radio]){width:16px;height:16px;margin:0}:is(input.vl-input,select.vl-select,textarea.vl-textarea,button.vl-button):disabled,fieldset.vl-fieldset:disabled{opacity:.55;cursor:not-allowed}.vl-field:has(>:disabled)>.vl-label{opacity:.55}.vl-field{flex-direction:column;gap:4px;display:flex}.vl-field>.vl-label{font-size:13px;font-weight:600}.vl-field>.vl-help{color:var(--muted);font-size:12px}.vl-field>.vl-error{white-space:normal;color:var(--danger);font-family:inherit;font-size:12px}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio])){grid-template-columns:auto 1fr;align-items:center;gap:2px 8px;display:grid}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>input{grid-area:1/1}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>.vl-label{font-size:inherit;grid-area:1/2;font-weight:400}.vl-field:has(>input.vl-input:is([type=checkbox],[type=radio]))>:is(.vl-help,.vl-error){grid-column:2}button.vl-button{font:inherit;border:1px solid var(--border);background:var(--surface);color:var(--ink);cursor:pointer;border-radius:6px;padding:6px 12px}button.vl-button[data-variant=primary]{background:var(--action-bg);color:#fff;border-color:#0000}button.vl-button[data-variant=ghost]{color:var(--accent);background:0 0;border-color:#0000}button.vl-button[data-variant=danger]{background:var(--danger-bg);color:#fff;border-color:#0000}button.vl-button[aria-pressed=true]{border-color:var(--accent);box-shadow:inset 0 0 0 2px var(--accent)}button.vl-button:not(:disabled):hover{border-color:var(--accent)}:is(input.vl-input,select.vl-select,textarea.vl-textarea,button.vl-button,a.vl-link,a.vl-a,summary.vl-summary):focus-visible{outline:3px solid var(--ink);outline-offset:3px}:is(.vl-col,.vl-row,.vl-stack,.vl-grid,.vl-card)>:is(.vl-p,.vl-h1,.vl-h2,.vl-h3,.vl-h4,.vl-h5,.vl-h6,.vl-ul,.vl-ol,.vl-menu,.vl-dl,.vl-table,.vl-figure,.vl-blockquote,.vl-pre,.vl-hr,.vl-fieldset,.vl-form,.vl-details){margin:0}fieldset.vl-fieldset{border:1px solid var(--border);border-radius:8px;flex-direction:column;gap:10px;min-width:0;padding:12px 16px 16px;display:flex}legend.vl-legend{padding:0 4px;font-size:13px;font-weight:600}table.vl-table{border-collapse:collapse;font-variant-numeric:tabular-nums}caption.vl-caption{text-align:start;padding-bottom:6px;font-weight:600}:is(th.vl-th,td.vl-td){text-align:start;border-bottom:1px solid var(--border);padding:6px 12px}thead.vl-thead th.vl-th{border-bottom-width:2px}details.vl-details{border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:8px 12px}summary.vl-summary{cursor:pointer;font-weight:600}details.vl-details[open]>summary.vl-summary{margin-bottom:8px}progress.vl-progress,meter.vl-meter{width:100%;max-width:320px}.vl-overlay{z-index:1000;position:fixed;inset:0}.vl-overlay-dialog{background:#00000073;justify-content:center;align-items:center;padding:24px;display:flex}.vl-dialog{background:var(--surface);color:var(--ink);border:1px solid var(--border);border-radius:8px;width:100%;max-width:420px;padding:20px;box-shadow:0 8px 40px #00000040}.vl-dialog:focus{outline:none}.vl-dialog-title{margin:0 0 8px;font-size:18px;font-weight:700}.vl-tablist{border-bottom:1px solid var(--border);flex-direction:row;gap:4px;display:flex}.vl-tab{font:inherit;color:var(--muted);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;padding:6px 12px}.vl-tab[aria-selected=true]{color:var(--ink);border-bottom-color:var(--accent)}.vl-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.vl-tabpanel{padding:16px 0}.vl-tabpanel:focus{outline:none}.vl-spinner{color:var(--muted);padding:24px}.vl-error{color:var(--danger);white-space:pre-wrap;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}.vl-built-note{color:var(--muted);margin-top:32px;font-size:12px}.vl-skip{z-index:1000;background:var(--action-bg,#2f6f4f);color:#fff;border-radius:6px;padding:8px 14px;text-decoration:none;transition:top .15s ease-in-out;position:absolute;top:-60px;left:8px}.vl-skip:focus{outline:3px solid var(--ink,#111);outline-offset:2px;top:8px}:where(main):focus{outline:none}}@layer theme,utilities;
@@ -411,18 +411,39 @@ class Emitter:
411
411
  """Follow `use ./x` / `use ../x` and collect their `model` / record `type`
412
412
  / `auth` declarations, so a `db.<table>` reference can be matched to a
413
413
  model, and a route guard resolved against the project's session
414
- settings, even when they live in a shared file."""
414
+ settings, even when they live in a shared file. Also collect the
415
+ outbound `api` clients the `use` lines import, so a call to a shared
416
+ client emits its fetch like a locally declared one."""
415
417
  import os
416
418
 
417
419
  base_dir = os.path.dirname(os.path.abspath(str(src_path)))
418
420
  seen: set[str] = set()
421
+ # Outbound `api` clients each module exports: its own `api` blocks
422
+ # plus whatever its own `use` lines bring in (vlcheck's resolver
423
+ # treats an imported name as part of the importing module's table,
424
+ # so a re-export through a middle file checks; the build agrees).
425
+ exports: dict[str, dict[str, A.ApiDecl]] = {}
426
+
427
+ def _target(path: str, from_dir: str):
428
+ cand = os.path.normpath(os.path.join(from_dir, path))
429
+ return next((c for c in (cand + ".vl", os.path.join(cand, "index.vl"), cand)
430
+ if os.path.isfile(c)), None)
431
+
432
+ def _take(use: A.Use, from_dir: str, into: dict) -> None:
433
+ """Copy the api clients `use` names (all of them for a bare
434
+ `use ./x`, as vlcheck does) from its target's exports."""
435
+ target = _target(use.path, from_dir)
436
+ if target is None:
437
+ return
438
+ got = exports.get(target, {})
439
+ for n in (list(use.names) if use.names else list(got)):
440
+ if n in got:
441
+ into.setdefault(n, got[n])
419
442
 
420
443
  def load(path: str, from_dir: str, depth: int) -> None:
421
444
  if depth > 8:
422
445
  return
423
- cand = os.path.normpath(os.path.join(from_dir, path))
424
- target = next((c for c in (cand + ".vl", os.path.join(cand, "index.vl"), cand)
425
- if os.path.isfile(c)), None)
446
+ target = _target(path, from_dir)
426
447
  if target is None or target in seen:
427
448
  return
428
449
  seen.add(target)
@@ -445,11 +466,27 @@ class Emitter:
445
466
  elif isinstance(it, A.Use) and (it.path.startswith("./")
446
467
  or it.path.startswith("../")):
447
468
  load(it.path, os.path.dirname(target), depth + 1)
469
+ clients: dict[str, A.ApiDecl] = {}
470
+ for it in mod.items:
471
+ if isinstance(it, A.Use) and (it.path.startswith("./")
472
+ or it.path.startswith("../")):
473
+ _take(it, os.path.dirname(target), clients)
474
+ for it in mod.items:
475
+ if isinstance(it, A.ApiDecl):
476
+ clients[it.name] = it # own declaration wins over an import
477
+ exports[target] = clients
448
478
 
449
479
  for it in module.items:
450
480
  if isinstance(it, A.Use) and (it.path.startswith("./")
451
481
  or it.path.startswith("../")):
452
482
  load(it.path, base_dir, 0)
483
+ # An `api` client imported with `use` is called exactly like a local
484
+ # one; the importing file's own `api` block of the same name (added
485
+ # by the caller after this) still wins.
486
+ for it in module.items:
487
+ if isinstance(it, A.Use) and (it.path.startswith("./")
488
+ or it.path.startswith("../")):
489
+ _take(it, base_dir, self.clients)
453
490
 
454
491
  # ------------------------------------------------------------------ driver
455
492
  def build(self, module: A.Module, src_path=None) -> str:
@@ -1169,12 +1206,13 @@ class Emitter:
1169
1206
  arglist = ", ".join(
1170
1207
  [self.expr(a) for a in call.args] + [self.expr(v) for _, v in call.named])
1171
1208
  else:
1172
- self.warnings.append(
1173
- "load %s calls '%s' but no service route or api endpoint matches"
1174
- % (st.name, endpoint))
1175
- caller = "route_%s" % endpoint
1176
- arglist = ", ".join(
1177
- [self.expr(a) for a in call.args] + [self.expr(v) for _, v in call.named])
1209
+ # This used to warn and emit a call to an undefined `route_<x>`,
1210
+ # so the page built but the load always failed at runtime and
1211
+ # never made its request. Fail the build instead.
1212
+ raise BuildError(
1213
+ "E-LOAD-UNRESOLVED: load %s calls '%s' but no service route or "
1214
+ "api endpoint matches -- declare it in this file or import "
1215
+ "its `api` block with `use`" % (st.name, endpoint))
1178
1216
  if call_arglist is None:
1179
1217
  call_arglist = arglist
1180
1218
  rec = self._fresh("_load")
@@ -421,6 +421,10 @@ audio video source track picture map area
421
421
  `rate` and `headers` directives fail the build
422
422
  (`E-API-DIRECTIVE-UNSUPPORTED`) rather than being silently ignored, and
423
423
  there are no server-side outbound calls yet.
424
+ An `api` block declared in a shared module (`app/_lib/data.vl`) works in
425
+ every page, layout or `app.vl` that imports it (`use ./_lib/data { content }`).
426
+ A `load` whose endpoint the build cannot find fails the build
427
+ (`E-LOAD-UNRESOLVED`) instead of shipping a load that always errors.
424
428
  - `std.list` `concat` / `append`; `xs |> map|filter(fn(x) …)`. (`fold` is in
425
429
  `volaro spec`'s stdlib table but is **not implemented** in this build —
426
430
  found stale while documenting `matches` below; corrected here, not fixed.)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volaro",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-alpha.9",
4
4
  "description": "Volaro \u2014 an application language written to be authored by an AI agent and read by a person. Ships the language reference and a working compiler for a supported subset (volaro check / build / dev). CLI: `volaro` (alias `vl`).",
5
5
  "repository": {
6
6
  "type": "git",