@squinch/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.d.ts
CHANGED
|
@@ -36,6 +36,17 @@ export declare function iconsUsedBy(files: ProjectFile[] | string): {
|
|
|
36
36
|
* them and a result of only `azure/key-vaults` reads as proof that
|
|
37
37
|
* `azure/key-vault` doesn't exist) can look them up via `packInfo`. */
|
|
38
38
|
export declare function searchIcons(query: string, pack?: string): string[];
|
|
39
|
+
/** The same search, saying how it matched. `relaxed: true` means no icon
|
|
40
|
+
* matched every word, so the hits are the closest partial matches instead —
|
|
41
|
+
* callers that print for an agent loop label them (`no exact match —
|
|
42
|
+
* closest:`), because near-misses answer the question and an empty list
|
|
43
|
+
* forces a second pass with different words. Measured before ranking
|
|
44
|
+
* existed: `rag` returned eighteen sto*rag*e rows with `sys/rag` dead last,
|
|
45
|
+
* and "message queue" / "object storage" returned nothing at all. */
|
|
46
|
+
export declare function searchIconsDetailed(query: string, pack?: string): {
|
|
47
|
+
hits: string[];
|
|
48
|
+
relaxed: boolean;
|
|
49
|
+
};
|
|
39
50
|
export type { ProjectFile };
|
|
40
51
|
export type * from "./model/types.js";
|
|
41
52
|
export { HUES } from "./model/types.js";
|
package/dist/api.js
CHANGED
|
@@ -69,6 +69,16 @@ export function iconsUsedBy(files) {
|
|
|
69
69
|
* them and a result of only `azure/key-vaults` reads as proof that
|
|
70
70
|
* `azure/key-vault` doesn't exist) can look them up via `packInfo`. */
|
|
71
71
|
export function searchIcons(query, pack) {
|
|
72
|
+
return searchIconsDetailed(query, pack).hits;
|
|
73
|
+
}
|
|
74
|
+
/** The same search, saying how it matched. `relaxed: true` means no icon
|
|
75
|
+
* matched every word, so the hits are the closest partial matches instead —
|
|
76
|
+
* callers that print for an agent loop label them (`no exact match —
|
|
77
|
+
* closest:`), because near-misses answer the question and an empty list
|
|
78
|
+
* forces a second pass with different words. Measured before ranking
|
|
79
|
+
* existed: `rag` returned eighteen sto*rag*e rows with `sys/rag` dead last,
|
|
80
|
+
* and "message queue" / "object storage" returned nothing at all. */
|
|
81
|
+
export function searchIconsDetailed(query, pack) {
|
|
72
82
|
// Vendors are inconsistent about number — Azure has "Container Registries"
|
|
73
83
|
// and "Data Factories" where everyone searches "container registry" and
|
|
74
84
|
// "data factory" — so both sides are singularized before comparing.
|
|
@@ -97,20 +107,46 @@ export function searchIcons(query, pack) {
|
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
const words = rawWords.map(stem);
|
|
100
|
-
const
|
|
110
|
+
const joined = words.join(" ");
|
|
111
|
+
const scored = [];
|
|
101
112
|
for (const name of allPackNames()) {
|
|
102
113
|
if (packFilter && name !== packFilter)
|
|
103
114
|
continue;
|
|
104
|
-
const aliases = packInfo(name)?.aliases ?? {};
|
|
105
115
|
const haystack = (id) => norm(`${id} ${iconTitle(name, id) ?? ""}`);
|
|
106
|
-
const
|
|
107
|
-
for (const id of
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
const matchedIds = new Set(iconIds(name).filter((id) => words.some((w) => haystack(id).includes(w)) || !words.length));
|
|
117
|
+
for (const id of matchedIds) {
|
|
118
|
+
const hay = haystack(id);
|
|
119
|
+
const hayWords = new Set(hay.split(" "));
|
|
120
|
+
scored.push({
|
|
121
|
+
hit: `${name}/${id}`,
|
|
122
|
+
exact: joined && norm(id) === joined ? 1 : 0,
|
|
123
|
+
whole: words.length && words.every((w) => hayWords.has(w)) ? 1 : 0,
|
|
124
|
+
matched: words.filter((w) => hay.includes(w)).length,
|
|
125
|
+
});
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
|
-
|
|
128
|
+
// One row per icon: an alias row is dropped only when its canonical is in
|
|
129
|
+
// the SAME result list. Checking the broader matched set instead deduped
|
|
130
|
+
// `sys/vector-search` (a full match) against a canonical that had only
|
|
131
|
+
// matched one word and was not being returned at all.
|
|
132
|
+
const dedupe = (list) => {
|
|
133
|
+
const present = new Set(list.map((s) => s.hit));
|
|
134
|
+
return list.filter((s) => {
|
|
135
|
+
const name = s.hit.slice(0, s.hit.indexOf("/"));
|
|
136
|
+
const id = s.hit.slice(s.hit.indexOf("/") + 1);
|
|
137
|
+
const canonical = (packInfo(name)?.aliases ?? {})[id];
|
|
138
|
+
return !(canonical && present.has(`${name}/${canonical}`)); // canonical covers it
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
const rank = (list) => dedupe(list)
|
|
142
|
+
.sort((a, b) => b.exact - a.exact || b.whole - a.whole || b.matched - a.matched || (a.hit < b.hit ? -1 : 1))
|
|
143
|
+
.map((s) => s.hit);
|
|
144
|
+
const full = scored.filter((s) => s.matched === words.length);
|
|
145
|
+
if (full.length || !words.length)
|
|
146
|
+
return { hits: rank(full), relaxed: false };
|
|
147
|
+
// Nothing matched every word: fall back to partial matches, best first.
|
|
148
|
+
// Capped — the point is a next move, not a directory listing.
|
|
149
|
+
return { hits: rank(scored).slice(0, 12), relaxed: true };
|
|
114
150
|
}
|
|
115
151
|
// the one colour vocabulary, for editors that complete it and tests that sweep it
|
|
116
152
|
export { HUES } from "./model/types.js";
|
|
@@ -169,7 +205,11 @@ export async function renderProject(input, opts = {}) {
|
|
|
169
205
|
loc: { from: 0, to: 0, line: 1, col: 1 },
|
|
170
206
|
};
|
|
171
207
|
}
|
|
172
|
-
|
|
208
|
+
// Dark is the default render. The one exception is an adaptive render with
|
|
209
|
+
// no theme named anywhere: an adaptive file is a light render carrying a
|
|
210
|
+
// dark override (the media query is `prefers-color-scheme: dark`), so its
|
|
211
|
+
// base has to be light — naming `dark` for it is still the error below.
|
|
212
|
+
const themeName = opts.theme ?? view.theme ?? built.model.fileTheme ?? (opts.adaptive ? "light" : "dark");
|
|
173
213
|
const theme = themes[themeName];
|
|
174
214
|
const diagnostics = [...built.diagnostics];
|
|
175
215
|
if (!theme)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_JS = "\"use strict\";(()=>{var Q={ms:460,ease:\"cubic-bezier(.32,.72,0,1)\"},X={ms:240,ease:\"cubic-bezier(.4,0,.2,1)\"},K=e=>({x:e.x+e.w/2,y:e.y+e.h/2}),Y=(e,n,
|
|
1
|
+
export declare const RUNTIME_JS = "\"use strict\";(()=>{var Q={ms:460,ease:\"cubic-bezier(.32,.72,0,1)\"},X={ms:240,ease:\"cubic-bezier(.4,0,.2,1)\"},K=e=>({x:e.x+e.w/2,y:e.y+e.h/2}),Y=(e,n,c)=>Math.min(c,Math.max(n,e)),Z=(e,n)=>Y(Math.min(e.w/n.w,e.h/n.h),1.15,3.2);function U(e){let{view:n,ghostBox:c,liveBox:a,anchor:p,dir:o}=e,{ms:l,ease:d}=p?Q:X;if(!p)return{ms:l,ease:d,gOrigin:\"50% 50%\",lOrigin:\"50% 50%\",gEnd:\"scale(.97)\",lStart:\"scale(1.03)\"};let f=Z(n,p),T=1+(f-1)*.62,E=K(p),h=K(n),y=h.x-E.x,w=h.y-E.y,x=o===\"in\",g=x?E:h,k=x?h:E;return{ms:l,ease:d,gOrigin:`${g.x-c.x}px ${g.y-c.y}px`,lOrigin:`${k.x-a.x}px ${k.y-a.y}px`,gEnd:x?`translate(${y}px, ${w}px) scale(${f})`:`translate(${-y}px, ${-w}px) scale(${1/f})`,lStart:x?`translate(${-y}px, ${-w}px) scale(${1/T})`:`translate(${y}px, ${w}px) scale(${T})`}}function W(e,n){if(n){if(!e)return n.split(\".\")[0];if(!(n===e||!n.startsWith(`${e}.`)))return`${e}.${n.slice(e.length+1).split(\".\")[0]}`}}function ee(e){if(!e)return[];let n=e.split(\".\");return n.map((c,a)=>n.slice(0,a+1).join(\".\"))}function O(e,n,c){return e.find(a=>a.scope===c&&a.name!==n)}function _(e,n,c){let a=e.find(d=>d.name===n)?.scope,p=e.find(d=>d.name===c)?.scope,o=W(a,p);if(o)return{dir:\"in\",anchor:o};let l=W(p,a);return l?{dir:\"out\",anchor:l}:{dir:\"in\"}}function te(e,n){let c=[],a=e.find(o=>!o.scope);if(a&&c.push({label:\"landscape\",view:a.name}),!n)return c;let p=n.split(\".\");for(let[o,l]of ee(n).entries())c.push({label:p[o],view:e.find(d=>d.scope===l)?.name});return c}function S(e,n,c){return[...te(e,c)].reverse().find(a=>a.view&&a.view!==n)?.view}var L=e=>document.querySelector(e);function j(){let e=JSON.parse(L(\"#sq-data\").textContent||\"{}\"),n=L(\"#sq-live\"),c=L(\"#sq-ghost\"),a=L(\"#sq-stage\"),p=()=>matchMedia(\"(prefers-reduced-motion: reduce)\").matches,o=e.entry,l=e.themes[0],d=0,f=!1;if(e.themes.length>1){let t=matchMedia(\"(prefers-color-scheme: dark)\").matches,s=e.themes.find(r=>r.includes(\"dark\")===t);s&&(l=s)}let T=`${e.entry}|${e.themes[0]}`,E=n.firstElementChild?.cloneNode(!0)??null,h=(t,s,r=0)=>{let i=r?`${t}|${s}|${r}`:`${t}|${s}`;if(i===T)return E?.cloneNode(!0)??null;let u=document.querySelector(`template[data-key=\"${CSS.escape(i)}\"]`);return u?u.content.cloneNode(!0):null},y=t=>e.views.find(s=>s.name===t)?.scope,w=t=>{let s=t.getBoundingClientRect(),r=a.getBoundingClientRect();return{x:s.left-r.left,y:s.top-r.top,w:s.width,h:s.height}};function x(){document.title=e.views.find(i=>i.name===o)?.title??document.title;let t=document.querySelector(\"#sq-tabs\");if(t){t.replaceChildren();for(let i of e.views){let u=document.createElement(\"button\");u.type=\"button\",u.className=i.name===o?\"on\":\"\",u.textContent=i.name,u.title=i.title??i.name,u.onclick=()=>g(i.name),t.append(u)}t.querySelector(\".on\")?.scrollIntoView?.({block:\"nearest\",inline:\"nearest\"})}for(let i of n.querySelectorAll(\"[data-path]\")){let u=i.getAttribute(\"data-path\");i.classList.toggle(\"sq-zoom\",!!(u&&O(e.views,o,u)))}let s=document.querySelector(\"#sq-step\"),r=e.flows[o]??0;s&&(s.textContent=f&&r?`${d||1} / ${r}`:\"\")}function g(t,s=!1){if(!t||t===o)return;let r=f&&e.flows[t]?s?e.flows[t]:1:0,i=h(t,l,r);if(!i)return;d=r;let{dir:u,anchor:v}=_(e.views,o,t),C=w(n),H=n.firstElementChild;if(p()||!H){n.replaceChildren(i),o=t,x();return}c.replaceChildren(H.cloneNode(!0)),c.style.cssText=`position:absolute;left:${C.x}px;top:${C.y}px;width:${C.w}px;height:${C.h}px;z-index:1;pointer-events:none`,n.replaceChildren(i),o=t,x();let G=w(n),P=v?(u===\"in\"?c:n).querySelector(`[data-path=\"${CSS.escape(v)}\"]`):null,m=U({view:{x:0,y:0,w:a.clientWidth,h:a.clientHeight},ghostBox:C,liveBox:G,anchor:P?w(P):void 0,dir:u}),b=c.style,$=n.style;b.transition=\"none\",b.transformOrigin=m.gOrigin,b.transform=\"none\",b.opacity=\"1\",$.transition=\"none\",$.transformOrigin=m.lOrigin,$.transform=m.lStart,$.opacity=\"0\",n.offsetHeight,b.transition=`transform ${m.ms}ms ${m.ease}, opacity ${Math.round(m.ms*.55)}ms ${m.ease}`,$.transition=`transform ${m.ms}ms ${m.ease}, opacity ${Math.round(m.ms*.6)}ms ${m.ease} ${Math.round(m.ms*.25)}ms`,b.transform=m.gEnd,b.opacity=\"0\",$.transform=\"none\",$.opacity=\"1\";let I=!1,q=0,R=()=>{I||(I=!0,cancelAnimationFrame(q),c.replaceChildren(),c.removeAttribute(\"style\"),n.removeAttribute(\"style\"))},z=()=>{getComputedStyle(n).transform===\"none\"?R():q=requestAnimationFrame(z)};q=requestAnimationFrame(z),setTimeout(R,m.ms+1e3)}function k(t){let s=t===l?null:h(o,t,d);s&&(l=t,document.documentElement.dataset.theme=t,n.replaceChildren(s))}function M(t){let s=e.flows[o]??0,r=d+t;if(s&&r>=1&&r<=s){let v=h(o,l,r);v&&(d=r,n.replaceChildren(v),x());return}let i=e.views.findIndex(v=>v.name===o),u=e.views[i+t];u&&g(u.name,t<0)}n.addEventListener(\"click\",t=>{let r=t.target.closest?.(\"[data-path]\")?.getAttribute(\"data-path\");if(r){let i=O(e.views,o,r);if(i)return g(i.name)}if(!r){let i=S(e.views,o,y(o));i&&g(i)}});let A=document.querySelector(\"#sq-theme\");A&&(A.onclick=()=>k(e.themes[(e.themes.indexOf(l)+1)%e.themes.length]));function B(t){if(t!==f){if(f=t,document.body.classList.toggle(\"presenting\",t),t){if(document.documentElement.requestFullscreen?.().catch(()=>{}),(e.flows[o]??0)&&!d){let r=h(o,l,1);r&&(d=1,n.replaceChildren(r))}}else if(document.fullscreenElement&&document.exitFullscreen?.().catch(()=>{}),d){let s=h(o,l,0);s&&(d=0,n.replaceChildren(s))}x()}}let N,V=()=>{document.body.classList.remove(\"idle\"),clearTimeout(N),f&&(N=setTimeout(()=>document.body.classList.add(\"idle\"),3500))};addEventListener(\"mousemove\",V),addEventListener(\"keydown\",V);let J=()=>k(e.themes[(e.themes.indexOf(l)+1)%e.themes.length]);addEventListener(\"keydown\",t=>{if(!(t.metaKey||t.ctrlKey||t.altKey))switch(t.key){case\"ArrowRight\":case\"PageDown\":case\" \":case\"Enter\":t.preventDefault(),M(1);break;case\"ArrowLeft\":case\"PageUp\":t.preventDefault(),M(-1);break;case\"ArrowUp\":case\"Backspace\":{let s=S(e.views,o,y(o));s&&(t.preventDefault(),g(s));break}case\"Home\":t.preventDefault(),g(e.views[0].name);break;case\"End\":t.preventDefault(),g(e.views[e.views.length-1].name,!0);break;case\"Escape\":f&&(t.preventDefault(),B(!1));break;case\"p\":case\"P\":B(!f);break;case\"f\":case\"F\":document.fullscreenElement?document.exitFullscreen?.().catch(()=>{}):document.documentElement.requestFullscreen?.().catch(()=>{});break;case\"t\":case\"T\":e.themes.length>1&&J();break}}),addEventListener(\"fullscreenchange\",()=>{!document.fullscreenElement&&f&&B(!1)});let D=document.querySelector(\"#sq-present\");D&&(D.onclick=()=>B(!f));let F=()=>{let t=decodeURIComponent(location.hash.slice(1));t&&t!==o&&e.views.some(s=>s.name===t)&&g(t)};if(addEventListener(\"hashchange\",F),document.documentElement.dataset.theme=l,l!==e.themes[0]){let t=h(o,l);t&&n.replaceChildren(t)}x(),F()}document.readyState===\"loading\"?addEventListener(\"DOMContentLoaded\",j):j();})();";
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
// output cannot drift with an esbuild upgrade; CI re-runs the generator and
|
|
4
4
|
// diffs. It bundles view/dive.ts and view/navigate.ts, which is what makes
|
|
5
5
|
// "the export moves exactly like the playground" true by construction.
|
|
6
|
-
export const RUNTIME_JS = "\"use strict\";(()=>{var Q={ms:460,ease:\"cubic-bezier(.32,.72,0,1)\"},X={ms:240,ease:\"cubic-bezier(.4,0,.2,1)\"},K=e=>({x:e.x+e.w/2,y:e.y+e.h/2}),Y=(e,n,
|
|
6
|
+
export const RUNTIME_JS = "\"use strict\";(()=>{var Q={ms:460,ease:\"cubic-bezier(.32,.72,0,1)\"},X={ms:240,ease:\"cubic-bezier(.4,0,.2,1)\"},K=e=>({x:e.x+e.w/2,y:e.y+e.h/2}),Y=(e,n,c)=>Math.min(c,Math.max(n,e)),Z=(e,n)=>Y(Math.min(e.w/n.w,e.h/n.h),1.15,3.2);function U(e){let{view:n,ghostBox:c,liveBox:a,anchor:p,dir:o}=e,{ms:l,ease:d}=p?Q:X;if(!p)return{ms:l,ease:d,gOrigin:\"50% 50%\",lOrigin:\"50% 50%\",gEnd:\"scale(.97)\",lStart:\"scale(1.03)\"};let f=Z(n,p),T=1+(f-1)*.62,E=K(p),h=K(n),y=h.x-E.x,w=h.y-E.y,x=o===\"in\",g=x?E:h,k=x?h:E;return{ms:l,ease:d,gOrigin:`${g.x-c.x}px ${g.y-c.y}px`,lOrigin:`${k.x-a.x}px ${k.y-a.y}px`,gEnd:x?`translate(${y}px, ${w}px) scale(${f})`:`translate(${-y}px, ${-w}px) scale(${1/f})`,lStart:x?`translate(${-y}px, ${-w}px) scale(${1/T})`:`translate(${y}px, ${w}px) scale(${T})`}}function W(e,n){if(n){if(!e)return n.split(\".\")[0];if(!(n===e||!n.startsWith(`${e}.`)))return`${e}.${n.slice(e.length+1).split(\".\")[0]}`}}function ee(e){if(!e)return[];let n=e.split(\".\");return n.map((c,a)=>n.slice(0,a+1).join(\".\"))}function O(e,n,c){return e.find(a=>a.scope===c&&a.name!==n)}function _(e,n,c){let a=e.find(d=>d.name===n)?.scope,p=e.find(d=>d.name===c)?.scope,o=W(a,p);if(o)return{dir:\"in\",anchor:o};let l=W(p,a);return l?{dir:\"out\",anchor:l}:{dir:\"in\"}}function te(e,n){let c=[],a=e.find(o=>!o.scope);if(a&&c.push({label:\"landscape\",view:a.name}),!n)return c;let p=n.split(\".\");for(let[o,l]of ee(n).entries())c.push({label:p[o],view:e.find(d=>d.scope===l)?.name});return c}function S(e,n,c){return[...te(e,c)].reverse().find(a=>a.view&&a.view!==n)?.view}var L=e=>document.querySelector(e);function j(){let e=JSON.parse(L(\"#sq-data\").textContent||\"{}\"),n=L(\"#sq-live\"),c=L(\"#sq-ghost\"),a=L(\"#sq-stage\"),p=()=>matchMedia(\"(prefers-reduced-motion: reduce)\").matches,o=e.entry,l=e.themes[0],d=0,f=!1;if(e.themes.length>1){let t=matchMedia(\"(prefers-color-scheme: dark)\").matches,s=e.themes.find(r=>r.includes(\"dark\")===t);s&&(l=s)}let T=`${e.entry}|${e.themes[0]}`,E=n.firstElementChild?.cloneNode(!0)??null,h=(t,s,r=0)=>{let i=r?`${t}|${s}|${r}`:`${t}|${s}`;if(i===T)return E?.cloneNode(!0)??null;let u=document.querySelector(`template[data-key=\"${CSS.escape(i)}\"]`);return u?u.content.cloneNode(!0):null},y=t=>e.views.find(s=>s.name===t)?.scope,w=t=>{let s=t.getBoundingClientRect(),r=a.getBoundingClientRect();return{x:s.left-r.left,y:s.top-r.top,w:s.width,h:s.height}};function x(){document.title=e.views.find(i=>i.name===o)?.title??document.title;let t=document.querySelector(\"#sq-tabs\");if(t){t.replaceChildren();for(let i of e.views){let u=document.createElement(\"button\");u.type=\"button\",u.className=i.name===o?\"on\":\"\",u.textContent=i.name,u.title=i.title??i.name,u.onclick=()=>g(i.name),t.append(u)}t.querySelector(\".on\")?.scrollIntoView?.({block:\"nearest\",inline:\"nearest\"})}for(let i of n.querySelectorAll(\"[data-path]\")){let u=i.getAttribute(\"data-path\");i.classList.toggle(\"sq-zoom\",!!(u&&O(e.views,o,u)))}let s=document.querySelector(\"#sq-step\"),r=e.flows[o]??0;s&&(s.textContent=f&&r?`${d||1} / ${r}`:\"\")}function g(t,s=!1){if(!t||t===o)return;let r=f&&e.flows[t]?s?e.flows[t]:1:0,i=h(t,l,r);if(!i)return;d=r;let{dir:u,anchor:v}=_(e.views,o,t),C=w(n),H=n.firstElementChild;if(p()||!H){n.replaceChildren(i),o=t,x();return}c.replaceChildren(H.cloneNode(!0)),c.style.cssText=`position:absolute;left:${C.x}px;top:${C.y}px;width:${C.w}px;height:${C.h}px;z-index:1;pointer-events:none`,n.replaceChildren(i),o=t,x();let G=w(n),P=v?(u===\"in\"?c:n).querySelector(`[data-path=\"${CSS.escape(v)}\"]`):null,m=U({view:{x:0,y:0,w:a.clientWidth,h:a.clientHeight},ghostBox:C,liveBox:G,anchor:P?w(P):void 0,dir:u}),b=c.style,$=n.style;b.transition=\"none\",b.transformOrigin=m.gOrigin,b.transform=\"none\",b.opacity=\"1\",$.transition=\"none\",$.transformOrigin=m.lOrigin,$.transform=m.lStart,$.opacity=\"0\",n.offsetHeight,b.transition=`transform ${m.ms}ms ${m.ease}, opacity ${Math.round(m.ms*.55)}ms ${m.ease}`,$.transition=`transform ${m.ms}ms ${m.ease}, opacity ${Math.round(m.ms*.6)}ms ${m.ease} ${Math.round(m.ms*.25)}ms`,b.transform=m.gEnd,b.opacity=\"0\",$.transform=\"none\",$.opacity=\"1\";let I=!1,q=0,R=()=>{I||(I=!0,cancelAnimationFrame(q),c.replaceChildren(),c.removeAttribute(\"style\"),n.removeAttribute(\"style\"))},z=()=>{getComputedStyle(n).transform===\"none\"?R():q=requestAnimationFrame(z)};q=requestAnimationFrame(z),setTimeout(R,m.ms+1e3)}function k(t){let s=t===l?null:h(o,t,d);s&&(l=t,document.documentElement.dataset.theme=t,n.replaceChildren(s))}function M(t){let s=e.flows[o]??0,r=d+t;if(s&&r>=1&&r<=s){let v=h(o,l,r);v&&(d=r,n.replaceChildren(v),x());return}let i=e.views.findIndex(v=>v.name===o),u=e.views[i+t];u&&g(u.name,t<0)}n.addEventListener(\"click\",t=>{let r=t.target.closest?.(\"[data-path]\")?.getAttribute(\"data-path\");if(r){let i=O(e.views,o,r);if(i)return g(i.name)}if(!r){let i=S(e.views,o,y(o));i&&g(i)}});let A=document.querySelector(\"#sq-theme\");A&&(A.onclick=()=>k(e.themes[(e.themes.indexOf(l)+1)%e.themes.length]));function B(t){if(t!==f){if(f=t,document.body.classList.toggle(\"presenting\",t),t){if(document.documentElement.requestFullscreen?.().catch(()=>{}),(e.flows[o]??0)&&!d){let r=h(o,l,1);r&&(d=1,n.replaceChildren(r))}}else if(document.fullscreenElement&&document.exitFullscreen?.().catch(()=>{}),d){let s=h(o,l,0);s&&(d=0,n.replaceChildren(s))}x()}}let N,V=()=>{document.body.classList.remove(\"idle\"),clearTimeout(N),f&&(N=setTimeout(()=>document.body.classList.add(\"idle\"),3500))};addEventListener(\"mousemove\",V),addEventListener(\"keydown\",V);let J=()=>k(e.themes[(e.themes.indexOf(l)+1)%e.themes.length]);addEventListener(\"keydown\",t=>{if(!(t.metaKey||t.ctrlKey||t.altKey))switch(t.key){case\"ArrowRight\":case\"PageDown\":case\" \":case\"Enter\":t.preventDefault(),M(1);break;case\"ArrowLeft\":case\"PageUp\":t.preventDefault(),M(-1);break;case\"ArrowUp\":case\"Backspace\":{let s=S(e.views,o,y(o));s&&(t.preventDefault(),g(s));break}case\"Home\":t.preventDefault(),g(e.views[0].name);break;case\"End\":t.preventDefault(),g(e.views[e.views.length-1].name,!0);break;case\"Escape\":f&&(t.preventDefault(),B(!1));break;case\"p\":case\"P\":B(!f);break;case\"f\":case\"F\":document.fullscreenElement?document.exitFullscreen?.().catch(()=>{}):document.documentElement.requestFullscreen?.().catch(()=>{});break;case\"t\":case\"T\":e.themes.length>1&&J();break}}),addEventListener(\"fullscreenchange\",()=>{!document.fullscreenElement&&f&&B(!1)});let D=document.querySelector(\"#sq-present\");D&&(D.onclick=()=>B(!f));let F=()=>{let t=decodeURIComponent(location.hash.slice(1));t&&t!==o&&e.views.some(s=>s.name===t)&&g(t)};if(addEventListener(\"hashchange\",F),document.documentElement.dataset.theme=l,l!==e.themes[0]){let t=h(o,l);t&&n.replaceChildren(t)}x(),F()}document.readyState===\"loading\"?addEventListener(\"DOMContentLoaded\",j):j();})();";
|
|
@@ -25,11 +25,15 @@ function boot() {
|
|
|
25
25
|
let step = 0;
|
|
26
26
|
let presenting = false;
|
|
27
27
|
// The reader's own preference wins over the author's, when the file carries
|
|
28
|
-
// a palette that matches it — the same rule an adaptive SVG follows.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// a palette that matches it — the same rule an adaptive SVG follows. Both
|
|
29
|
+
// directions: a light-mode reader of a dark-entry file gets light, not just
|
|
30
|
+
// the reverse (the entry is dark by default now, so one-way promotion would
|
|
31
|
+
// have made the system setting a no-op for half of readers).
|
|
32
|
+
if (data.themes.length > 1) {
|
|
33
|
+
const wantDark = matchMedia("(prefers-color-scheme: dark)").matches;
|
|
34
|
+
const match = data.themes.find((t) => t.includes("dark") === wantDark);
|
|
35
|
+
if (match)
|
|
36
|
+
theme = match;
|
|
33
37
|
}
|
|
34
38
|
// The entry view is inline rather than in a <template>, so that a reader
|
|
35
39
|
// whose browser never runs this script still sees a diagram. That means it is
|
package/dist/render/html.js
CHANGED
|
@@ -66,8 +66,11 @@ export async function exportHTML(files, opts = {}) {
|
|
|
66
66
|
// A document has one palette at a time and a button to change it, so the
|
|
67
67
|
// theme is the document's rather than each view's — a per-view `theme` is
|
|
68
68
|
// deliberately overridden here, the way `--theme` overrides it elsewhere.
|
|
69
|
-
const base = opts.themes?.[0] ?? built.model.fileTheme ?? "
|
|
70
|
-
|
|
69
|
+
const base = opts.themes?.[0] ?? built.model.fileTheme ?? "dark";
|
|
70
|
+
// the counterpart pairs in either direction: light names dark, and dark is
|
|
71
|
+
// found by being named — so a dark entry still bundles light for the toggle
|
|
72
|
+
const mate = themes[base]?.pairsWith ?? Object.values(themes).find((t) => t.pairsWith === base)?.name;
|
|
73
|
+
const palette = opts.themes ?? [base, mate].filter(Boolean);
|
|
71
74
|
for (const name of palette)
|
|
72
75
|
if (!themes[name])
|
|
73
76
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squinch/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The Squinch engine: parse, model, layout and deterministic SVG rendering for architecture diagrams as code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"@lezer/lr": "^1.4.10",
|
|
34
34
|
"elkjs": "^0.12.0",
|
|
35
35
|
"fast-xml-parser": "^5.10.1",
|
|
36
|
-
"@squinch/pack-aws": "0.
|
|
37
|
-
"@squinch/pack-azure": "0.
|
|
38
|
-
"@squinch/pack-k8s": "0.
|
|
39
|
-
"@squinch/pack-logos": "0.
|
|
40
|
-
"@squinch/pack-sys": "0.
|
|
36
|
+
"@squinch/pack-aws": "0.2.0",
|
|
37
|
+
"@squinch/pack-azure": "0.2.0",
|
|
38
|
+
"@squinch/pack-k8s": "0.2.0",
|
|
39
|
+
"@squinch/pack-logos": "0.2.0",
|
|
40
|
+
"@squinch/pack-sys": "0.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@fontsource/ibm-plex-mono": "^5.3.0",
|