diff --git "a/studio/assets/main-DD_Xvh8j.js" "b/studio/assets/main-DD_Xvh8j.js" deleted file mode 100644--- "a/studio/assets/main-DD_Xvh8j.js" +++ /dev/null @@ -1,17 +0,0 @@ -var No=Object.defineProperty;var Io=(e,t,s)=>t in e?No(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var Y=(e,t,s)=>Io(e,typeof t!="symbol"?t+"":t,s);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))o(a);new MutationObserver(a=>{for(const i of a)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&o(r)}).observe(document,{childList:!0,subtree:!0});function s(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function o(a){if(a.ep)return;a.ep=!0;const i=s(a);fetch(a.href,i)}})();async function Dt(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}function Oo(){return!!(globalThis||window).isTauri}var Hn;(function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_SUSPENDED="tauri://suspended",e.WINDOW_RESUMED="tauri://resumed",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"})(Hn||(Hn={}));var Un;(function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"})(Un||(Un={}));var jn;(function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"})(jn||(jn={}));var Vn;(function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"})(Vn||(Vn={}));var Yn;(function(e){e.Default="default",e.FluentOverlay="fluentOverlay"})(Yn||(Yn={}));var Zn;(function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"})(Zn||(Zn={}));var Jn;(function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"})(Jn||(Jn={}));const _o=` - -`;function n(e,t=null,...s){const[o,...a]=e.split("."),i=document.createElement(o||"div");return a.length&&(i.className=a.join(" ")),Bo(i,t),mn(i,s),i}function xe(e,t=null,...s){const[o,...a]=e.split("."),i=document.createElementNS("http://www.w3.org/2000/svg",o||"g");a.length&&i.setAttribute("class",a.join(" "));for(const[r,l]of Object.entries(t??{}))l==null||l===!1||(r.startsWith("on")&&typeof l=="function"?i.addEventListener(r.slice(2),l):i.setAttribute(r,String(l)));return mn(i,s),i}function Bo(e,t){for(const[s,o]of Object.entries(t??{}))o==null||o===!1||(s==="class"?e.className=e.className?`${e.className} ${o}`:String(o):s==="style"&&typeof o=="object"?Object.assign(e.style,o):s.startsWith("on")&&typeof o=="function"?e.addEventListener(s.slice(2),o):s==="value"&&e instanceof HTMLInputElement?e.value=String(o):o===!0?e.setAttribute(s,""):e.setAttribute(s,String(o)))}function mn(e,t){for(const s of t.flat(8))s==null||s===!1||e.append(s instanceof Node?s:document.createTextNode(String(s)))}function F(e,...t){e.replaceChildren(),mn(e,t)}const Xn={image:"M3 3h18v18H3zM3 15l5-5 4 4 3-3 6 6M8.5 8.5h.01",type:"M4 7V4h16v3M9 20h6M12 4v16",droplet:"M12 2.7 6.7 8a7.5 7.5 0 1 0 10.6 0Z",wand:"m3 21 9-9M15 4V2M15 16v-2M8 9h2M20 9h2M17.8 11.8 19 13M15 9h.01M17.8 6.2 19 5M12.8 6.2 11.6 5",layers:"m12 2 9 5-9 5-9-5 9-5M3 12l9 5 9-5M3 17l9 5 9-5",palette:"M12 21a9 9 0 1 1 0-18c4.97 0 9 3.58 9 8 0 1.66-1.34 3-3 3h-2a2 2 0 0 0-1.6 3.2 2 2 0 0 1-1.6 3.2ZM7.5 10.5h.01M10.5 7.5h.01M14 7.5h.01M16.5 10.5h.01",check:"m20 6-11 11-5-5",checkCircle:"M22 11.1V12a10 10 0 1 1-5.9-9.1M22 4 12 14.01l-3-3",x:"M18 6 6 18M6 6l12 12",alert:"M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z",info:"M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM12 16v-4M12 8h.01",clock:"M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM12 6v6l4 2",download:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3",upload:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12",globe:"M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10Z",copy:"M20 9H11a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2ZM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1",folder:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",settings:"M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6 1.65 1.65 0 0 0 10 3.09V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z",chevronDown:"m6 9 6 6 6-6",chevronRight:"m9 18 6-6-6-6",minus:"M5 12h14",pen:"M21.17 6.81a2.82 2.82 0 0 0-3.99-3.99L3.84 16.17a2 2 0 0 0-.5.83l-1.32 4.35a.5.5 0 0 0 .62.62l4.35-1.32a2 2 0 0 0 .83-.5Z",square:"M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2Z",crosshair:"M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM22 12h-4M6 12H2M12 6V2M12 22v-4",zap:"M4 14h7l-3 8 12-12h-7l3-8Z",play:"m6 3 14 9-14 9V3Z",pause:"M14 4h4v16h-4zM6 4h4v16H6z",rotate:"M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5",share:"M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13",file:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM14 2v6h6",maximize:"M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3",external:"M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"};function Z(e,t=16){const s=Xn[e]??Xn.info;return xe("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round","aria-hidden":"true",focusable:"false"},xe("path",{d:s}))}function Ds(e=18){const t=Ro.cloneNode(!0);t.removeAttribute("color");const[,,s,o]=(t.getAttribute("viewBox")??"0 0 1 1").split(/\s+/).map(Number);return t.setAttribute("height",String(e)),t.setAttribute("width",String(Math.round(e*s/o))),t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false"),t}const Ro=(()=>{const e=new DOMParser().parseFromString(_o,"image/svg+xml").documentElement;return document.importNode(e,!0)})();function Go(e,t){let s=0;const o=i=>[...i.dataTransfer?.types??[]].includes("Files"),a=()=>{s=0,e.set({dragging:!1}),document.body.classList.remove("dragging")};window.addEventListener("dragenter",i=>{o(i)&&(s++,e.set({dragging:!0}),document.body.classList.add("dragging"))}),window.addEventListener("dragover",i=>{o(i)&&(i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy"))}),window.addEventListener("dragleave",i=>{o(i)&&(s=Math.max(0,s-1),s===0&&a())}),window.addEventListener("drop",i=>{if(!o(i))return;i.preventDefault(),a();const r=[...i.dataTransfer?.files??[]];r.length&&t(r)}),Wo(t)}function Wo(e){window.addEventListener("paste",t=>{if(t.target?.closest?.("input, textarea, [contenteditable]"))return;let o=[...t.clipboardData?.files??[]];o.length||(o=[...t.clipboardData?.items??[]].filter(a=>a.kind==="file").map(a=>a.getAsFile()).filter(a=>a!==null)),o.length&&(t.preventDefault(),e(o))})}const qo=760;function Ho(e){const t=window.matchMedia(`(max-width: ${qo}px)`).matches,s=window.matchMedia("(pointer: coarse)").matches;if(!(t&&s))return;const o=n("div.phonenote",{role:"dialog","aria-label":"Best on a larger screen"},n("div.phonecard",null,n("span.serif",{style:{fontSize:"22px"}},"Best on a larger screen"),n("p",null,"Inkvec Studio Lite is a full editor: a viewer, a rail of controls and a palette side by side. On a phone they do not fit. Open this page on a laptop or desktop to use it; everything runs in your browser and nothing is uploaded."),n("button.btn",{onclick:()=>o.remove()},"Continue anyway")));e.append(o)}async function Uo(){const e=new URLSearchParams(window.location.search),t=e.get("sample"),s=e.get("open")==="handoff";return!t&&!s?null:(window.history.replaceState(null,"",window.location.pathname),t?{sample:t}:new Promise(o=>{let a;try{a=indexedDB.open("inkvec-handoff",1)}catch{o(null);return}a.onupgradeneeded=()=>a.result.createObjectStore("files"),a.onerror=()=>o(null),a.onsuccess=()=>{const i=a.result.transaction("files","readwrite").objectStore("files"),r=i.get("open");r.onsuccess=()=>{i.delete("open");const l=r.result;o(l?.bytes?{name:l.name??"image",bytes:l.bytes}:null)},r.onerror=()=>o(null)}}))}let vt=0,Mt=null,rn=0,tt=!1,Fs=performance.now();const Ut=e=>(e/(1024*1024)).toFixed(1);function jo(){rn=0;const e=Mt;if(Mt=null,!e||tt)return;vt=e.fraction;const t=document.getElementById("boot-bar"),s=document.getElementById("boot-fill"),o=document.getElementById("boot-status"),a=document.getElementById("boot-pct"),i=document.getElementById("boot-detail"),r=Math.round(vt*100);s&&(s.style.transform=`scaleX(${vt})`),t?.setAttribute("aria-valuenow",String(r)),o&&(o.textContent=e.text),a&&(a.textContent=`${r}%`),i&&(i.textContent=e.detail)}function at(e,t,s=""){tt||(Fs=performance.now(),Mt={text:e,fraction:Math.max(vt,Mt?.fraction??0,Math.min(1,t)),detail:s},rn||(rn=requestAnimationFrame(jo)))}function Vo(e,t){const s=t>0?Math.min(1,e/t):0;at("Downloading the engine",.04+.8*s,t>0?`${Ut(e)} of ${Ut(t)} MB`:`${Ut(e)} MB`)}function Yo(){at("Starting the engine",.88,"Compiling, and starting a thread per core")}function Zo(e,t){at(e,.9+.1*Math.min(1,Math.max(0,t)))}function Jo(){if(tt)return;const e=document.getElementById("boot-fill");e&&(e.style.transitionDuration="0.12s"),at("Ready",1),window.setTimeout(()=>{tt=!0;const t=document.getElementById("boot");t&&(t.classList.add("leaving"),window.setTimeout(()=>t.remove(),260))},130)}{window.__inkvecBoot=!0,at("Loading the Studio",.03);const e=window.setInterval(()=>{if(tt){window.clearInterval(e);return}if(performance.now()-Fs>2e4){const t=document.getElementById("boot-detail");t&&(t.textContent="Still waiting for the network. On a slow connection the first visit can take a minute; later ones start from the browser's cache.")}},2e3)}function Xo(){let e;try{e=window.self!==window.top}catch{e=!0}const t=window.location.ancestorOrigins;if(t)for(let a=0;as.classList.toggle("fullscreen",!!document.fullscreenElement);document.addEventListener("fullscreenchange",o),o()}function Re(e,t,s="application/octet-stream"){const o=t instanceof Blob?t:new Blob([t],{type:s}),a=URL.createObjectURL(o),i=document.createElement("a");i.href=a,i.download=e,i.style.display="none",document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(a),6e4)}var Ko={pkg:5814248,"pkg-threads":5846361};const Kn="inkvec-studio-lite:prefs",Qn="inkvec-denoiser-v1",Qo="inkvec-ort-",ea=3e4,ta=700,es="Not available in the browser. Inkvec Studio, the desktop app, has it.",ts=[["flat-logo.png","Flat logo"],["icon-64.png","Icon, 64 px"],["crest-filigree.png","Crest, filigree"],["signature-bw.png","Signature, B&W"]];class na{constructor(){Y(this,"worker",null);Y(this,"ready",null);Y(this,"queue",[]);Y(this,"running",null);Y(this,"nextId",1);Y(this,"listeners",new Map);Y(this,"generation",0);Y(this,"previewGeneration",0);Y(this,"shared",null);Y(this,"source",null);Y(this,"prefs",null);Y(this,"prefsTimer",0);Y(this,"denoiser",null);Y(this,"denoiserPort",null);Y(this,"downloading",!1);Y(this,"fetchState",{phase:"idle",got:0,total:null});Y(this,"wanted",!1);Y(this,"cancelled",!1);Y(this,"retried",!1);Y(this,"info",null);Y(this,"restartMs",null);Y(this,"stopped",[]);Y(this,"stopTimer",0);Y(this,"denoiserRuns",0);Y(this,"last",null)}base(){return new URL("./",document.baseURI).href}start(){const t=typeof crossOriginIsolated<"u"&&crossOriginIsolated;t&&!this.shared&&(this.shared=new Int32Array(new SharedArrayBuffer(4)));const s=new Worker(new URL(""+new URL("engine.worker-wbMABDxy.js",import.meta.url).href,import.meta.url),{type:"module",name:"inkvec-engine"});this.worker=s;let o=null;if(t){const i=new MessageChannel;o=i.port1,this.denoiser?this.denoiser.postMessage({type:"port",port:i.port2},[i.port2]):this.denoiserPort=i.port2}this.ready=new Promise((i,r)=>{s.onmessage=l=>{const c=l.data;c.type==="loading"?(Vo(c.got,c.total),c.done&&Yo()):c.type==="instantiated"?t&&(this.startDenoiser(c.denoiserUrl,c.denoiserSha256),this.prefetchDenoiser()):c.type==="ready"?(this.info={threads:c.threads,isolated:c.isolated,version:c.version},c.poolError&&console.warn("inkvec: thread pool did not start:",c.poolError),t&&this.startDenoiser(c.denoiserUrl,c.denoiserSha256),i(c)):c.type==="loadfail"?r(new Error(`The engine could not load: ${c.error}`)):c.type==="progress"?c.generation===this.generation&&this.emit("trace:progress",{generation:c.generation,...JSON.parse(c.json)}):c.type==="reply"&&this.finish(c)},s.onerror=l=>{r(new Error(`The engine stopped: ${l.message||"unknown error"}`)),this.crashed("The engine stopped unexpectedly.")}});const a=o?[o]:[];return s.postMessage({type:"init",base:this.base(),token:"3fbf0151b8d5",generations:this.shared?this.shared.buffer:null,denoiserPort:o,wasmBytes:Ko},a),this.ready}whenReady(){return this.ready??this.start()}crashed(t){const s=this.running;this.running=null,window.clearTimeout(this.stopTimer),this.worker?.terminate(),this.worker=null,this.ready=null,s&&s.reject(new Error(t)),s?.op==="open_bytes"&&(this.source=null);const o=this.source,a=performance.now();this.whenReady().then(async()=>{o&&await this.enqueue("open_bytes",-1,{bytes:o.bytes.slice(),name:o.name}).catch(()=>{}),this.restartMs=performance.now()-a,this.pump()})}supersede(t,s){window.clearTimeout(this.stopTimer);const o=this.running;if(o?.op!=="trace"||o.payload.generation===this.generation||!t&&s&&sa(o.payload.request,s))return;const a=performance.now(),i=()=>{this.running!==o||o.payload.generation===this.generation||(this.stopped.push({generation:o.payload.generation,asked:a,killed:performance.now()}),this.crashed("superseded by a newer request"))},r=a-(o.started??a),l=t?0:Math.max(0,(this.restartMs??ta)-r);l===0?i():this.stopTimer=window.setTimeout(i,l)}finish(t){const s=this.running;if(!(!s||s.id!==t.id)){if(this.last={op:s.op,ms:t.ms??0,mem:t.mem??null},this.running=null,window.clearTimeout(this.stopTimer),t.ok)s.resolve(t.value);else if(t.crashed){this.crashed(t.error??"The engine stopped."),s.reject(new Error(t.error??"The engine stopped."));return}else s.reject(new Error(t.error??"failed"));this.pump()}}enqueue(t,s,o,a){return new Promise((i,r)=>{if(a){for(const l of this.queue.filter(c=>c.kind===a))l.reject(new Error("superseded by a newer request"));this.queue=this.queue.filter(l=>l.kind!==a)}this.queue.push({id:this.nextId++,op:t,priority:s,kind:a,payload:o,resolve:i,reject:r}),this.queue.sort((l,c)=>l.priority-c.priority||l.id-c.id),this.whenReady().then(()=>this.pump(),l=>{for(const c of this.queue)c.reject(l);this.queue=[]})})}pump(){if(this.running||!this.worker||!this.info)return;const t=this.queue.shift();if(!t)return;if(t.op==="trace"&&t.payload.generation!==this.generation)return t.resolve(null),this.pump();if(t.op==="preview"&&t.payload.generation!==this.previewGeneration)return t.resolve(null),this.pump();this.running=t,t.started=performance.now();const s=[];t.op==="open_bytes"&&s.push(t.payload.bytes.buffer),this.worker.postMessage({type:"job",id:t.id,op:t.op,...t.payload},s)}emit(t,s){for(const o of this.listeners.get(t)??[])o(s)}async listen(t,s){const o=i=>s({payload:i});let a=this.listeners.get(t);return a||this.listeners.set(t,a=new Set),a.add(o),()=>void a?.delete(o)}async invoke(t,s={}){return await this.dispatch(t,s)}async dispatch(t,s){switch(t){case"capabilities":{const o=await this.denoiserStatus();return this.enqueue("capabilities",0,{supported:o.supported,installed:o.installed,bytes:o.bytes})}case"startup_progress":return Zo(String(s.text??""),Number(s.progress??0)),null;case"app_ready":return Jo(),null;case"open_path":throw new Error("A browser opens files you choose or drop, not paths.");case"open_bytes":{const o=s.bytes,a=o instanceof Uint8Array?o:new Uint8Array(o);return this.open(a,s.name??null)}case"open_sample":{const o=String(s.name);if(!ts.some(([i])=>i===o))throw new Error(`${o} is not one of the bundled samples`);const a=await fetch(new URL(`samples/${o}`,this.base()));if(!a.ok)throw new Error(`cannot fetch the sample ${o}: HTTP ${a.status}`);return this.open(new Uint8Array(await a.arrayBuffer()),o)}case"list_samples":return ts.map(([o,a])=>({file:o,label:a,preview:new URL(`samples/${o}`,this.base()).href}));case"launch_path":return null;case"start_trace":{const o=s.request,a=this.bump();this.rememberTrace(o.settings);const i=this.withoutUnreadyDenoiser(o);this.supersede(!1,i);const r=await this.loadPrefs();return this.enqueue("trace",1,{request:i,draftPx:r.draftPx??512,draftSeconds:r.draftSeconds??.4,generation:a},"trace").then(l=>{l&&a===this.generation&&this.emit("trace:done",{generation:a,outcome:l})},l=>{a===this.generation&&!/superseded/.test(l.message)&&this.emit("trace:done",{generation:a,outcome:{state:"failed",message:l.message}})}),a}case"cancel_trace":return this.bump(),this.queue=this.queue.filter(o=>o.kind==="trace"?(o.resolve(null),!1):!0),this.supersede(!0),null;case"start_preview":{const o=this.withoutUnreadyDenoiser(s.request),a=++this.previewGeneration,i=await this.loadPrefs();return this.enqueue("preview",2,{request:o,draftPx:i.draftPx??512,draftSeconds:i.draftSeconds??.4,generation:a},"preview").then(r=>{r&&a===this.previewGeneration&&this.emit("preview:done",{generation:a,outcome:r})},r=>{a===this.previewGeneration&&!/superseded/.test(r.message)&&this.emit("preview:done",{generation:a,outcome:{state:"failed",message:r.message}})}),a}case"cancel_previews":{this.previewGeneration++,this.queue=this.queue.filter(a=>a.kind==="preview"?(a.resolve(null),!1):!0);const o=this.running;return o?.op==="preview"&&o.started!==void 0&&performance.now()-o.started>500&&this.crashed("superseded by a newer request"),null}case"source_facts":case"trace_bands":case"snap_inks":case"match_palette":case"plan_export":case"minify_svg":case"fab_analyze":return this.enqueue(t,0,{args:s});case"fab_prepare":return this.enqueue(t,0,{args:s},"fab");case"write_export":{const o=s.request,a=await this.enqueue("export",0,{request:o,zip:!0}),i=(this.source?.name??"image").replace(/\.[^.]+$/,"")||"image";if(a.length===1&&a[0].name)return Re(a[0].name,a[0].data),[a[0].name];const r=`${i}-inkvec.zip`;return a[0]&&Re(r,a[0].data,"application/zip"),[r]}case"read_text_file":throw new Error("A browser reads files you choose or drop, not paths.");case"save_bytes":{const o=String(s.path);return Re(o.split(/[\\/]/).pop()||"download",new Uint8Array(s.bytes)),null}case"batch_scan":case"batch_start":case"batch_stats_csv":throw new Error(es);case"batch_pause":case"batch_cancel":return null;case"denoiser_status":return this.denoiserStatus();case"denoiser_download":return this.downloadDenoiser();case"denoiser_cancel":return this.fetchState.phase==="downloading"&&(this.cancelled=!0,this.wanted=!1,this.denoiser?.postMessage({type:"abort"})),this.downloading&&(this.downloading=!1,this.emit("denoiser:done",{ok:!1,message:"cancelled",status:await this.denoiserStatus()})),null;case"denoiser_remove":try{await caches.delete(Qn);for(const o of await caches.keys())o.startsWith(Qo)&&await caches.delete(o)}catch{}return this.denoiser?.postMessage({type:"forget"}),this.fetchState.phase!=="ready"&&this.setFetch({phase:"idle",got:0,total:null}),this.denoiserStatus();case"load_prefs":return this.loadPrefs();case"save_prefs":{this.prefs={...this.prefs??{},...s.prefs},this.storePrefs();const o=await this.enqueue("sanitise_prefs",0,{args:{prefs:s.prefs}});return this.prefs=o,this.storePrefs(),o}case"reset_prefs":{const o=await this.loadPrefs();return this.prefs=await this.enqueue("reset_prefs",0,{args:{prefs:o}}),this.storePrefs(),this.prefs}case"third_party_notices":{const a=(await Promise.all(["STUDIO_THIRD_PARTY.md","THIRD_PARTY.md"].map(i=>fetch(new URL(i,this.base())).then(r=>r.ok?r.text():null,()=>null)))).filter(i=>!!i);if(!a.length)throw new Error("the notices could not be fetched");return a.join(` - - -`)}case"check_update":return{latest:null,newer:!1,url:"https://github.com/logolabs/inkvec/releases",offline:"a web page is always the version it is served as"};case"cli_status":case"context_menu_status":case"install_cli":case"remove_cli":case"install_context_menu":case"remove_context_menu":return{available:!1,installed:!1,path:null,note:es}}throw new Error(`${t} is not something the browser build does`)}bump(){return this.generation++,this.shared&&Atomics.store(this.shared,0,this.generation),this.generation}async open(t,s){return this.source={bytes:t.slice(),name:s},this.bump(),this.queue=this.queue.filter(o=>o.kind?(o.resolve(null),!1):!0),this.supersede(!0),this.enqueue("open_bytes",0,{bytes:t,name:s})}async loadPrefs(){if(this.prefs)return this.prefs;let t=null;try{const s=localStorage.getItem(Kn);t=s?JSON.parse(s):null}catch{t=null}return this.prefs=await(t?this.enqueue("sanitise_prefs",0,{args:{prefs:t}}):this.enqueue("default_prefs",0,{args:{}})),this.prefs}rememberTrace(t){if(!this.prefs)return;const s={...t};delete s.colourGroups,this.prefs={...this.prefs,trace:s},window.clearTimeout(this.prefsTimer),this.prefsTimer=window.setTimeout(()=>this.storePrefs(),1e3)}storePrefs(){try{localStorage.setItem(Kn,JSON.stringify(this.prefs))}catch{}}startDenoiser(t,s){if(this.denoiser||!this.denoiserPort)return;const o=new Worker(new URL(""+new URL("denoiser.worker-DjPuHhk4.js",import.meta.url).href,import.meta.url),{type:"module",name:"inkvec-denoiser"});this.denoiser=o,o.onmessage=a=>void this.denoiserMessage(a.data),o.postMessage({type:"init",base:this.base(),token:"3fbf0151b8d5",url:t,sha256:s,port:this.denoiserPort},[this.denoiserPort])}setFetch(t){this.fetchState={...this.fetchState,retrace:void 0,...t},this.emit("denoiser:fetch",this.fetchState)}get denoiserFetch(){return this.fetchState}async denoiserMessage(t){const s=this.fetchState.phase;switch(t.type){case"progress":if(s==="preparing"||s==="ready")return;this.setFetch({phase:"downloading",got:t.got??0,total:t.total??null,message:void 0,saveData:void 0}),this.downloading&&this.emit("denoiser:progress",{got:t.got,total:t.total});return;case"stored":{if(s==="preparing"||s==="ready")return;const o=this.fetchState.total??this.fetchState.got;this.setFetch({phase:"stored",got:o,total:o,message:void 0}),this.downloading&&(this.downloading=!1,this.emit("denoiser:done",{ok:!0,status:await this.denoiserStatus()}));return}case"preparing":this.setFetch({phase:"preparing",message:void 0});return;case"ran":this.denoiserRuns++;return;case"loaded":{const o=this.wanted;this.wanted=!1,this.setFetch({phase:"ready",message:void 0,retrace:o});return}case"failed":{const o=t.message??"the download did not finish";if(this.cancelled||/abort/i.test(o)){this.cancelled=!1,this.setFetch({phase:"idle",message:void 0});return}this.setFetch({phase:"failed",message:o}),this.downloading&&(this.downloading=!1,this.emit("denoiser:done",{ok:!1,message:o,status:await this.denoiserStatus()})),t.during==="download"&&!this.retried&&(this.retried=!0,window.setTimeout(()=>{this.fetchState.phase==="failed"&&this.denoiser?.postMessage({type:this.wanted?"prepare":"prefetch"})},ea));return}}}prefetchDenoiser(){if(navigator.connection?.saveData){this.setFetch({phase:"idle",saveData:!0});return}this.denoiser?.postMessage({type:"prefetch"})}withoutUnreadyDenoiser(t){const s=t.settings?.cleanUpDamage;return!s||s==="off"||!this.denoiser||this.fetchState.phase==="ready"?t:(this.wanted=!0,this.cancelled=!1,this.denoiser.postMessage({type:"prepare"}),{...t,settings:{...t.settings,cleanUpDamage:"off"}})}async denoiserStatus(){const t=await this.whenReady();let s=!1,o=null;try{const a=await(await caches.open(Qn)).match(t.denoiserUrl);a&&(s=!0,o=Number(a.headers.get("content-length"))||(await a.blob()).size)}catch{}return{supported:t.isolated,installed:s,path:null,bytes:o,repo:t.denoiserRepo,sha256:t.denoiserSha256}}async downloadDenoiser(){if(await this.whenReady(),!this.denoiser)throw new Error("The denoiser needs a cross-origin isolated page.");return this.downloading=!0,this.cancelled=!1,this.denoiser.postMessage({type:this.wanted?"prepare":"download"}),null}}function sa(e,t){const s=o=>{const a={...o?.settings??{}};return delete a.minify,delete a.margin,JSON.stringify(Object.keys(a).sort().map(i=>[i,a[i]]))};return s(e)===s(t)}let dt=null;function Ns(){return dt||(dt=new na,globalThis.__inkvecStudioLite=dt),dt}function R(e,t){return Ns().invoke(e,t)}function ve(e,t){return Ns().listen(e,t)}const B={capabilities:()=>R("capabilities"),startupProgress:(e,t)=>R("startup_progress",{text:e,progress:t}),appReady:()=>R("app_ready"),openPath:e=>R("open_path",{path:e}),openBytes:(e,t)=>R("open_bytes",{bytes:e,name:t??null}),openSample:e=>R("open_sample",{name:e}),listSamples:()=>R("list_samples"),startTrace:(e,t)=>R("start_trace",{request:{settings:e,tier:t}}),cancelTrace:()=>R("cancel_trace"),startPreview:e=>R("start_preview",{request:{settings:e,tier:"draft"}}),cancelPreviews:()=>R("cancel_previews"),sourceFacts:e=>R("source_facts",{maxDim:e}),launchPath:()=>R("launch_path"),traceBands:e=>R("trace_bands",{generation:e}),snapInks:(e,t,s,o)=>R("snap_inks",{svg:e,snaps:t,width:s,height:o}),matchPalette:(e,t)=>R("match_palette",{traced:e,pasted:t}),planExport:e=>R("plan_export",{request:e}),writeExport:(e,t)=>R("write_export",{request:e,destination:t}),minify:(e,t)=>R("minify_svg",{svg:e,settings:t}),fabAnalyze:e=>R("fab_analyze",{svg:e}),fabPrepare:(e,t)=>R("fab_prepare",{svg:e,options:t}),readTextFile:e=>R("read_text_file",{path:e}),saveBytes:(e,t)=>R("save_bytes",{path:e,bytes:t}),batchScan:e=>R("batch_scan",{folder:e}),batchStart:e=>R("batch_start",{plan:e}),batchPause:e=>R("batch_pause",{paused:e}),batchCancel:()=>R("batch_cancel"),batchStatsCsv:()=>R("batch_stats_csv"),denoiserStatus:()=>R("denoiser_status"),denoiserDownload:()=>R("denoiser_download"),denoiserCancel:()=>R("denoiser_cancel"),denoiserRemove:()=>R("denoiser_remove"),loadPrefs:()=>R("load_prefs"),savePrefs:e=>R("save_prefs",{prefs:e}),resetPrefs:()=>R("reset_prefs"),thirdPartyNotices:()=>R("third_party_notices"),checkUpdate:()=>R("check_update"),cliStatus:()=>R("cli_status"),installCli:()=>R("install_cli"),removeCli:()=>R("remove_cli"),contextMenuStatus:()=>R("context_menu_status"),installContextMenu:()=>R("install_context_menu"),removeContextMenu:()=>R("remove_context_menu")},ke={traceProgress:e=>ve("trace:progress",t=>e(t.payload)),traceDone:e=>ve("trace:done",t=>e(t.payload)),previewDone:e=>ve("preview:done",t=>e(t.payload)),openPath:e=>ve("open:path",t=>e(t.payload)),batchRow:e=>ve("batch:row",t=>e(t.payload)),batchTotals:e=>ve("batch:totals",t=>e(t.payload)),batchFinished:e=>ve("batch:finished",t=>e(t.payload)),denoiserProgress:e=>ve("denoiser:progress",t=>e(t.payload)),denoiserDone:e=>ve("denoiser:done",t=>e(t.payload)),denoiserFetch:e=>ve("denoiser:fetch",t=>e(t.payload))};function oa(e,t){return{caps:null,prefs:null,tab:"vectorize",screen:null,source:null,settings:e,preset:"logo",generation:0,tracing:!1,tracingTier:"final",liveStages:[],traceStarted:0,traceEnded:0,liveNow:null,traceLog:[],logOpen:!0,result:null,resultGeneration:0,bandsMissing:!1,previous:null,svg:null,palette:[],snaps:[],colourGroups:[],groupSuggestions:null,resultGroups:[],dismissedGroups:[],simplifyTo:null,paletteSelection:[],hoverFill:null,losses:[],report:null,worstCorner:null,stageState:{kind:"empty"},justSwapped:!1,view:"side",show:{fill:!0,wireframe:!1,anchors:!1,handles:!1,certainty:!1},zoom:1,pan:{x:0,y:0},fitted:!0,flicked:!1,wipe:.5,detail:!1,railTab:"result",groupsOpen:{Detail:!0,Colour:!0,Shape:!0,Output:!1},exportOpen:!1,dragging:!1,minify:{name:null,before:null,settings:t,result:null,error:null},fab:{name:null,svg:null,analysis:null,options:Is(),plan:null,error:null,shown:-1,unit:"mm",showProblems:!0,preset:null,dxf:!1,gcode:!1},batch:{folder:null,outputDir:null,preset:"logo",skipExisting:!0,rows:[],totals:null,running:!1,paused:!1,failuresFirst:!1},update:null,denoiserFetch:null,auto:null,facts:null,chooser:!1,wizard:null,compare:null,autoNoteHidden:!1}}function Is(){return{mode:"singleColour",widthMm:100,include:[],order:[],bleedMm:.8,minFeatureMm:.8,removeThin:!1,stickerMarginMm:3,bridgeMm:1.5,stencilMarginMm:10,maxLineMm:0,penMm:.4,registration:!0,weedBorderMm:0,mirror:!1,cutStyle:"filled",sizeCheckMm:0,dogboneMm:0,gcodeFeedMmMin:1e3,gcodePower:1e3,gcodePasses:1,fileUnits:"mm",kerfMm:0,toleranceMm:.05,mergeDeltaE:3}}class aa{constructor(t){Y(this,"listeners",new Set);this.state=t}set(t){const s=new Set;for(const[o,a]of Object.entries(t))this.state[o]!==a&&(this.state[o]=a,s.add(o));s.size&&this.emit(s)}touch(...t){this.emit(new Set(t))}on(t,s){const o=new Set(t),a=i=>{for(const r of i)if(o.has(r)){s();return}};return this.listeners.add(a),()=>this.listeners.delete(a)}emit(t){for(const s of[...this.listeners])s(t)}}function oe(e){return e.toLocaleString("en-US")}function ie(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function re(e){return e==null?"—":e.toFixed(2)}function nt(e){return e<1?`${Math.round(e*1e3)} ms`:`${e.toFixed(2)} s`}function bn(e,t){if(!e.source)return null;const s=Math.max(e.source.width,e.source.height),o=t==="draft"?Math.min(e.prefs?.draftPx??512,e.settings.traceSize):e.settings.traceSize;return Math.min(o,s)}function it(e,t){const s=t.mode==="fast"&&!t.blackAndWhite&&!t.lineArt;return e.modes==="qualityOnly"?!s:e.modes==="fastOnly"?s:!0}function $e(e,t=0){return`${(e*100).toFixed(t)}%`}function ia(e){const t=Math.max(0,Math.round(e));return t<60?`${t} s`:`${Math.floor(t/60)} min ${t%60} s`}function Os(e){return e==="macos"?"⌘":"Ctrl"}function ra(){return{preset:"logo",tab:"vectorize",railTab:"result",groupsOpen:{},view:"side",wipe:.5,show:{fill:!0,wireframe:!1,anchors:!1,handles:!1,certainty:!1},detail:!1,export:null,minify:null,minifyBackdrop:"auto",minifyView:"side",fab:null,fabUnit:"mm",fabShowProblems:!0,fabPreset:null,fabDxf:!1,fabGcode:!1,batchPreset:null,batchSkipExisting:!0,batchFailuresFirst:!1}}const Ce={export:null,minifyBackdrop:"auto",minifyView:"side"};let ln=()=>{};function la(){return{...Ce}}function cn(e){Object.assign(Ce,e),ln()}function ca(e){const t=Ce.export;return t?{...e,...t,pngSizes:[...t.pngSizes??e.pngSizes]}:e}function un(e){const t={...e.fab.options};return delete t.include,delete t.order,{preset:e.preset,tab:e.tab,railTab:e.railTab,groupsOpen:{...e.groupsOpen},view:e.view,wipe:e.wipe,show:{...e.show},detail:e.detail,export:Ce.export,minify:{...e.minify.settings},minifyBackdrop:Ce.minifyBackdrop,minifyView:Ce.minifyView,fab:t,fabUnit:e.fab.unit,fabShowProblems:e.fab.showProblems,fabPreset:e.fab.preset,fabDxf:e.fab.dxf,fabGcode:e.fab.gcode,batchPreset:e.batch.preset,batchSkipExisting:e.batch.skipExisting,batchFailuresFirst:e.batch.failuresFirst}}function dn(e){const t={...e};return delete t.colourGroups,t}function ua(e,t,s,o){const a=e.state,i={...ra(),...t.ui??{}};Ce.export=i.export,Ce.minifyBackdrop=i.minifyBackdrop,Ce.minifyView=i.minifyView,e.set({preset:i.preset&&o(i.preset)?i.preset:null,tab:s.includes(i.tab)?i.tab:"vectorize",railTab:i.railTab,groupsOpen:{...a.groupsOpen,...i.groupsOpen},view:i.view,wipe:i.wipe,show:{...a.show,...i.show},detail:i.detail,minify:{...a.minify,settings:{...a.minify.settings,...i.minify??{}}},fab:{...a.fab,options:{...Is(),...i.fab??{},include:[],order:[]},unit:i.fabUnit,showProblems:i.fabShowProblems,preset:i.fabPreset,dxf:i.fabDxf,gcode:i.fabGcode},batch:{...a.batch,preset:i.batchPreset&&o(i.batchPreset)?i.batchPreset:a.batch.preset,skipExisting:i.batchSkipExisting,failuresFirst:i.batchFailuresFirst}})}const da=["settings","preset","tab","railTab","groupsOpen","view","wipe","show","detail","minify","fab","batch"];function pa(e,t,s=500){let o=0,a=JSON.stringify({ui:un(e.state),trace:dn(e.state.settings)});const i=()=>{if(window.clearTimeout(o),o=0,!e.state.prefs)return;const c={ui:un(e.state),trace:dn(e.state.settings)},d=JSON.stringify(c);d!==a&&(a=d,t(c))};ln=()=>{window.clearTimeout(o),o=window.setTimeout(i,s)};const r=e.on(da,ln),l=()=>{document.visibilityState==="hidden"&&o&&i()};return window.addEventListener("pagehide",i),document.addEventListener("visibilitychange",l),()=>{r(),window.removeEventListener("pagehide",i),document.removeEventListener("visibilitychange",l)}}const ha="modulepreload",fa=function(e,t){return new URL(e,t).href},ns={},ga=function(t,s,o){let a=Promise.resolve();if(s&&s.length>0){let r=function(p){return Promise.all(p.map(h=>Promise.resolve(h).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};const l=document.getElementsByTagName("link"),c=document.querySelector("meta[property=csp-nonce]"),d=c?.nonce||c?.getAttribute("nonce");a=r(s.map(p=>{if(p=fa(p,o),p in ns)return;ns[p]=!0;const h=p.endsWith(".css"),f=h?'[rel="stylesheet"]':"";if(!!o)for(let b=l.length-1;b>=0;b--){const x=l[b];if(x.href===p&&(!h||x.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${p}"]${f}`))return;const g=document.createElement("link");if(g.rel=h?"stylesheet":ha,h||(g.as="script"),g.crossOrigin="",g.href=p,d&&g.setAttribute("nonce",d),document.head.appendChild(g),h)return new Promise((b,x)=>{g.addEventListener("load",b),g.addEventListener("error",()=>x(new Error(`Unable to preload CSS for ${p}`)))})}))}function i(r){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=r,window.dispatchEvent(l),!l.defaultPrevented)throw r}return a.then(r=>{for(const l of r||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})};async function ma(e={}){return typeof e=="object"&&Object.freeze(e),await Dt("plugin:dialog|open",{options:e})}async function ba(e={}){return typeof e=="object"&&Object.freeze(e),await Dt("plugin:dialog|save",{options:e})}async function ya(e,t){await Dt("plugin:opener|open_url",{url:e,with:t})}async function wa(e){return Dt("plugin:opener|reveal_item_in_dir",{paths:typeof e=="string"?[e]:e})}const Ft="Inkvec Studio Lite",_s="https://github.com/logolabs/inkvec/releases",va="Inkvec Studio for Windows, macOS and Linux: the native engine is faster, it traces whole folders in one batch, has no browser memory limit (traces up to 16384 px), and works offline.";function Bs(e){return{name:e.name||"pasted image",path:null,file:e}}async function yn(e,t=!1){return new Promise(s=>{const o=document.createElement("input");o.type="file",o.multiple=t,o.accept=e.flatMap(r=>r.extensions.map(l=>`.${l}`)).join(","),o.style.display="none";let a=!1;const i=r=>{a||(a=!0,o.remove(),s(r))};o.addEventListener("change",()=>i([...o.files??[]].map(Bs))),o.addEventListener("cancel",()=>i([])),document.body.append(o),o.click()})}async function Ct(e){if(e.file)return e.file.text();if(e.path)return B.readTextFile(e.path);throw new Error("nothing to read")}const ss=!1;async function Rs(e,t,s){const o=typeof t=="string"?new TextEncoder().encode(t):t;return Re(e,o),{path:null,name:e}}async function xa(e,t,s){const o=new TextEncoder,a=i=>typeof i=="string"?o.encode(i):i;{if(e.length===1)Re(e[0].name,a(e[0].data));else{const{zipStore:i}=await ga(async()=>{const{zipStore:r}=await import("./zip-DlX9-WSC.js");return{zipStore:r}},[],import.meta.url);Re(t,i(e.map(r=>({name:r.name,data:a(r.data)}))))}return{folder:null,first:null}}}const ka=!1;async function Nt(e){await navigator.clipboard.writeText(e)}async function It(e){{window.open(e,"_blank","noopener,noreferrer");return}}function Ot(e){return e&&ka?{label:"Show in folder",run:()=>void 0}:void 0}function Pe(e){const t={...e,cleanUpDamage:"off"};return JSON.stringify(Object.keys(t).sort().map(s=>[s,t[s]]))}class Sa{constructor(){Y(this,"results",new Map);Y(this,"queue",[]);Y(this,"running",null);Y(this,"epoch",0);Y(this,"early",new Map);Y(this,"listeners",new Set);ke.previewDone(({generation:t,outcome:s})=>this.done(t,s))}get(t){return this.results.get(t)}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}want(t){const s=new Set,o=t.filter(a=>{if(s.has(a.key))return!1;s.add(a.key);const i=this.results.get(a.key)?.state;return i!=="done"&&i!=="running"});if(o.map(a=>a.key).join(` -`)!==this.queue.map(a=>a.key).join(` -`)){for(const a of this.queue)this.results.get(a.key)?.state==="queued"&&this.results.delete(a.key);this.queue=o;for(const a of this.queue)this.results.set(a.key,{state:"queued"});this.emit(),this.next()}}pause(){this.want([])}reset(){this.epoch++;for(const t of this.results.values())t.state==="done"&&URL.revokeObjectURL(t.url);this.results.clear(),this.queue=[],this.early.clear(),this.running&&B.cancelPreviews().catch(()=>{}),this.running=null,this.emit()}pending(){return this.queue.length+(this.running?1:0)}next(){if(this.running)return;const t=this.queue.shift();if(!t)return;const s=this.epoch;this.running={key:t.key,generation:null,epoch:s},this.results.set(t.key,{state:"running"}),this.emit(),B.startPreview(t.settings).then(o=>{if(this.running?.epoch!==s||this.running.key!==t.key)return;this.running.generation=o;const a=this.early.get(o);a&&(this.early.delete(o),this.done(o,a))},o=>{this.running?.epoch===s&&this.finish(t.key,{state:"failed",message:String(o)})})}done(t,s){const o=this.running;if(o){if(o.generation===null){this.early.set(t,s);return}if(o.generation===t)if(s.state==="traced"){const a=URL.createObjectURL(new Blob([s.svg],{type:"image/svg+xml"}));this.finish(o.key,{state:"done",svg:s.svg,url:a,report:s.report})}else{const a="message"in s?s.message:s.state==="flat"?"one flat colour":s.state==="cancelled"?"stopped":"out of memory";this.finish(o.key,{state:"failed",message:a})}}}finish(t,s){this.results.set(t,s),this.running=null,this.emit(),this.next()}emit(){for(const t of[...this.listeners])t()}}const os=400;function $a(e,t,s){let o=e.liveStages,a=e.liveNow;const i=[...e.traceLog],r=c=>{for(let d=i.length-1;d>=0;d--){const p=i[d];if(p.kind==="stage")return p.what===c&&p.ms===void 0?d:-1}return-1};for(const c of t.entries)if(c.kind==="begin")a={stage:c.stage,what:c.what,since:s,step:null},i.push({at:c.at,kind:"stage",stage:c.stage,what:c.what});else if(c.kind==="end"){if(c.stage!=="measure"){const p=o.findIndex(h=>h.name===c.stage);o=p>=0?o.map((h,f)=>f===p?{...h,ms:h.ms+c.ms}:h):[...o,{name:c.stage,ms:c.ms}]}const d=r(c.what);d>=0?i[d]={...i[d],ms:c.ms,step:i[d].step??null}:i.push({at:Math.max(0,c.at-c.ms),kind:"stage",stage:c.stage,what:c.what,ms:c.ms}),a&&a.what===c.what&&(a=null)}else i.push({at:c.at,kind:"note",stage:c.stage,what:c.what,text:c.text});const l=t.step;if(l){!a||a.what!==l.what?a={stage:l.stage,what:l.what,since:s,step:l}:a={...a,step:l};const c=r(l.what);c>=0&&(i[c]={...i[c],step:l})}return{liveStages:o,liveNow:a,traceLog:i.length>os?i.slice(i.length-os):i}}function wn(e){return e.total>0?`${e.done} / ${e.total} ${e.unit}`:`${e.unit} ${e.done}`}function Ge(e){const t=Math.max(0,e)/1e3;return t<60?`${t.toFixed(1)} s`:`${Math.floor(t/60)} min ${String(Math.floor(t%60)).padStart(2,"0")} s`}function as(e){return`${(Math.max(0,e)/1e3).toFixed(2)}`}function Ma(e,t,s){let o=0;const a=()=>{const r=performance.now(),l=t(),c=s();for(const d of document.querySelectorAll('[data-live="elapsed"]'))d.textContent=Ge(r-l);for(const d of document.querySelectorAll('[data-live="stage"]'))d.textContent=c===null?"":Ge(r-c)},i=()=>{e()&&!o?(a(),o=window.setInterval(a,100)):!e()&&o&&(window.clearInterval(o),o=0)};return()=>{i(),o&&a()}}function _t(e){const t=e.liveNow;if(t){const s=t.stage==="measure"?"measuring":t.stage;return t.step?`${s} · ${wn(t.step)}`:`${s} · ${t.what}`}return e.liveStages[e.liveStages.length-1]?.name??"starting"}function Bt(e){return Ge(performance.now()-e.traceStarted)}const vn=()=>document.getElementById("overlays"),Ca=()=>document.getElementById("toasts");function W(e,t={}){const s=t.kind??"plain",o=s==="good"?"checkCircle":s==="bad"?"alert":"info",a=n("div.toast",{role:"status"},n(`span.glyph.${s}`,null,Z(o,16)),n("span.text",null,e),t.action&&n("button",{class:"reset",onclick:()=>{t.action?.run(),a.remove()}},t.action.label));Ca().append(a),window.setTimeout(()=>a.remove(),6e3)}function ue(){F(vn()),document.removeEventListener("keydown",xn,!0)}function xn(e){e.key==="Escape"&&(e.stopPropagation(),ue())}function Ye(e){const t=n("div.scrim",{onmousedown:s=>{s.target===t&&ue()}});e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),t.append(e),F(vn(),t),document.addEventListener("keydown",xn,!0),(e.querySelector("button, input, [tabindex]")??e).focus()}function Rt(e,t,s=[]){return n("div.modal",{tabindex:"-1"},n("h2",null,e),...t.filter(Boolean).map(o=>typeof o=="string"?n("p",null,o):o),s.length?n("div.actions",null,...s):null)}function kn(e,t){const s=n("div.popover",null,t),o=n("div",{style:{position:"fixed",inset:"0",zIndex:"44"},onmousedown:ue});F(vn(),o,s),document.addEventListener("keydown",xn,!0);const a=e.getBoundingClientRect(),i=s.getBoundingClientRect(),r=8,l=Math.min(Math.max(r,a.left),window.innerWidth-i.width-r),c=a.bottom+6,d=c+i.height+r>window.innerHeight?a.top-i.height-6:c;s.style.left=`${Math.round(l)}px`,s.style.top=`${Math.round(Math.max(r,d))}px`,(s.querySelector("button, input, [tabindex]")??s).focus?.()}let _e=null,Sn=0;function st(){window.cancelAnimationFrame(Sn),_e?.node.remove(),_e=null}function Gs(){const e=_e;if(!e)return;if(!(e.el.isConnected&&(e.el.matches(":hover")||e.el.matches(":focus-visible")))){st();return}Sn=window.requestAnimationFrame(Gs)}function Fe(e,t){let s=0;const o=()=>{if(!e.isConnected||_e?.el===e)return;st();const i=n("div.tooltip",{role:"tooltip"},t);document.body.append(i);const r=e.getBoundingClientRect(),l=i.getBoundingClientRect(),c=Math.min(Math.max(8,r.left),window.innerWidth-l.width-8),d=r.top-l.height-8;i.style.left=`${Math.round(c)}px`,i.style.top=`${Math.round(d<8?r.bottom+8:d)}px`,_e={node:i,el:e},Sn=window.requestAnimationFrame(Gs)},a=()=>{window.clearTimeout(s),_e?.el===e&&st()};return e.addEventListener("pointerenter",()=>{window.clearTimeout(s),s=window.setTimeout(o,350)}),e.addEventListener("pointerleave",a),e.addEventListener("pointerdown",a),e.addEventListener("focus",()=>{e.matches(":focus-visible")&&o()}),e.addEventListener("blur",a),e}document.addEventListener("keydown",e=>{e.key==="Escape"&&_e&&st()},!0);window.addEventListener("blur",st);function Ta(e,t,s,o,a=!1){Ye(Rt(e,[t],[n("button.btn",{onclick:ue},"Cancel"),n(`button.btn.primary${a?".danger":""}`,{onclick:()=>{ue(),o()}},s)]))}function Ws(e){const t=e.trim().replace(/^#/,""),s=t.length===3?[...t].map(o=>o+o).join(""):t;return/^[0-9a-fA-F]{6}$/.test(s)?[0,2,4].map(o=>parseInt(s.slice(o,o+2),16)/255):null}function qs([e,t,s]){const o=g=>g<=.04045?g/12.92:((g+.055)/1.055)**2.4,[a,i,r]=[o(e),o(t),o(s)],l=(.4124564*a+.3575761*i+.1804375*r)/.95047,c=.2126729*a+.7151522*i+.072175*r,d=(.0193339*a+.119192*i+.9503041*r)/1.08883,p=g=>g>216/24389?Math.cbrt(g):(24389/27*g+16)/116,[h,f,u]=[p(l),p(c),p(d)];return[116*f-16,500*(h-f),200*(f-u)]}function Aa(e,t){const[s,o,a]=e,[i,r,l]=t,c=Math.PI/180,d=Math.hypot(o,a),p=Math.hypot(r,l),h=((d+p)/2)**7,f=.5*(1-Math.sqrt(h/(h+25**7))),u=(1+f)*o,g=(1+f)*r,b=Math.hypot(u,a),x=Math.hypot(g,l),M=(pe,Te)=>{if(pe===0&&Te===0)return 0;const fe=Math.atan2(Te,pe)/c;return fe<0?fe+360:fe},v=M(u,a),S=M(g,l),y=i-s,$=x-b;let C=0;b*x!==0&&(C=S-v,C>180?C-=360:C<-180&&(C+=360));const D=2*Math.sqrt(b*x)*Math.sin(C*c/2),z=(s+i)/2,O=(b+x)/2;let A=v+S;b*x!==0&&(A=Math.abs(v-S)<=180?(v+S)/2:v+S<360?(v+S+360)/2:(v+S-360)/2);const P=1-.17*Math.cos((A-30)*c)+.24*Math.cos(2*A*c)+.32*Math.cos((3*A+6)*c)-.2*Math.cos((4*A-63)*c),J=30*Math.exp(-(((A-275)/25)**2)),H=O**7,X=2*Math.sqrt(H/(H+25**7)),se=1+.015*(z-50)**2/Math.sqrt(20+(z-50)**2),ee=1+.045*O,Q=1+.015*O*P,j=-Math.sin(2*J*c)*X,ne=y/se,ae=$/ee,te=D/Q;return Math.sqrt(ne*ne+ae*ae+te*te+j*ae*te)}function Tt(e){return e.kind==="gradient"&&e.stops.length>1?e.stops.map(t=>t.toLowerCase()).join(">"):e.traced.toLowerCase()}function qe(e){return e.split(">").map(t=>t.trim().toLowerCase())}function rt(e){return e.includes(">")}function $n(e,t=!1){const s=qe(e);return s.length<2?s[0]??"transparent":t?`radial-gradient(circle, ${s.join(", ")})`:`linear-gradient(90deg, ${s.join(", ")})`}const pt=new Map;function is(e){const t=pt.get(e);if(t)return t;const s=Ws(e);if(!s)return null;const o=qs(s);return pt.size>4096&&pt.clear(),pt.set(e,o),o}function Mn(e,t){const s=qe(e).map(is),o=qe(t).map(is);if(s.some(l=>!l)||o.some(l=>!l)||!s.length||!o.length)return 1/0;const a=s,i=o,r=(l,c)=>l.reduce((d,p)=>d+Math.min(...c.map(h=>Aa(p,h))),0)/l.length;return Math.max(r(a,i),r(i,a))}const Ea=2;function Gt(e,t){const s=rt(e),o=t.find(r=>Tt(r)===e);if(o)return o;let a=null,i=Ea;for(const r of t){if(r.kind==="gradient"!==s)continue;const l=Mn(e,Tt(r));l<=i&&(i=l,a=r)}return a}function Se(e,t){return e===t||Mn(e,t)<.5}const za=2,La=240;function Pa(e,t={}){const s=t.skip??[],o=e.filter(p=>p.share>0),a=[...o].sort((p,h)=>h.share-p.share).slice(0,La).map(p=>({member:Tt(p),share:p.share})).filter(p=>!s.some(h=>Se(h,p.member))),i=a.length;if(i<2)return[];const r=a.map((p,h)=>a.map((f,u)=>h===u?0:Mn(p.member,f.member))),l=a.map((p,h)=>[h]);let c=i;const d=t.count?Math.max(1,Math.round(t.count)-(o.length-i)):null;for(;!(d!==null&&c<=d);){let p=1/0,h=-1,f=-1;for(let u=0;uza)break;l[h]=[...l[h],...l[f]],l[f]=null,c--;for(let u=0;up.length>1).map(p=>p.map(h=>a[h]).sort((h,f)=>f.share-h.share)).sort((p,h)=>h.reduce((f,u)=>f+u.share,0)-p.reduce((f,u)=>f+u.share,0)).map(p=>({members:p.map(h=>h.member),target:null}))}function Cn(e){return[...e.members].sort().join(",")}function Da(e){return e.filter(t=>t.trimStart().startsWith("merge colors")).map(t=>{const s=t.trim().replace(/^merge colors\s+/,""),o=/;\s*(?:not found in the trace:|no ink near)\s*(.*)$/.exec(s),a=(o?.[1]??"").split(",").map(i=>i.trim().toLowerCase()).filter(i=>/^#[0-9a-f]{6}(>#[0-9a-f]{6})*$/.test(i));return{line:o?s.slice(0,o.index).trim():s,unmatched:a,leftAlone:s.startsWith("group left alone")}})}function Fa(e,t){return e?e.unmatched.some(s=>s===t||qe(t).includes(s)||Se(s,t)):!1}function Hs(e){const t=(e.groupSuggestions??[]).filter(a=>a.edited),s=[...e.colourGroups.flatMap(a=>a.members),...t.flatMap(a=>a.members)],o=Pa(e.palette,{count:e.simplifyTo,skip:s}).filter(a=>!e.dismissedGroups.includes(Cn(a)));return[...t,...o]}function Na(e,t){const s=e.members.filter(a=>Gt(a,t));if(s.length<2)return null;let o=e.target;if(o?.startsWith("@")){const a=e.members[Number(o.slice(1))-1],i=a?s.indexOf(a):-1;o=i>=0?`@${i+1}`:null}return{...e,members:s,target:o}}function Tn(e){return(e.groupSuggestions??[]).map((t,s)=>({g:Na(t,e.palette),index:s})).filter(t=>t.g!==null)}function Us(e,t){let s=e.target;if(s?.startsWith("@")){const o=Number(s.slice(1))-1;s=o===t?null:o>t?`@${o}`:s}return{...e,members:e.members.filter((o,a)=>a!==t),target:s}}function be(e,t,s=-1,o=!1){return e.map((a,i)=>{if(i===s)return a;const r=a.members.findIndex(l=>Se(l,t));return r<0?a:{...Us(a,r),...o?{edited:!0}:{}}})}const He=e=>e.filter(t=>t.members.length>1);function Ia(e,t,s,o,a){const i=e.state;let r=i.colourGroups.map(f=>({...f,members:[...f.members]})),l=(i.groupSuggestions??[]).map(f=>({...f,members:[...f.members]}));const c=a?.closest("[data-drop]")??null,d=c?.dataset.drop??null,p=Number(c?.dataset.index??-1);if((d==="group"||d==="sugg")&&o.kind===d&&o.index===p||d==="ink"&&Se(c?.dataset.member??"",s))return;if(d==="group")r=be(r,s,p),l=be(l,s,-1,!0),r[p].members.some(f=>Se(f,s))||r[p].members.push(s);else if(d==="sugg")r=be(r,s),l=be(l,s,p,!0),l[p].members.some(f=>Se(f,s))||(l[p]={...l[p],members:[...l[p].members,s],edited:!0});else if(d==="ink"){const f=c?.dataset.member??"";r=be(r,s),l=be(l,s,-1,!0);const u=r.findIndex(g=>g.members.some(b=>Se(b,f)));u>=0?r[u].members.push(s):(l=be(l,f,-1,!0),r.push({members:[f,s],target:null}))}else{if(o.kind==="palette")return;o.kind==="group"?r=be(r,s):l=be(l,s,-1,!0)}const h=He(r);e.set({groupSuggestions:He(l),paletteSelection:[]}),JSON.stringify(h)!==JSON.stringify(i.colourGroups)&&t.setColourGroups(h)}function Oa(e,t){const s=e.state,o=s.paletteSelection;if(o.length<2)return;let a=s.colourGroups.map(l=>({...l,members:[...l.members]}));const i=a.findIndex(l=>l.members.some(c=>o.some(d=>Se(d,c))));let r=s.groupSuggestions??[];for(const l of o)a=be(a,l,i),r=be(r,l,-1,!0);if(i>=0)for(const l of o)a[i].members.some(c=>Se(c,l))||a[i].members.push(l);else a.push({members:[...o],target:null});e.set({groupSuggestions:He(r),paletteSelection:[]}),t.setColourGroups(He(a))}const _a=4;function js(e,t,s,o,a,i){e.addEventListener("pointerdown",r=>{if(r.button!==0)return;const l=r.clientX,c=r.clientY;let d=null,p=null;const h=g=>{g!==p&&(p?.classList.remove("dropping"),p=g,p?.classList.add("dropping"))},f=g=>{if(!d){if(Math.hypot(g.clientX-l,g.clientY-c)<_a)return;d=n("div.dragghost",null,n("span.swatch",null,n("i",{style:{background:$n(o,i)}}))),document.body.append(d),document.body.classList.add("dragging-ink")}d.style.left=`${g.clientX+8}px`,d.style.top=`${g.clientY+8}px`;const b=document.elementFromPoint(g.clientX,g.clientY);h(b?.closest("[data-drop]")??null)},u=g=>{if(window.removeEventListener("pointermove",f),window.removeEventListener("pointerup",u),window.removeEventListener("pointercancel",u),!d)return;d.remove(),document.body.classList.remove("dragging-ink"),h(null);const b=x=>x.stopPropagation();window.addEventListener("click",b,!0),window.setTimeout(()=>window.removeEventListener("click",b,!0),0),g.type!=="pointercancel"&&Ia(t,s,o,a,document.elementFromPoint(g.clientX,g.clientY))};window.addEventListener("pointermove",f),window.addEventListener("pointerup",u),window.addEventListener("pointercancel",u)})}function ht(e,t){return Gt(e,t)?.keys??[]}function Vs(e,t){let s=0;const o=()=>{let i=[...e.querySelectorAll("[data-fill]:hover")].pop()??null;if(!i){const l=document.activeElement;l instanceof HTMLElement&&e.contains(l)&&l.matches(":focus-visible")&&(i=l.closest("[data-fill]"))}const r=i?JSON.parse(i.dataset.fill??"[]"):[];return r.length?r:null},a=()=>{const i=o(),r=t.state.hoverFill;(i??[]).join(` -`)!==(r??[]).join(` -`)&&t.set({hoverFill:i}),window.cancelAnimationFrame(s),i&&(s=window.requestAnimationFrame(a))};for(const i of["pointerover","pointerout","focusin","focusout"])e.addEventListener(i,()=>window.requestAnimationFrame(a))}function Ys(e,t){const s=e.state,o=Tn(s),a=s.palette.filter(r=>r.kind!=="gradient"),i=s.paletteSelection;return n("div.card.palettecard",null,n("div.cardhead",null,n("span.eyebrow",{style:{display:"flex",alignItems:"center",gap:"6px"}},Z("palette",12),`Palette · ${s.palette.length} ink${s.palette.length===1?"":"s"}`),n("button.reset",{disabled:!a.length,onclick:()=>Va(e,t)},"Paste brand palette")),s.colourGroups.length?Ba(e,t):null,s.palette.length?Wa(e,t,o):null,n("div.inklist",{"data-drop":"list","aria-label":"Inks"},...s.palette.map(r=>qa(e,t,r))),i.length?n("div.mergebar",null,n("span.muted",null,`${i.length} ticked`),n("button.btn.compact",{"data-ctl":"merge-ticked",disabled:i.length<2,title:"Draw the ticked inks as one: a new group, or the group one of them is already in",onclick:()=>Oa(e,t)},"Merge"),n("button.reset",{onclick:()=>e.set({paletteSelection:[]})},"Clear")):null,s.palette.length?n("div",{style:{display:"flex",alignItems:"baseline",justifyContent:"space-between",gap:"10px"}},n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},"Snapping rewrites fills only — no re-trace. Grouping re-traces, so the shapes join."),n("button.reset",{style:{fontSize:"11px",flex:"none"},title:"Custom properties, one per flat ink, in canvas-share order",onclick:()=>void ja(a)},"Copy all as CSS")):n("span.muted",{style:{fontSize:"11px"}},"Trace an image to see its inks."))}function rs(e,t=!1){return n(`span.swatch.mini${rt(e)?".ramp":""}`,null,n("i",{style:{background:$n(e,t)}}))}function jt(e){const t=qe(e);return t.length>1?`${t.length} stops`:t[0].toUpperCase()}function Ba(e,t){const s=e.state,a=JSON.stringify(s.resultGroups)===JSON.stringify(s.colourGroups)&&s.result?Da(s.result.engineLog):[];return n("div.cgroups",null,n("div.cgroupshead",null,n("span.eyebrow",null,`Colour groups · ${s.colourGroups.length}`),n("button.reset",{title:"Trace every ink on its own again",onclick:()=>t.setColourGroups([])},"Clear groups")),...s.colourGroups.map((i,r)=>Zs(e,t,i,r,"group",a[r]??null)))}function Ra(e,t){if(e.target?.startsWith("#"))return{member:e.target.toLowerCase(),label:"custom colour"};if(e.target?.startsWith("@")){const a=e.members[Number(e.target.slice(1))-1]??e.members[0];return{member:a,label:rt(a)?"extends this gradient":"becomes this"}}let s=e.members[0],o=-1;for(const a of e.members){const i=Gt(a,t)?.share??0;i>o&&(o=i,s=a)}return{member:s,label:"most-used"}}function Zs(e,t,s,o,a,i){const r=e.state,l=r.palette,c=x=>Gt(x,l)?.gradient==="radial",d=Ra(s,l),p=[...new Set([...s.members,d.member].flatMap(x=>ht(x,l)))],h=x=>{if(a==="group"){const M=r.colourGroups.map((v,S)=>S===o?x:v);t.setColourGroups(He(M))}else{const M=(r.groupSuggestions??[]).map((v,S)=>S===o?{...x,edited:!0}:v);e.set({groupSuggestions:He(M)})}},f=x=>Fa(i,x),u=x=>{const M=ht(x,l);return M.length||a==="sugg"?M:ht(d.member,l)},g=s.members.map((x,M)=>{const v=s.target===`@${M+1}`,S=n(`div.cchip${v?".target":""}${f(x)?".unmatched":""}`,{"data-fill":JSON.stringify(u(x))},n("button.cchipmain",{"data-ctl":`${a}${o}:${x}`,"aria-pressed":String(v),title:v?"The group becomes this. Press again for the most-used colour.":rt(x)?"Make the group this gradient: it is extended over the other members":"Make the group this colour",onclick:()=>h({...s,target:v?null:`@${M+1}`})},rs(x,c(x)),n("span.num",null,jt(x))),n("button.cchipx",{"data-ctl":`${a}${o}:${x}:x`,"aria-label":`Take ${jt(x)} out of the group`,title:"Take it out of the group",onclick:()=>h(Us(s,M))},Z("x",11)));return js(S,e,t,x,{kind:a,index:o},c(x)),S}),b=n("button.reset",{title:"Paint the whole group one colour of your choosing",onclick:x=>Ha(x.currentTarget,d.member,M=>h({...s,target:M}))},"Custom…");return n(`div.cgroup${a==="sugg"?".suggested":""}`,{"data-drop":a,"data-index":String(o),"data-fill":JSON.stringify(p)},n("div.cchips",null,...g),n("div.cresult",null,n("span.arrow",{"aria-hidden":"true"},"→"),n("span.cresultchip",{"data-fill":JSON.stringify(ht(d.member,l))},rs(d.member,c(d.member)),n("span.num",null,jt(d.member))),n("span.muted",null,d.label),n("span",{style:{flex:"1"}}),b),a==="sugg"?n("div.cactions",null,n("button.btn.compact.primary",{onclick:()=>{const x=(r.groupSuggestions??[]).filter((M,v)=>v!==o);e.set({groupSuggestions:x}),t.setColourGroups([...r.colourGroups,{members:s.members,target:s.target}])}},"Accept"),n("button.btn.compact",{onclick:()=>e.set({groupSuggestions:(r.groupSuggestions??[]).filter((x,M)=>M!==o),dismissedGroups:[...r.dismissedGroups,Cn(s)]})},"Dismiss")):[Ga(i),n("div.cactions",null,n("span",{style:{flex:"1"}}),s.target?n("button.reset",{title:"Become the member covering the most of the image",onclick:()=>h({...s,target:null})},"Most-used"):null,n("button.reset",{title:"Trace these inks on their own again",onclick:()=>t.setColourGroups(r.colourGroups.filter((x,M)=>M!==o))},"Ungroup"))])}function Ga(e){if(!e)return n("span.muted.creport",null,"Merged on the next trace.");if(e.leftAlone)return n("span.creport.warn",{title:e.line},Z("alert",12),"Left alone: fewer than two of these colours are in the image now.");if(e.unmatched.length){const s=e.unmatched.map(o=>rt(o)?"a gradient":o.toUpperCase()).join(", ");return n("span.creport.warn",{title:`${e.line}; not found: ${e.unmatched.join(", ")}`},Z("alert",12),`${s} ${e.unmatched.length===1?"is":"are"} no longer in the image; the rest merged.`)}const t=e.line.replace(/ -> /," → ").replace(/#[0-9a-f]{6}/gi,s=>s.toUpperCase());return n("span.muted.creport",{title:e.line},t)}function Wa(e,t,s){const o=e.state,a=o.palette.length,i=l=>{const c=l===null?null:Math.max(1,Math.min(a,Math.round(l))),d=(o.groupSuggestions??[]).filter(p=>p.edited);e.set({simplifyTo:c,groupSuggestions:Hs({...o,simplifyTo:c,groupSuggestions:d})})},r=n("input.numberfield.simplify",{type:"text",inputmode:"numeric",value:String(o.simplifyTo??a),"aria-label":"Simplify the palette to this many colours",onchange:l=>{const c=Number(l.target.value);Number.isFinite(c)&&c>=1&&i(c>=a?null:c)}});return n("div.cgroups.suggestions",null,n("div.cgroupshead",null,n("span.eyebrow",null,s.length?`Suggested · ${s.length}`:"Suggested"),s.length>1?n("button.reset",{title:"Accept every suggestion below",onclick:()=>{e.set({groupSuggestions:[]}),t.setColourGroups([...o.colourGroups,...s.map(({g:l})=>({members:l.members,target:l.target}))])}},"Accept all"):null),n("div.simplifyrow",null,n("span.muted",null,"Colours"),n("button.btn.compact.icon",{"aria-label":"One colour fewer",disabled:(o.simplifyTo??a)<=1,onclick:()=>i((o.simplifyTo??a)-1)},Z("minus",12)),r,n("button.btn.compact.icon",{"aria-label":"One colour more",disabled:o.simplifyTo===null,onclick:()=>i((o.simplifyTo??a)+1>=a?null:(o.simplifyTo??a)+1)},"+"),n("span.muted",{style:{fontSize:"11px"}},o.simplifyTo===null?`of ${a} · near duplicates`:`of ${a} · merging down`)),...s.length?s.map(({g:l,index:c})=>Zs(e,t,l,c,"sugg",null)):[n("span.muted",{style:{fontSize:"11px"}},o.simplifyTo===null?"No near-duplicate inks: nothing to propose.":"Nothing more to propose at this count.")])}function qa(e,t,s){const o=e.state,a=Tt(s),i=s.kind==="gradient",r=s.snappedDe00!==null,l=o.paletteSelection.includes(a),c=o.colourGroups.some(f=>f.members.some(u=>Se(u,a))),d=n("button.inkmain",{"data-ctl":`ink:${a}`,title:i?`${s.gradient??"linear"} gradient · ${s.stops.map(f=>f.toUpperCase()).join(" → ")}`:"Snap this ink to a colour of your choosing","aria-disabled":i?"true":null,onclick:f=>{i||Ua(f.currentTarget,s,t)}},n(`span.swatch${r?".snapped":""}${i?".ramp":""}`,null,n("i",{style:{background:i?$n(a,s.gradient==="radial"):s.hex}})),n("span.hex",null,i?s.gradient==="radial"?"radial":"linear":s.hex.toUpperCase()),n("span.share",null,$e(s.share)),n(`span.snap${r?".on":""}`,null,i?`${s.stops.length} stops`:r?`snapped · ΔE ${re(s.snappedDe00)}`:"snap to…")),p=()=>e.set({paletteSelection:l?o.paletteSelection.filter(f=>f!==a):[...o.paletteSelection,a]}),h=n(`div.ink${c?".grouped":""}`,{"data-drop":"ink","data-member":a,"data-fill":JSON.stringify(s.keys)},n("button.inkpick",{"data-ctl":`pick:${a}`,role:"checkbox","aria-checked":String(l),"aria-label":`Tick ${i?"this gradient":s.hex.toUpperCase()} to merge it`,title:"Tick inks, then Merge to draw them as one. Or drag one onto another.",onclick:p,onkeydown:f=>{f.key===" "&&(f.preventDefault(),f.stopPropagation(),f.repeat||p())}},l?Z("check",11):null),d);return js(h,e,t,a,{kind:"palette"},s.gradient==="radial"),h}function Ha(e,t,s){const o=qe(t)[0]??"#000000",a=n("input.numberfield",{type:"text",value:o.toUpperCase(),spellcheck:"false",style:{width:"100%",textAlign:"left",height:"30px",padding:"0 9px"}}),i=n("input",{type:"color",value:o,style:{width:"100%",height:"60px",border:"none",background:"none",padding:"0"},oninput:()=>{a.value=i.value.toUpperCase()}}),r=()=>{const l=a.value.trim().toLowerCase(),c=l.startsWith("#")?l:`#${l}`;if(!/^#[0-9a-f]{6}$/.test(c)){W(`${a.value} is not a colour; write it as #RRGGBB.`,{kind:"bad"});return}s(c),ue()};kn(e,n("div",{style:{width:"260px",padding:"12px",display:"flex",flexDirection:"column",gap:"11px"}},i,n("span.muted",{style:{fontSize:"11px"}},"The group becomes"),n("div",{style:{display:"flex",gap:"6px"}},a,n("button.btn.compact",{style:{background:"var(--accent)",borderColor:"var(--accent)",color:"var(--accent-ink)",fontWeight:"600"},onclick:r},"Use")))),a.addEventListener("keydown",l=>{l.key==="Enter"&&r()})}function Ua(e,t,s){const o=n("input.numberfield",{type:"text",value:t.hex.toUpperCase(),spellcheck:"false",style:{width:"100%",textAlign:"left",height:"30px",padding:"0 9px"}}),a=n("input",{type:"color",value:t.hex,style:{width:"100%",height:"60px",border:"none",background:"none",padding:"0"},oninput:()=>{o.value=a.value.toUpperCase()}}),i=n("span",{style:{fontSize:"11.5px",color:"var(--gold)"}},"You are overriding a measurement.");kn(e,n("div",{style:{width:"300px",padding:"12px",display:"flex",flexDirection:"column",gap:"11px"}},a,n("div",{style:{display:"flex",gap:"8px",alignItems:"center"}},n("span.swatch",null,n("i",{style:{background:t.traced}})),n("div",{style:{display:"flex",flexDirection:"column"}},n("span.muted",{style:{fontSize:"11px"}},"traced"),n("span.num",{style:{fontSize:"12.5px"}},`${t.traced.toUpperCase()} · ${$e(t.share)} of canvas`))),n("span.muted",{style:{fontSize:"11px"}},"Snap to"),n("div",{style:{display:"flex",gap:"6px"}},o,n("button.btn.compact",{style:{background:"var(--accent)",borderColor:"var(--accent)",color:"var(--accent-ink)",fontWeight:"600"},onclick:()=>{s.snap([{from:t.traced,to:o.value.trim().toLowerCase()}]),ue()}},"Snap")),i))}async function ja(e){const t=e.map((s,o)=>` --ink-${o+1}: ${s.hex.toLowerCase()}; /* ${$e(s.share)} of canvas */`).join(` -`);try{await Nt(`:root { -${t} -} -`),W(`${e.length} ink${e.length===1?"":"s"} copied as CSS.`,{kind:"good"})}catch(s){W(String(s),{kind:"bad"})}}function Va(e,t){const s=e.state.palette.filter(l=>l.kind!=="gradient"),o=n("textarea",{rows:"5",spellcheck:"false",placeholder:`#12443E -#E9B24C -rgb(207, 198, 180)`,style:{width:"100%",border:"1px solid var(--rule2)",borderRadius:"var(--radius)",background:"var(--paper)",color:"var(--ink)",padding:"10px 12px",font:"12px/1.8 var(--font-mono)",resize:"vertical"}}),a=n("div",{style:{display:"flex",flexDirection:"column",gap:"7px"}});let i=[];const r=async()=>{i=await B.matchPalette(s.map(l=>l.traced),o.value),F(a,...i.map(l=>n("div",{style:{display:"flex",alignItems:"center",gap:"9px",fontSize:"12px"},class:"num"},n("span.swatch",{style:{width:"20px",height:"20px"}},n("i",{style:{background:l.from}})),n("span.muted",null,l.from.toUpperCase()),n("span.muted",null,"→"),n("span.swatch",{style:{width:"20px",height:"20px"}},n("i",{style:{background:l.to}})),n("span.dim",null,l.to.toUpperCase()),n("span",{style:{marginLeft:"auto",color:l.de00<.5?"var(--good)":"var(--gold)"}},l.de00<.05?"exact":`${re(l.de00)} dE00`))))};o.addEventListener("input",()=>void r()),Ye(Rt("Paste a brand palette",[o,n("span.muted",{style:{fontSize:"11.5px"}},"Hex, RGB or CSS variables, one per line or separated by commas. We match each to the nearest traced ink."),a],[n("button.btn",{onclick:ue},"Cancel"),n("button.btn.primary",{onclick:()=>{t.snap(i.map(l=>({from:l.from,to:l.to}))),ue()}},`Snap ${s.length} inks`)]))}const Js=2.5,Ya=256;function Xs(e,t){return!!e?.lossy||t!==null&&t.noiseLevels>=Js}function Za(e){return e.filter(t=>t.share>=.005)}function Ja(e){if(e.length!==2||e.some(l=>l.kind==="gradient"))return!1;const t=e.map(l=>Ws(l.hex)).map(l=>l?qs(l):null);if(t.some(l=>l===null))return!1;const[s,o]=t,a=l=>Math.hypot(l[1],l[2])<12,[i,r]=s[0]65}function Xa(e){const{source:t,report:s,palette:o,losses:a,facts:i,settings:r}=e;if(!t||!s)return[];const l=[];if(Xs(t,i)&&r.cleanUpDamage==="off"){const f=[t.lossy?t.container:null,i?`noise ${i.noiseLevels.toFixed(1)} levels`:null].filter(Boolean);l.push({id:"photo",preset:"photo-or-scan",title:"Looks like a photo, a scan or a screenshot",evidence:f.join(" · "),action:"Photo or scan, with the denoiser"})}const c=Za(o);!r.blackAndWhite&&!r.monochrome&&Ja(c)&&l.push({id:"bw",preset:"black-and-white",title:"Two colours, black and white",evidence:`${c.length} inks cover the canvas`,action:"Two-tone scan"}),!r.lineArt&&a.some(f=>f.kind==="strokes")&&!r.blackAndWhite&&l.push({id:"lineart",preset:"line-art",title:"Drawn with strokes",evidence:"line work came back as filled outlines",action:"Line art, as strokes"});const d=Math.max(t.width,t.height),p=o.some(f=>f.kind==="gradient");return!(r.speckleFloor<=1&&r.maxColours<=16)&&d<=Ya&&!p&&c.length<=16&&l.length===0&&l.push({id:"icon",preset:"icon",title:"A small flat mark",evidence:`${d} px, ${c.length} colour${c.length===1?"":"s"}`,action:"Icon, keeping one-pixel details"}),l.slice(0,2)}function De(e,t,s){return(e?.controls??[]).filter(o=>t[o.key]!==s[o.key]).map(o=>o.key)}function Ks(e,t,s){const o=e?.presets.find(a=>a.id===s);if(o){const a=De(e,t,o.settings).length,i=o.id==="logo"?"the Logo preset, the default":`the ${o.name} preset`;return a?`${i}, with ${a} control${a===1?"":"s"} changed last time`:i}return"your settings from last time"}function An(e){const t=e.state,s=t.auto;return s?.report?Xa({source:t.source,report:s.report,palette:s.palette,losses:s.losses,facts:t.facts,settings:s.settings,preset:s.preset}):[]}function Qs(e){const t=e.state;return t.auto?t.preset!==t.auto.preset||De(t.caps,t.settings,t.auto.settings).length>0:!1}function Ka(e){const t=e.state;return t.auto?.report?Qs(e)||Tn(t).length>0||An(e).length>0:!1}function eo(e,t,s=!1){const o=e.state,a=o.auto;if(!a?.report)return null;const i=An(e),r=Tn(o),l=Qs(e),c=o.caps?.presets.find(p=>p.id===o.preset),d=i.map(p=>{const h=o.preset===p.preset;return n("div.autorow",null,n("div.autotext",null,n("span.autotitle",null,p.title),n("span.muted.num",null,p.evidence)),h?n("span.autotaken",null,"chosen"):n("button.btn.compact",{"data-ctl":`auto-try:${p.id}`,title:`Switch to ${p.action}. An ordinary preset: Back to Auto undoes it.`,onclick:()=>t.setPreset(p.preset)},`Try ${p.action}`))});if(r.length){const p=r.reduce((h,{g:f})=>h+f.members.length,0);d.push(n("div.autorow",null,n("div.autotext",null,n("span.autotitle",null,`${r.length} colour group${r.length===1?"":"s"} suggested`),n("span.muted.num",null,`${p} inks that look like the same colour measured twice`)),n("div.autobtns",null,n("button.btn.compact",{"data-ctl":"auto-groups-accept",title:"Draw each suggested group as one colour; the trace runs again so the shapes join",onclick:()=>{e.set({groupSuggestions:[]}),t.setColourGroups([...o.colourGroups,...r.map(({g:h})=>({members:h.members,target:h.target}))])}},"Accept"),n("button.reset",{"data-ctl":"auto-groups-dismiss",onclick:()=>e.set({groupSuggestions:[],dismissedGroups:[...o.dismissedGroups,...r.map(({g:h})=>Cn(h))]})},"Dismiss"))))}return n("div.autochose",null,s?null:n("span.autolead",null,`Auto traced with ${Ks(o.caps,a.settings,a.preset)}.`),...d,!d.length&&!s?n("span.muted",null,"Nothing in its report suggests another preset."):null,l?n("div.autorow.moved",null,n("div.autotext",null,n("span.autotitle",null,c?`Now: ${c.name}`:"Now: your own settings"),n("span.muted",null,"An ordinary setting, shown under Tune")),n("button.reset",{"data-ctl":"auto-back",onclick:t.backToAuto},"Back to Auto")):null)}const Vt=e=>`${Math.round(e/(1024*1024))}`;function En(e){return e.phase==="stored"||e.phase==="preparing"||e.phase==="ready"?100:e.phase!=="downloading"||!e.total?null:Math.min(100,Math.floor(e.got/e.total*100))}function At(e,t){if(!e)return t?"Starting the denoiser…":null;switch(e.phase){case"downloading":{const s=En(e);return e.total?`Downloading the denoiser, ${Vt(e.got)} of ${Vt(e.total)} MB, ${s}%`:`Downloading the denoiser, ${Vt(e.got)} MB`}case"stored":case"preparing":return t?"Preparing the denoiser…":null;case"failed":return"The denoiser did not download";case"idle":return t?"Starting the denoiser download…":null;case"ready":return null}}function zn(e){return/[….]$/.test(e)?e:`${e}.`}function Et(e){if(!e||e.phase!=="downloading"&&e.phase!=="preparing"&&e.phase!=="stored")return null;const t=En(e)??0;return n("div.progress.fetchbar",{role:"progressbar","aria-label":"Denoiser download","aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":String(t)},n("i",{style:{width:`${t}%`}}))}const Ln=new Set(["mode","cleanUpDamage","editability"]),Yt={axisHandles:.34,smoothJoins:.89,alignedNodes:.86};function Qa(e,t){const s=n("div.railtabs"),o=n("div.railmodes"),a=n("div.railscroll"),i=n("div.railfoot"),r=n("aside.rail",{"aria-label":"Controls"},s,o,a,i),l={result:0,tune:0};let c=e.state.railTab;const d=()=>{const u=e.state,g=document.activeElement instanceof HTMLElement&&a.contains(document.activeElement)?document.activeElement.getAttribute("data-ctl"):null;l[c]=a.scrollTop,F(a,...u.railTab==="result"?[u.tracing?di(e):null,...ri(e,t)]:si(e,t)),c=u.railTab,a.scrollTop=l[c]??0,g&&a.querySelector(`[data-ctl="${g}"]`)?.focus({preventScroll:!0})},p=()=>{const u=e.state,g=i.querySelector(".readout.busy");if(u.tracing&&g){g.querySelector(".stage").textContent=_t(u);return}F(i,...yi(e,t))},h=()=>{const u=document.activeElement instanceof HTMLElement&&o.contains(document.activeElement)?document.activeElement.getAttribute("data-ctl"):null;F(o,...ei(e,t)),u&&o.querySelector(`[data-ctl="${u}"]`)?.focus({preventScroll:!0})},f=()=>{F(s,...ti(e,t)),h(),d(),p()};return e.on(["caps","prefs","preset","settings","railTab","groupsOpen"],f),e.on(["denoiserFetch"],h),e.on(["tracing","liveStages","liveNow","result","report","palette","losses","source","worstCorner","previous","colourGroups","groupSuggestions","resultGroups","paletteSelection","simplifyTo","auto","facts","chooser","wizard","autoNoteHidden"],()=>{e.state.railTab==="result"&&d(),p();const u=s.querySelector('[data-note="result"]');u&&(u.textContent=no(e))}),Vs(a,e),f(),r}function ei(e,t){const s=e.state,o=x=>s.caps?.controls.find(M=>M.key===x)?.help??"",a=s.settings.mode??"quality",i=n("div.mode.mode-engine",null,n("div.modehead",null,Fe(n("span.modetitle",{tabindex:"0"},"Engine"),o("mode")||"Quality places every edge to a fraction of a pixel and fits the fewest curves that match the image: the closest trace, and the default. Fast traces each shape in a single pass, many times quicker, for previews, batches and very large images."),n("span.modestate",null,a==="fast"?"one pass":"closest fit")),n("div.seg.big",{role:"group","aria-label":"Tracing Engine"},n("button",{"aria-pressed":String(a==="quality"),"data-ctl":"mode:quality",title:"Quality: the closest trace, edges placed to a fraction of a pixel",onclick:()=>t.changeSetting("mode","quality")},"Quality"),n("button",{"aria-pressed":String(a==="fast"),"data-ctl":"mode:fast",title:"Fast: each shape traced in one pass, many times quicker, a little less exact",onclick:()=>t.changeSetting("mode","fast")},"Fast"))),r=s.caps?.denoiser,l=s.settings.cleanUpDamage,c=r?.supported??!1,d=c&&l!=="off"&&s.denoiserFetch?.phase!=="ready"?s.denoiserFetch:void 0,p=d!==void 0,h=!p&&c&&r!==void 0&&!r.installed&&l!=="off",f={off:"pixels as they are",auto:"only if damaged",on:"always"},u=n("div.mode",null,n("div.modehead",null,Fe(n("span.modetitle",{tabindex:"0"},"Denoiser"),o("cleanUpDamage")),p&&d?.phase==="failed"?n("button.reset",{"data-ctl":"denoiser-retry",onclick:t.retryDenoiser},"Retry"):h?n("button.reset",{onclick:t.openDenoiser,title:"It runs on this computer; nothing is uploaded"},"Download it"):n("span.modestate",null,c?f[l]:"needs its own tab")),n("div.seg.big",{role:"group","aria-label":"Denoiser"},...["off","auto","on"].map(x=>n("button",{"aria-pressed":String(l===x),disabled:!c,"data-ctl":`cleanUpDamage:${x}`,onclick:()=>t.changeSetting("cleanUpDamage",x)},x==="off"?"Off":x==="auto"?"Auto":"On"))),p?n("div.fetchnote",{"data-ctl":"denoiser-progress",role:"status"},Et(d??null),n("span.fetchline",null,d?.phase==="failed"?`${At(d,!0)}: ${d.message??"the connection dropped"}.`:zn(At(d??null,!0)??"")),n("span.muted",null,d?.phase==="failed"?"The trace shown is without it.":"Shown without it until then; it traces again by itself.")):null),g=s.settings.editability,b=n("div.mode",null,n("div.modehead",null,Fe(n("span.modetitle",{tabindex:"0"},"Editable"),o("editability"))),n("button.bigswitch",{role:"switch","aria-checked":String(g),"aria-label":"Editable structure","data-ctl":"editability:switch",onclick:()=>t.changeSetting("editability",!g)},n("span.knob"),n("span.state",null,g?"On":"Off")));return[i,n("div.railmodes-row",null,u,b)]}function to(e,t){const s=t.baseSettings(),o=e.state;return(o.caps?.controls??[]).filter(a=>it(a,o.settings)&&o.settings[a.key]!==s[a.key]).length}function no(e){const t=e.state.report;return t?`${re(t.meanDe00)} dE00`:""}function ti(e,t){const s=e.state,o=to(e,t),a=(i,r,l,c)=>n("button",{role:"tab","aria-selected":String(s.railTab===i),title:c,onclick:()=>e.set({railTab:i})},n("span.tablabel",null,r),n("span.tabnote",{"data-note":i},l));return[n("div.railseg",{role:"tablist","aria-label":"What the rail shows"},a("result","Result",no(e),"What this trace produced"),a("tune","Tune",o?`${o} changed`:"","The settings that make the next trace")),n("p.tabcaption",null,s.railTab==="result"?"What this trace produced.":"The settings for the next trace.")]}function ni(e,t){const s=e.state;if(s.settings.mode!=="fast")return null;const o=(s.caps?.controls??[]).filter(a=>!Ln.has(a.key)&&!it(a,s.settings)).length;return o?n("div.guideline",{"data-note":"quality-only"},n("span.faint",null,`${o} more control${o===1?" is":"s are"} for Quality: switch to see ${o===1?"it":"them"}.`),n("button.reset",{"data-ctl":"show-quality",title:"Switch the engine to Quality",onclick:()=>t.changeSetting("mode","quality")},"Quality")):null}function si(e,t){return[oi(e,t),ni(e,t),ai(e,t),...bi(e,t)]}function oi(e,t){return e.state.source?n("div.guideline",null,n("span.faint",null,"Not sure which controls matter here?"),n("button.reset",{"data-ctl":"open-wizard",title:"The main choices one step at a time, each explained, with the result beside Auto's",onclick:t.openWizard},"Walk me through it")):null}function ai(e,t){const s=e.state,o=s.caps?.presets??[],a=s.prefs?.saved??[],i=Os(s.caps?.platform),r=(p,h,f,u,g)=>n(g?"button.preset.saved":"button.preset",{"aria-pressed":String(s.preset===p),title:`${h} — ${f}${u?` (${i}+${u})`:""}`,onclick:()=>t.setPreset(p)},n("span.name",null,h),g?n("span.forget",{role:"button",tabindex:"0","aria-label":`Forget ${h}`,title:`Forget ${h}`,onclick:b=>{b.stopPropagation(),g()}}):null),l=o.find(p=>p.id===s.preset),c=l?`${l.subtitle}.`:a.find(p=>p.id===s.preset)?"One of your saved presets.":"Controls moved from the preset they started at.",d=to(e,t);return n("div.presetblock",null,n("div.cardhead",null,n("span.eyebrow",null,"Preset"),n("button.reset",{disabled:!s.caps||!d,title:"Put every control back to where the preset had it",onclick:()=>s.preset&&t.setPreset(s.preset)},d?`Reset ${d} changed`:"Nothing changed")),n("div.presets",{role:"group","aria-label":"Presets"},...o.map((p,h)=>r(p.id,p.name,p.subtitle,h<9?h+1:null,null)),...a.map(p=>r(p.id,p.name,"Saved preset",null,()=>t.deletePreset(p.id)))),n("div.traybar",null,n("span.faint",null,c),n("button.reset",{disabled:!s.caps,title:`Remember all ${s.caps?.controls.length??""} controls exactly as they stand`,onclick:()=>ii(e,t)},"Save as preset")))}function ii(e,t){const s=e.state.caps?.presets.find(i=>i.id===e.state.preset),o=n("input.numberfield",{type:"text",maxlength:"40",spellcheck:"false",placeholder:s?`${s.name}, adjusted`:"Northwind, flat",style:{width:"100%",textAlign:"left",height:"32px",padding:"0 10px"}}),a=()=>{t.savePreset((o.value.trim()||o.placeholder).slice(0,40)),ue()};Ye(Rt("Save current as preset",[o,n("span.muted",{style:{fontSize:"11.5px",lineHeight:"1.5"}},`All ${e.state.caps?.controls.length??""} controls, as they stand. A saved preset is a snapshot rather than a set of differences from the defaults, so it will not drift when those move.`)],[n("button.btn",{onclick:ue},"Cancel"),n("button.btn.primary",{onclick:a},"Save")])),o.focus(),o.addEventListener("keydown",i=>{i.key==="Enter"&&a()})}function ri(e,t){return e.state.source?[li(e,t),pi(e,t),fi(e,t),gi(e),Ys(e,t),ui()]:[ci()]}function li(e,t){const s=e.state;if(s.chooser||s.wizard||s.autoNoteHidden||!Ka(e))return null;const o=eo(e,t,!0);return o?n("div.card.autocard",null,n("div.cardhead",null,n("span.eyebrow",null,"Auto chose"),n("div",{style:{display:"flex",gap:"10px"}},n("button.reset",{"data-ctl":"auto-customise",onclick:t.openWizard},"Customise"),n("button.reset",{"data-ctl":"auto-hide",title:"Hide this for the image",onclick:t.hideAutoNote},"Hide"))),o):null}function ci(){return n("div.card.emptycard",null,n("span.eyebrow",null,"Quality report"),n("p",null,"Open an image and its report lands here: the measured colour difference, what the drawing cost in coordinates, how editable it is, what could not be recovered, and its palette."),n("span.faint",null,"The controls are under Tune; they apply to whatever you open next."))}function ui(){return n("div.benchmark",null,n("span.eyebrow.label",null,"Benchmark"),n("p",null,"Across our published 21-case set, VTracer's defaults average 4.4× the coordinates at 10× the colour error. ",n("span.dim",null,"Not a measurement of this file.")))}function di(e){const t=e.state,s=t.caps?.stages??[],o=new Map(t.liveStages.map(i=>[i.name,i.ms])),a=t.liveNow;return n("div.card",null,n("div.cardhead",null,n("span",{style:{fontSize:"12px",fontWeight:"500"}},`Tracing at ${bn(t,t.tracingTier)??"—"} px`),n("span.muted.num",{style:{fontSize:"11px"},"data-live":"elapsed"},Bt(t))),n("div.sweep",null,n("i")),n("div.stagelist",null,...s.map(i=>{const r=o.get(i);return a?.stage===i?n("div.stagerow.running",null,n("span.mark",null,"›"),n("span.name",null,i,n("span.sub",null,a.step?wn(a.step):a.what)),n("span.num",{"data-live":"stage"})):n(`div.stagerow${r===void 0?"":".done"}`,null,n("span.mark",null,r===void 0?"·":"✓"),n("span.name",null,i),n("span.muted",null,r===void 0?"—":`${r.toFixed(0)} ms`))})))}function pi(e,t){const s=e.state,o=s.report,a=o?s.result?.tier==="draft"?`draft · ${o.tracedPx} px`:`full trace · ${o.tracedPx} px`:"—",i=r=>`${(Math.sqrt(Math.min(r,5)/5)*100).toFixed(1)}%`;return n("div.card",{style:{opacity:s.tracing?"0.45":"1",transition:"opacity var(--d-fast)"}},n("div.cardhead",null,n("span.eyebrow",null,"Quality report"),n("span.muted",{style:{fontSize:"11px"}},a)),n("div.headline",null,n("span.figure",null,re(o?.meanDe00)),n("div.of",null,n("span.dim",{style:{fontSize:"11.5px"}},"mean colour difference"),n("span.muted.num",{style:{fontSize:"11px"}},`median ${re(o?.medianDe00)} · dE00`))),n("div",{style:{display:"flex",flexDirection:"column",gap:"5px"}},n("div.meter",{role:"img","aria-label":`Colour difference ${re(o?.meanDe00)} of a 0 to 5 scale`},n("span.threshold",{style:{left:i(1)}}),o?.meanDe00!=null?n("span.needle",{style:{left:i(o.meanDe00)}}):null),n("div.meterscale",null,n("span",null,"0 invisible"),n("span",null,"1.0 just visible"),n("span",null,"5.0"))),n("div.stats",null,...hi(e).map(([r,l])=>n("div",null,n("span.k",null,r),n("span.v",null,l)))),o&&s.worstCorner?n("button.reset",{style:{textAlign:"left"},onclick:t.jumpToWorst},`Find the worst corner · ${re(s.worstCorner.de00)} dE00 →`):null)}function hi(e){const t=e.state.report;return t?[["coordinates",oe(t.coordinates)],["paths",oe(t.paths)],["colours found",oe(t.colours)],["segments",oe(t.segments)],["file size",ie(t.bytes)],["minified",t.minifiedBytes===null?"already":ie(t.minifiedBytes)],["traced at",`${t.tracedPx} px`],["time taken",nt(t.seconds)]]:[["coordinates","—"],["paths","—"],["colours found","—"],["segments","—"],["file size","—"],["minified","—"]]}function fi(e,t){const s=e.state,o=s.report?.structure;if(s.report&&!o)return null;const a=s.settings.editability,r=(o?[{label:"Handles on an axis",help:"Curve handles that point exactly along x or y, as an artist places them with the keyboard. Counted over every cubic handle in the drawing.",part:o.axisHandles,whole:o.handles,artist:Yt.axisHandles},{label:"Smooth joins",help:"Places where two curves meet with their tangents within a degree of each other, so the outline has no kink for a hand to trip on.",part:o.smoothJoins,whole:o.joins,artist:Yt.smoothJoins},{label:"Nodes sharing a coordinate",help:"Nodes with the same x or the same y as another node, so a group of them can be selected and aligned in one move.",part:o.alignedNodes,whole:o.nodes,artist:Yt.alignedNodes}]:[]).filter(c=>c.whole>0),l=o!==void 0&&o.cubics===0;return n("div.card.structure",{style:{opacity:s.tracing?"0.45":"1",transition:"opacity var(--d-fast)"}},n("div.cardhead",null,n("span.eyebrow",null,"Editability"),n("button.reset",{title:a?"Stop moving nodes and handles onto what an artist would draw":"Move nodes and handles onto what an artist would draw. Costs about 0.04 dE00 on icons, and the report above shows the price on this image.",onclick:()=>t.changeSetting("editability",!a)},a?"Editable structure on · turn off":"Make it editable")),...r.map(c=>{const d=c.part/c.whole;return n("div.srow",{title:c.help},n("span.k",null,c.label),n("span.v",null,$e(d),n("span.of",null,`${c.part} of ${c.whole}`)),n("div.sbar",{role:"img","aria-label":`${c.label}: ${$e(d)}, ${c.part} of ${c.whole}; hand-drawn files ${$e(c.artist)}`},n("i",{style:{width:`${(d*100).toFixed(1)}%`}}),n("b",{style:{left:`${(c.artist*100).toFixed(1)}%`}})))}),l?n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},"This drawing is lines and arcs, so there are no curve handles to tidy; only its nodes can line up."):null,n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},"The tick is where hand-drawn files sit (median of 1,544 artist SVGs). ",a?"Nothing moves further than the trace's own tolerance.":"A trace fitted for pixels alone starts near zero on all three."))}function gi(e){const t=e.state,s=o=>({lettering:"type",lossy:"image",strokes:"wand",ramp:"droplet",detail:"layers"})[o]??"info";return n("div.card",null,n("span.eyebrow",null,"What this trace could not recover"),...t.losses.map(o=>mi(o,s(o.kind))),n("div.nothinglost",null,n("span.glyph",null,Z("checkCircle",15)),n("span",null,t.losses.length?"Nothing else we can detect was lost.":"Nothing we can detect was lost.")))}function mi(e,t){const s=n("div.why",{hidden:!0},e.why),o=n("button.expand",{"aria-expanded":"false",onclick:()=>{const a=s.hasAttribute("hidden");s.toggleAttribute("hidden",!a),o.setAttribute("aria-expanded",String(a)),o.textContent=a?"Hide":"Why"}},"Why");return n("div.loss",null,n("span.glyph",null,Z(t,15)),n("div",{style:{display:"flex",flexDirection:"column",gap:"3px",minWidth:"0"}},n("span.text",null,e.text),n("div.meta",null,o,e.link?n("a",{href:e.link.href,"data-external":e.link.href.startsWith("http")?"1":null},e.link.label):null),s))}function bi(e,t){const s=e.state,o=(s.caps?.controls??[]).filter(r=>!Ln.has(r.key)&&it(r,s.settings)),a=t.baseSettings();return[...new Set(o.map(r=>r.group))].map(r=>{const l=o.filter(p=>p.group===r),c=s.groupsOpen[r]!==!1,d=l.filter(p=>s.settings[p.key]!==a[p.key]).length;return n("section.group",{class:c?"open":void 0},n("div.grouphead",null,n("button.groupbtn",{"aria-expanded":String(c),"data-ctl":`group:${r}`,onclick:()=>{s.groupsOpen[r]=!c,e.touch("groupsOpen")}},Z(c?"chevronDown":"chevronRight",13),n("span.eyebrow",null,r),d?n("span.changed",null,`${d} changed`):null),d?n("button.reset",{onclick:()=>t.resetGroup(r)},"Reset"):null),c?n("div.groupbody",null,...l.map(p=>so(e,p,t,a))):null)})}function so(e,t,s,o){const a=e.state.settings[t.key],i=a!==o[t.key],r=(...u)=>n(i?"div.control.changed":"div.control",null,...u);if(t.kind==="switch"){const u=n("button.switch",{role:"switch","aria-checked":String(!!a),"aria-label":t.label,"data-ctl":`${t.key}:switch`,onclick:()=>s.changeSetting(t.key,!a)});return r(n("div.controlhead",null,Fe(n("span.label",{tabindex:"0"},t.label),t.help),u))}if(t.kind==="tri"){const u=[["off","Off"],["auto","Auto"],["on","On"]];return r(n("div.controlhead",null,Fe(n("span.label",{tabindex:"0"},t.label),t.help),n("div.seg",null,...u.map(([g,b])=>n("button",{"aria-pressed":String(a===g),"data-ctl":`${t.key}:${g}`,onclick:()=>s.changeSetting(t.key,g)},b)))))}if(t.kind==="choice"){const u=t.stops.map(g=>[g.label.toLowerCase(),g.label]);return r(n("div.controlhead",null,Fe(n("span.label",{tabindex:"0"},t.label),t.help),n("div.seg",null,...u.map(([g,b])=>n("button",{"aria-pressed":String(a===g),"data-ctl":`${t.key}:${g}`,onclick:()=>s.changeSetting(t.key,g)},b)))))}const l=u=>Math.pow((Number(u)-t.min)/(t.max-t.min||1),1/t.curve)*1e3,c=u=>t.min+Math.pow(u/1e3,t.curve)*(t.max-t.min),d=u=>t.decimals===0?String(Math.round(u)):u.toFixed(t.decimals),p=n("input.numberfield",{type:"text",value:d(Number(a)),inputmode:"decimal","aria-label":`${t.label}${t.unit?` in ${t.unit}`:""}`,"data-ctl":`${t.key}:field`,onchange:u=>{const g=Number(u.target.value);Number.isFinite(g)&&s.changeSetting(t.key,Math.min(t.max,Math.max(t.min,g)))}}),h=u=>{const g=c(Number(u.value));return t.decimals===0?Math.round(g):Number(g.toFixed(t.decimals))},f=n("input.slider",{type:"range",min:"0",max:"1000",step:"1",value:String(Math.round(l(Number(a)))),"aria-label":t.label,"aria-valuetext":`${d(Number(a))} ${t.unit}`.trim(),"data-ctl":`${t.key}:slider`,oninput:u=>{const g=u.target,b=h(g);p.value=d(b),g.setAttribute("aria-valuetext",`${d(b)} ${t.unit}`.trim())},onchange:u=>s.changeSetting(t.key,h(u.target))});return r(n("div.controlhead",null,Fe(n("span.label",{tabindex:"0"},t.label),t.help),t.unit?n("span.unit",null,t.unit):null,p),f,t.stops.length?n("div.stops",null,...t.stops.map(u=>n("span",null,u.label))):null)}function yi(e,t){const s=e.state,o=!!s.svg&&!s.tracing;return[wi(e,t),n("div.row",null,n("button.btn.primary",{style:{flex:"1"},disabled:!o,onclick:t.openExport},"Export"),n("button.btn",{disabled:!o,onclick:t.copySvg,title:"Paste straight into Figma or Illustrator"},"Copy SVG"),n("button.btn.icon",{disabled:!o,onclick:t.saveCard,"aria-label":"Save comparison card",title:"Save comparison card"},Z("share",16))),n("div.note",null,n("span",null,"Export runs a fresh full trace."),n("button.reset",{disabled:!s.source||s.tracing,onclick:t.traceNow},"Trace again"))]}function wi(e,t){const s=e.state,o=s.report;if(s.tracing)return n("div.readout.busy",null,n("span.pulse"),n("span.stage",null,_t(s)),n("span.muted.num.elapsed",{"data-live":"elapsed"},Bt(s)),n("button.reset",{onclick:t.cancel,title:"Esc"},"Cancel"));if(!o)return n("div.readout.idle",null,n("span.faint",null,s.source?"Nothing traced yet.":"Open an image to see what a setting does."));const a=s.result?.tier==="final"?s.previous:null,i=(c,d,p)=>n("div.cell",null,n("span.v.num",null,c),n("span.k",null,d),p),r=(s.settings.mode??"quality")==="fast",l=n("div.readout-badge-row",null,n("button.engine-pill"+(r?".fast":".quality"),{type:"button",title:r?"Fast vectorizer active. Click to switch to Quality mode.":"Quality vectorizer active. Click to switch to Fast mode.",onclick:()=>t.changeSetting("mode",r?"quality":"fast")},n("span.engine-name",null,r?"Fast":"Quality")),o.seconds!=null?n("span.time-pill.num",{title:`Traced in ${nt(o.seconds)}`},nt(o.seconds)):null);return n("div.readout",{"aria-live":"polite"},l,n("div.readout-grid",null,i(re(o.meanDe00),"dE00",a?Zt(o.meanDe00,a.meanDe00,c=>c.toFixed(2),.005,!0):null),i(oe(o.coordinates),"coordinates",a?Zt(o.coordinates,a.coordinates,oe,0,!1):null),i(ie(o.bytes),"file",a?Zt(o.bytes,a.bytes,ie,16,!1):null)))}function Zt(e,t,s,o,a){if(e==null||t==null)return null;const i=e-t;if(Math.abs(i)<=o)return n("span.delta.same",null,"no change");const r=i<0?"better":a?"worse":"same";return n(`span.delta.${r}`,null,`${i<0?"−":"+"}${s(Math.abs(i))}`)}const vi="https://logolabs.github.io/inkvec/";function xi(e,t,s){const o=n("div.chooser",{role:"region","aria-label":"How to trace this image"});let a=!1;const i=d=>{const p=e.state;a&&t.setOnOpen(d),p.prefs?.seenFirstRun||t.markSeen(),e.set({chooser:!1}),d==="custom"&&s()},r=()=>{const d=e.state;if(d.tracing&&d.generation===d.auto?.generation)return`Tracing now · ${_t(d)} · ${Bt(d)}`;const p=d.auto?.report;return p?`Done · ${re(p.meanDe00)} dE00 · ${oe(p.coordinates)} coordinates · ${ie(p.bytes)}`:d.stageState.kind!=="drawing"&&d.stageState.kind!=="decoding"?"Stopped; the stage says why":"Starting…"},l=()=>{const d=e.state,p=d.chooser&&!!d.source&&d.tab==="vectorize"&&!d.wizard&&!d.screen;if(o.hidden=!p,!p){F(o);return}const h=!d.prefs?.seenFirstRun,f=eo(e,t);F(o,n("div.choosehead",null,n("span.eyebrow",null,`Just opened · ${d.source?.name??""}`),n("button.btn.icon.compact.ghost",{"aria-label":"Close; Auto stands",title:"Close. Auto stands (Esc)",onclick:()=>i("auto")},Z("x",14))),h?n("p.chooseintro",null,"New: every image is traced the moment it opens. Keep Auto, or customise it a step at a time with the result beside each choice. Ignore this card and Auto stands."):null,n("div.choices",null,n("div.choice",null,n("span.choicename",null,Z("zap",14),"Auto"),n("span.choicebody.num",{"data-status":""},r()),n("button.btn.primary.compact",{"data-ctl":"chooser-auto",onclick:()=>i("auto")},"Keep Auto")),n("div.choice",null,n("span.choicename",null,Z("settings",14),"Custom"),n("span.choicebody",null,"What it is, colours, detail, shape, output: each explained, beside Auto's result."),n("button.btn.compact",{"data-ctl":"chooser-custom",onclick:()=>i("custom")},"Customise…"))),f?n("div.choosenews",null,n("span.eyebrow",null,"Auto chose"),f):null,n("label.remember",null,n("input",{type:"checkbox",checked:a,onchange:u=>{a=u.target.checked}}),n("span",null,"Remember my choice"),n("span.muted",null,"· change it in Settings")))},c=()=>{const d=o.querySelector("[data-status]");d&&(d.textContent=r())};return e.on(["chooser","source","tab","wizard","screen","auto","facts","prefs","preset","settings","groupSuggestions","palette","stageState"],l),e.on(["tracing","liveStages","liveNow","generation"],c),l(),o}const ft={kind:{id:"kind",title:"What is it?",does:"Picks the preset that suits the kind of image: a handful of settings chosen together. Each tile is a small draft of this image under that preset.",when:"When Auto's trace is not the kind of drawing you wanted: strokes that should be lines, a scan that should be two inks.",costs:"Nothing on its own. Photo or scan adds the denoiser to the full trace, about 0.6 s.",learn:["How a trace is made","algorithm/plain/00-overview.html"]},cleanup:{id:"cleanup",title:"Clean-up",does:"Repairs JPEG and screenshot damage before tracing, so the colours are the ones the artwork was drawn with rather than the ones the compression left.",when:"The source is a JPEG, a screenshot or a photo of a mark, and its flat colours come back speckled or with extra shades.",costs:"About 0.6 s per full trace, and a one-time download. Drafts skip it.",learn:["Intake and noise","algorithm/plain/01-intake.html"]},colours:{id:"colours",title:"Colours",does:"Shows every ink the trace found. Suggested groups are inks that are the same colour measured twice; grouping re-traces so the shapes between them join.",when:"The palette has near-duplicates, or more colours than the design has.",costs:"Fewer colours can merge a detail into its neighbour; the colour difference says when.",learn:["How the palette is found","algorithm/plain/03-palette.html"]},detail:{id:"detail",title:"Detail",does:"How closely curves follow the pixels, and how small a speck can be before it is dropped.",when:"Tighter for crests and filigree; looser for a smaller file with fewer nodes.",costs:"Tighter precision means more nodes and a bigger file; the readout above shows the trade.",learn:["Curve fitting","algorithm/plain/11-fitting.html"]},shape:{id:"shape",title:"Shape",does:"How the outlines are built: nodes an artist would place, fewer paths, one shape reused where it repeats, and what a curve or a corner costs the fit.",when:"The SVG will be edited by hand, or has to be as small as possible.",costs:"Editable nodes cost a little colour accuracy (about 0.04 dE00 on icons); the readout shows it here.",learn:["Symmetry and repeated shapes","algorithm/plain/10-symmetry.html"]},output:{id:"output",title:"Output",does:"How the file is written: the background, a margin around the drawing, and whether it is minified.",when:"Placing the SVG on a coloured page, or shipping it on the web.",costs:"None of these re-trace: they rewrite the same drawing.",learn:["Writing the SVG","algorithm/plain/13-emit.html"]}},ls={detail:["precision","speckleFloor"],shape:["editability","fewerPaths","matchRepeatedShapes","bezierCost","cornerAngle"],output:["transparentBackground","margin","minify"]},ki=[{preset:"logo",name:"Logo",why:"Flat colours and crisp shapes. The default."},{preset:"icon",name:"Icon",why:"A small flat mark: keeps one-pixel details, at most 16 colours."},{preset:"fine-detail",name:"Illustration or emoji",why:"Gradients and fine detail: curves fitted twice as closely."},{preset:"line-art",name:"Line art",why:"Even-width lines come back as strokes you can re-weight."},{preset:"monochrome",name:"One-colour logo",why:"Every shape drawn black on white, light colours included."},{preset:"monochrome-transparent",name:"Black, no background",why:"Black artwork on a transparent background, ready to place on any design."},{preset:"black-and-white",name:"Two-tone scan",why:"Split by lightness, like a photocopy: stamps, signatures."},{preset:"photo-or-scan",name:"Photo or scan",why:"JPEGs and screenshots: the denoiser cleans compression damage first.",note:"The draft is shown without the denoiser, which runs on the full trace."}];function Si(e){const t=e.state,s=!!t.caps?.denoiser.supported&&Xs(t.source,t.facts);return["kind","cleanup","colours","detail","shape","output"].filter(o=>o!=="cleanup"||s)}function $i(e,t,s,o){let a=null,i=[],r=null,l=e.state.view,c="auto",d=null,p=null;const h=n("div.wizhead"),f=n("div.wizintro"),u=n("div.wizcontrols"),g=n("div.wizlive"),b=n("div.wizguide"),x=n("div.wizscroll",null,f,u,g,b),M=n("div.wiznav");Vs(g,e);const v=()=>Si(e),S=()=>e.state.wizard?.step??"kind",y=k=>{k!==S()&&(S()==="kind"&&o.pause(),e.set({wizard:{step:k},hoverFill:null}),x.scrollTop=0)},$=()=>{const k=e.state.auto?.svg;return c!=="auto"||!k?null:(d?.svg!==k&&(d={svg:k,label:"Auto"}),d)},C=()=>e.set({compare:$()}),D=()=>{const k=e.state.auto?.svg;return k?(p?.svg!==k&&(p&&URL.revokeObjectURL(p.url),p={svg:k,url:URL.createObjectURL(new Blob([k],{type:"image/svg+xml"}))}),p.url):null},z=()=>{const k=v(),N=Math.max(0,k.indexOf(S())),E=ft[S()],V=r!==null&&(e.state.preset!==r.preset||De(e.state.caps,e.state.settings,r.settings).length>0||JSON.stringify(e.state.colourGroups)!==JSON.stringify(r.colourGroups));F(h,n("div.cardhead",null,n("span.eyebrow",null,`Custom · step ${N+1} of ${k.length}`),n("div",{style:{display:"flex",gap:"10px",alignItems:"baseline"}},V?n("button.reset",{"data-ctl":"wiz-undo",title:"Put every control and colour group back where they were when the wizard opened",onclick:()=>r&&s.restore(r)},"Undo my changes"):null,n("button.reset",{"data-ctl":"wiz-close",title:"Keep what is chosen so far (Esc)",onclick:()=>Ae(!0)},"Close"))),n("h2.wiztitle.serif",null,E.title),n("div.wizdots",{role:"tablist","aria-label":"Steps"},...k.map((K,I)=>n("button",{role:"tab","aria-selected":String(K===S()),"aria-label":`Step ${I+1}: ${ft[K].title}`,title:ft[K].title,class:Iy(K)}))),n("div.wizcompare",null,n("span.muted",null,"Left pane"),n("div.seg",{role:"group","aria-label":"What the left pane shows"},n("button",{"aria-pressed":String(c==="auto"&&!!e.state.auto?.svg),disabled:!e.state.auto?.svg,title:e.state.auto?.svg?"Auto's trace, to compare yours with":"Auto's trace has not landed yet",onclick:()=>{c="auto",C(),z()}},"Auto's trace"),n("button",{"aria-pressed":String(c==="source"||!e.state.auto?.svg),onclick:()=>{c="source",C(),z()}},"Source"))))},O=()=>{const k=ft[S()];F(f,n("p.wizlead",null,k.does)),F(b,n("dl.wizexplain",null,n("dt",null,"Change it when"),n("dd",null,k.when),n("dt",null,"What it costs"),n("dd",null,k.costs)),n("a.wizlearn",{href:vi+k.learn[1]},`Learn more: ${k.learn[0]} →`))},A=()=>{const k=v(),N=k.indexOf(S()),E=N===k.length-1;F(M,n("button.btn",{"data-ctl":"wiz-back",disabled:N<=0,onclick:()=>y(k[N-1])},"Back"),E?null:n("button.reset",{"data-ctl":"wiz-skip",title:"Keep what is chosen so far and go to the last step",onclick:()=>y(k[k.length-1])},"Skip to finish"),n("span",{style:{flex:"1"}}),E?n("button.btn",{"data-ctl":"wiz-export",disabled:!e.state.svg,onclick:()=>Ae(!0,!0)},"Export now"):null,E?n("button.btn.primary",{"data-ctl":"wiz-finish",onclick:()=>Ae(!0)},"Finish"):n("button.btn.primary",{"data-ctl":"wiz-next",onclick:()=>y(k[N+1])},"Next"))},P=()=>{if(!a)return;const k=v();if(!k.includes(S())){e.set({wizard:{step:k[Math.min(k.length-1,1)]}});return}z(),O(),A(),X(),j()},J=k=>e.state.caps?.controls.find(N=>N.key===k),H=k=>{const N=s.baseSettings();return k.map(E=>J(E)).filter(E=>!!E&&it(E,e.state.settings)).map(E=>so(e,E,s,N))},X=()=>{const k=document.activeElement instanceof HTMLElement&&u.contains(document.activeElement)?document.activeElement.getAttribute("data-ctl"):null,N=e.state,E=S();let V=[];E==="cleanup"?V=ee():E==="colours"?V=H(N.facts?.hasAlpha?["maxColours","traceTransparency"]:["maxColours"]):E==="detail"?V=[se(),...H(ls.detail??[])]:(E==="shape"||E==="output")&&(V=H(ls[E]??[])),F(u,...V),u.hidden=V.length===0,k&&u.querySelector(`[data-ctl="${k}"]`)?.focus({preventScroll:!0}),a&&z()},se=()=>n("div.wizrow",null,n("span.label",null,"Compare as"),n("div.seg",{role:"group","aria-label":"How the two panes are arranged"},...["side","wipe"].map(k=>n("button",{"aria-pressed":String(e.state.view===k),"data-ctl":`wiz-view:${k}`,onclick:()=>e.set({view:k})},k==="side"?"Side by side":"Wipe")))),ee=()=>{const k=e.state,N=k.caps?.denoiser,E=k.settings.cleanUpDamage,V=[k.source?.lossy?`the file is a ${k.source.container}, which stores colour approximately`:null,k.facts&&k.facts.noiseLevels>=Js?`its pixels measure ${k.facts.noiseLevels.toFixed(1)} levels of noise (a clean file reads about 0.5)`:null].filter(Boolean),K=(I,U,w)=>n("button.wizchoice",{"aria-pressed":String(E===I||I==="auto"&&E==="on"),"data-ctl":`wiz-cleanup:${I}`,onclick:()=>s.changeSetting("cleanUpDamage",I)},n("span.name",null,U),n("span.muted",null,w));return[V.length?n("p.wizwhy",null,`Offered because ${V.join(", and ")}.`):null,n("div.wizchoices",null,K("off","Off","Trace the pixels as they are, damage and all."),K("auto","Auto","Clean the damage first, where it is found.")),N?.supported&&E!=="off"&&k.denoiserFetch?.phase!=="ready"?n("div.wiznote",{"data-ctl":"wiz-denoiser-progress"},Et(k.denoiserFetch),n("span",null,`${zn(At(k.denoiserFetch,!0)??"Starting the denoiser…")} It comes from ${N.repo}, is checked against its SHA-256 on arrival and runs in this tab: the model comes down, your image never goes up. Until it is ready the trace runs without it, then again with it.`)):null,n("p.muted.wizsmall",null,"Left: Auto's trace, without it. Right: yours. It runs on the full trace only, so the right pane changes when the full trace lands, a moment after the draft.")].filter(I=>I!==null)};let Q=null;const j=()=>{const k=S();if(k==="kind"){Le();return}Q=null;const N=e.state;k==="colours"?F(g,N.palette.length?Ys(e,s):n("p.muted.wizsmall",null,"The palette appears when a trace lands."),n("p.muted.wizsmall",null,"Hover an ink or a group to single it out in the drawing on the right.")):k==="detail"||k==="shape"?F(g,ne()):k==="output"?F(g,ae(),ne()):F(g)},ne=()=>{const k=e.state,N=k.auto?.report??null,E=k.report,V=k.result?.tier==="draft",K=U=>U?U.structure?.nodes||Math.round(U.coordinates/2):null,I=(U,w,L,T,_=!0,q=0)=>{const G=w!==null&&L!==null&&!V&&!k.tracing?L-w:null,le=G===null||Math.abs(G)<=q?"same":G<0===_?"better":"worse";return n("tr",null,n("th",null,U),n("td.num",null,w===null?"—":T(w)),n("td.num",null,L===null?"—":T(L)),n(`td.num.delta.${le}`,null,G===null?"":Math.abs(G)<=q?"same":`${G<0?"−":"+"}${T(Math.abs(G))}`))};return n("div.card.wizreadout",{"aria-live":"polite"},n("div.cardhead",null,n("span.eyebrow",null,"Auto · yours"),n("span.muted",{style:{fontSize:"11px"}},k.tracing?"tracing…":V?"draft; the full trace follows":"full traces")),n("table",null,n("thead",null,n("tr",null,n("th"),n("th",null,"Auto"),n("th",null,"Yours"),n("th"))),n("tbody",null,I("colour difference",N?.meanDe00??null,E?.meanDe00??null,U=>U.toFixed(2),!0,.005),I("nodes",K(N),K(E),oe),I("paths",N?.paths??null,E?.paths??null,oe),I("file size",N?.bytes??null,E?.bytes??null,ie,!0,16))),n("span.muted.wizsmall",null,"Colour difference in dE00: under 1 is invisible side by side."))},ae=()=>{const k=e.state,N=k.auto,E=N?De(k.caps,N.settings,k.settings):[],V=(I,U)=>typeof U=="boolean"?U?"on":"off":typeof U=="number"?`${I.decimals?U.toFixed(I.decimals):Math.round(U)}${I.unit?` ${I.unit}`:""}`:String(U),K=k.caps?.presets.find(I=>I.id===k.preset);return n("div.card.wizchanges",null,n("span.eyebrow",null,"What you changed from Auto"),K&&N&&K.id!==N.preset?n("div.wizchange",null,n("span",null,"Preset"),n("span.num",null,K.name)):null,...E.map(I=>{const U=J(I);return!U||!N?null:n("div.wizchange",null,n("span",null,U.label),n("span.num",null,`${V(U,N.settings[I])} → ${V(U,k.settings[I])}`))}),k.colourGroups.length?n("div.wizchange",null,n("span",null,"Colour groups"),n("span.num",null,String(k.colourGroups.length))):null,!E.length&&!k.colourGroups.length&&!(K&&N&&K.id!==N.preset)?n("span.muted",null,"Nothing yet: this is Auto's trace."):n("span.muted.wizsmall",null,"Finish keeps these as ordinary settings, under Tune."))},te=()=>{const k=e.state,N=k.auto,E=k.caps,V=new Set(An(e).map(T=>T.preset)),K=k.colourGroups.length?{colourGroups:k.colourGroups}:{},I=N?Pe({...N.settings,...K}):null,U=[];let w=null;for(const T of ki){const _=E?.presets.find(G=>G.id===T.preset);if(!_)continue;const q={..._.settings,...K};if(I&&Pe(q)===I&&T.preset!=="photo-or-scan"&&w===null){w=T.name;continue}U.push({id:T.preset,name:T.name,why:T.why,note:T.note,settings:q,preset:T.preset,suggested:V.has(T.preset)})}return[{id:"auto",name:w?`${w} · Auto`:"As Auto traced",why:N?`Auto used ${Ks(E,N.settings,N.preset)}.`:"Auto's trace.",settings:null,preset:null,suggested:!1},...U.filter(T=>T.suggested),...U.filter(T=>!T.suggested)]},pe=k=>{const N=e.state;if(k.id==="auto"){const E=N.auto;return!!E&&N.preset===E?.preset&&De(N.caps,N.settings,E.settings).length===0}return N.preset===k.preset},Te=k=>{k.id==="auto"?s.backToAuto():k.preset&&s.setPreset(k.preset)},fe=k=>{if(!k){C();return}const N=e.state;if(k.id==="auto"||Me(k)){N.auto?.svg&&e.set({compare:$()??{svg:N.auto.svg,label:"Auto"}});return}const E=k.settings?o.get(Pe(k.settings)):void 0;E?.state==="done"&&e.set({compare:{svg:E.svg,label:`Preview · ${k.name} (draft)`}})},Me=k=>{const N=e.state.auto;if(!N||!k.settings)return!1;const E=e.state.colourGroups.length?{colourGroups:e.state.colourGroups}:{};return Pe(k.settings)===Pe({...N.settings,...E})},ze=k=>{const N=e.state;if(k.id==="auto"||Me(k)){const V=N.auto?.report;return V?{url:D(),text:`${re(V.meanDe00)} dE00 · ${oe(V.paths)} paths · ${ie(V.bytes)}`,busy:!1}:N.tracing?{url:null,text:"tracing…",busy:!0}:{url:null,text:"no trace yet",busy:!1}}const E=k.settings?o.get(Pe(k.settings)):void 0;return!E||E.state==="queued"?{url:null,text:N.auto?.report?"queued":"after Auto",busy:!0}:E.state==="running"?{url:null,text:"tracing a draft…",busy:!0}:E.state==="failed"?{url:null,text:"no preview",busy:!1}:{url:E.url,text:`${re(E.report.meanDe00)} dE00 · ${oe(E.report.paths)} paths · ${ie(E.report.bytes)}`,busy:!1}},Le=()=>{const k=te(),N=k.map(I=>I.id).join(",");if(!Q||Q.dataset.signature!==N){Q=n("div.kinds",{"data-signature":N,role:"radiogroup","aria-label":"What is it?"});for(const I of k){const U=n("button.kind",{role:"radio","data-key":I.id,"data-ctl":`wiz-kind:${I.id}`,onclick:()=>Te(I),onpointerenter:()=>fe(I),onpointerleave:()=>fe(null),onfocus:()=>fe(I),onblur:()=>fe(null)},n("span.kthumb",null,n("img",{alt:"",draggable:"false"}),n("span.kspin")),n("span.kname",null,I.name,I.suggested?n("span.ksugg",null,"suggested"):null),n("span.kwhy",null,I.why),I.note?n("span.knote",null,I.note):null,n("span.kstat.num"));Q.append(U)}F(g,Q,n("p.muted.wizsmall.kqueue"))}const E=e.state;E.auto&&!E.auto.report&&E.tracing||o.want(k.filter(I=>I.settings&&!Me(I)).map(I=>({key:Pe(I.settings),settings:I.settings})));for(const I of k){const U=Q.querySelector(`[data-key="${I.id}"]`);if(!U)continue;const w=ze(I),L=U.querySelector("img");w.url&&L.getAttribute("src")!==w.url&&(L.src=w.url),L.hidden=!w.url,U.classList.toggle("busy",w.busy),U.setAttribute("aria-checked",String(pe(I))),U.querySelector(".kstat").textContent=w.text}const V=o.pending(),K=g.querySelector(".kqueue");K&&(K.textContent=V?`Previews are small drafts of this image, traced one at a time behind the main trace · ${V} to go.`:"Previews are small drafts of this image. Hover one to see it large on the left; choose one and the full trace follows on the right.")};function de(){if(a)return;if(!e.state.auto&&e.state.svg&&e.state.report&&!e.state.tracing){const E=e.state;e.set({auto:{generation:E.resultGeneration,settings:{...E.settings},preset:E.preset,svg:E.svg,report:E.report,palette:E.palette,losses:E.losses}})}const k=e.state;r={settings:{...k.settings},preset:k.preset,colourGroups:[...k.colourGroups]},l=k.view,c="auto",Q=null;const N=n("div.sheetbody.wizbody",null,h,x,M);a=n("div.sheet.wizsheet",null,N),t.classList.add("wizarding"),t.append(a),e.set({wizard:{step:v()[0]},chooser:!1,view:k.view==="ab"?"side":k.view,compare:$()}),k.prefs?.seenFirstRun||s.markSeen(),i=[e.on(["wizard","facts","caps","prefs"],P),e.on(["denoiserFetch"],()=>{a&&S()==="cleanup"&&X()}),e.on(["settings","preset","colourGroups","view"],()=>{X(),(S()==="kind"||S()==="output")&&j()}),e.on(["report","result","tracing","palette","groupSuggestions","resultGroups","paletteSelection","simplifyTo","losses","auto","svg"],()=>{j(),(e.state.compare===null||e.state.compare===d||e.state.compare?.label==="Auto")&&C(),A()}),o.on(()=>{S()==="kind"&&j()}),e.on(["auto"],z)],P(),(M.querySelector("[data-ctl=wiz-next]")??N).focus?.()}function Ae(k=!1,N=!1){if(!a)return;for(const I of i)I();i=[],o.pause(),a.remove(),a=null,Q=null,t.classList.remove("wizarding"),p&&URL.revokeObjectURL(p.url),p=null,d=null;const E=e.state,V=r!==null&&(E.preset!==r.preset||De(E.caps,E.settings,r.settings).length>0||JSON.stringify(E.colourGroups)!==JSON.stringify(r.colourGroups)),K=E.auto?De(E.caps,E.auto.settings,E.settings).length:0;e.set({wizard:null,compare:null,hoverFill:null,view:l,railTab:V?"tune":E.railTab}),r=null,k&&V&&W(K?`Custom settings kept: ${K} control${K===1?"":"s"} changed from Auto, all of them under Tune.`:"Custom settings kept. Everything is under Tune.",{kind:"good"}),N&&s.openExport()}return{open:de,close:Ae,isOpen:()=>a!==null}}const cs=4,us=24;function Mi(e){const t={size:"1600x900",theme:"dark",fileName:!1,detail:!0,numbers:!0,zoom:12},s=n("img",{alt:"Preview of the comparison card",draggable:"false"}),o=n("div.cardstage",null,s,n("div.cardbusy",{"aria-hidden":"true"},n("span.cardspin"))),a=n("span.faint",{style:{fontSize:"11.5px"}});let i=0;const r=async()=>{const y=++i;o.classList.add("busy");try{const $=await ps(e,t);if(y!==i)return;s.src=$.toDataURL("image/png")}catch{y===i&&W("The card could not be drawn.",{kind:"bad"})}finally{y===i&&o.classList.remove("busy")}},l=()=>{const[y,$]=oo(t.size);o.style.setProperty("--ar",String(y/$)),a.textContent=`${y} × ${$} px · PNG · ${t.theme}`},c=[],d=()=>{c.forEach(y=>y()),l(),r()},p=(y,$,C)=>{const D=C.map(z=>n("button",{type:"button",role:"radio",onclick:()=>{t[y]=z.value,d()}},n("span",null,z.label),z.sub?n("small",null,z.sub):null));return c.push(()=>D.forEach((z,O)=>z.setAttribute("aria-checked",String(t[y]===C[O].value)))),ds($,n("div.cardseg",{role:"radiogroup","aria-label":$},...D))},h=(y,$,C)=>{const D=n("span.switch"),z=n("button.optrow",{type:"button",role:"switch",onclick:()=>{t[y]=!t[y],d()}},n("span.optext",null,n("span.optlabel",null,$),n("span.optnote",null,C)),D);return c.push(()=>{z.setAttribute("aria-checked",String(t[y])),D.setAttribute("aria-checked",String(t[y]))}),z},f=n("span.num",null,`${t.zoom}×`),u=n("input.slider",{type:"range",min:String(cs),max:String(us),step:"1",value:String(t.zoom),"aria-label":"Detail crop magnification",oninput:y=>{f.textContent=`${y.target.value}×`},onchange:y=>{t.zoom=Number(y.target.value),d()}}),g=n("div.optslider",null,n("div.optsliderhead",null,n("span",null,"Detail magnification"),f),u,n("div.stops",null,n("span",null,`${cs}×`),n("span",null,`${us}×`)));c.push(()=>{u.disabled=!t.detail,g.classList.toggle("off",!t.detail)});const b=(y,$,C)=>async()=>{const D=y.lastChild?.textContent??"";y.disabled=!0,y.lastChild&&(y.lastChild.textContent=$);try{await C()}finally{y.disabled=!1,y.lastChild&&(y.lastChild.textContent=D)}},x=async()=>{const y=await ps(e,t);return new Promise($=>y.toBlob($,"image/png"))},M=n("button.btn",{type:"button"},Z("copy",15),n("span",null,"Copy image"));M.onclick=b(M,"Copying…",async()=>{const y=await x();if(y)try{await navigator.clipboard.write([new ClipboardItem({"image/png":y})]),W("Card copied.",{kind:"good"})}catch{W("This system would not take an image on the clipboard; save it instead.",{kind:"bad"})}});const v=n("button.btn.primary",{type:"button"},Z("download",15),n("span",null,"Download PNG"));v.onclick=b(v,"Saving…",async()=>{const y=e.state.source?.name.replace(/\.[^.]+$/,"")??"inkvec",$=await x();if(!$)return;const C=await Rs(`${y}-card.png`,new Uint8Array(await $.arrayBuffer()),[{name:"PNG",extensions:["png"]}]);C&&(ue(),W(C.path?`Card saved to ${C.path}`:`Downloaded ${C.name}`,{kind:"good",action:Ot(C.path)}))});const S=n("div.modal.composer",{tabindex:"-1"},n("div.composerhead",null,n("div",null,n("h2",null,"Save comparison card"),n("p",null,"The before and after on one poster, with the measurements behind it.")),n("button.btn.icon.ghost",{type:"button","aria-label":"Close",onclick:ue},Z("x",16))),n("div.composerbody",null,n("div.composerpreview",null,o,a),n("div.composerside",null,p("size","Format",[{value:"1600x900",label:"Landscape",sub:"1600 × 900"},{value:"1080x1080",label:"Square",sub:"1080 × 1080"}]),p("theme","Appearance",[{value:"dark",label:"Dark"},{value:"light",label:"Light"}]),ds("On the card",n("div.optlist",null,h("numbers","The numbers","Colour difference, coordinates and file size."),h("detail","A zoomed detail","The vector edge cutting through the source's pixels."),g,h("fileName","The file name","Off by default, so unreleased work stays private."))),n("div.composeractions",null,v,M))));Ye(S),d()}function ds(e,t){return n("div.optgroup",null,n("span.eyebrow",null,e),t)}function oo(e){return e==="1600x900"?[1600,900]:[1080,1080]}async function ps(e,t){const[s,o]=oo(t.size),a=document.createElement("canvas");a.width=s,a.height=o;const i=a.getContext("2d");if(!i)return a;const r=t.theme==="dark",l=r?"#1a1816":"#faf8f5",c=r?"#faf8f5":"#1a1816",d=r?"rgba(250,248,245,.4)":"rgba(26,24,22,.46)",p=r?"rgba(250,248,245,.55)":"rgba(26,24,22,.58)",h=r?"#c9754a":"#9c5730",f=r?"#26231f":"#ffffff",u=r?"#211f1c":"#e8e3db";await Promise.all([document.fonts.load('500 20px "Playfair Studio"'),document.fonts.load('400 20px "Inter Studio"'),document.fonts.load('500 20px "Inter Studio"')]).catch(()=>{}),i.fillStyle=l,i.fillRect(0,0,s,o);const g=i.createRadialGradient(s*.2,-o*.1,0,s*.2,-o*.1,s*.6);g.addColorStop(0,"rgba(201,117,74,.16)"),g.addColorStop(1,"rgba(201,117,74,0)"),i.fillStyle=g,i.fillRect(0,0,s,o);const b=Math.round(s*.042),x=t.size==="1080x1080",M=x?s-b*2:Math.round(s*.46);i.fillStyle=d,i.font=`500 ${Math.round(s*.0105)}px "Inter Studio", Inter, sans-serif`,i.letterSpacing="3px",i.fillText("PNG TRACED TO SVG",b,b+14),i.letterSpacing="0px",i.fillStyle=c;const v=Math.round(s*(x?.05:.039));i.font=`500 ${v}px "Playfair Studio", Georgia, serif`,i.fillText("A logo, measured",b,b+v+26),i.fillText("rather than guessed.",b,b+v*2+30);let S=b+v*2+96;t.fileName&&e.state.source&&(i.fillStyle=p,i.font=`400 ${Math.round(s*.013)}px "Inter Studio", Inter, sans-serif`,i.fillText(e.state.source.name,b,S),S+=40);const y=e.state.report;if(t.numbers&&y){const se=[[re(y.meanDe00),"mean dE00"],[oe(y.coordinates),"coordinates"],[`${(y.bytes/1024).toFixed(1)} KB`,"SVG"]];let ee=b;for(const[Q,j]of se.slice(0,x?2:3)){i.fillStyle=Q===re(y.meanDe00)?h:c;const ne=Math.round(s*.026);i.font=`500 ${ne}px "Playfair Studio", Georgia, serif`,i.fillText(Q,ee,S+ne),i.fillStyle=d,i.font=`400 ${Math.round(s*.0085)}px "Inter Studio", Inter, sans-serif`,i.fillText(j,ee,S+ne+20),ee+=Math.max(i.measureText(j).width,120)+Math.round(s*.05)}}i.fillStyle=p,i.font=`400 ${Math.round(s*.0095)}px "Inter Studio", Inter, sans-serif`,i.fillText("traced with Inkvec Studio · by LogoLabs",b,o-b);const $=x?b:b+M,C=s-$-b,D=x?Math.round(o*.42):b,z=Math.round(x?o*.3:o*.44),O=(se,ee,Q,j)=>{for(let ae=0;ae*14{i.save(),i.beginPath(),i.rect(ee,D,A,z),i.clip(),O(ee,D,A,z);const ne=await Jt(se),ae=Math.min((A-40)/ne.width,(z-40)/ne.height),te=ne.width*ae,pe=ne.height*ae;i.drawImage(ne,ee+(A-te)/2,D+(z-pe)/2,te,pe),i.restore(),i.fillStyle=j,i.font=`500 ${Math.round(s*.0085)}px "Inter Studio", Inter, sans-serif`,i.letterSpacing="2px",i.fillText(Q,ee+12,D+22),i.letterSpacing="0px"};if(P&&await X(P.preview,$,P.container.toUpperCase(),d),H&&await X(H.url,$+A+12,"SVG",h),t.detail&&P&&H){const se=D+z+14,ee=o-se-b-40;if(ee>60){const Q=e.state.worstCorner,j=t.zoom,ne=Q?.x??H.w/2,ae=Q?.y??H.h/2,te=$+C/2-ne*j,pe=se+ee/2-ae*j;i.save(),i.beginPath(),i.rect($,se,C,ee),i.clip(),i.fillStyle=r?"#141210":"#f0ece6",i.fillRect($,se,C,ee);const Te=await Jt(P.preview);i.imageSmoothingEnabled=!1,i.drawImage(Te,te,pe,H.w*j,H.h*j),i.imageSmoothingEnabled=!0,i.strokeStyle=r?"rgba(250,248,245,.09)":"rgba(26,24,22,.12)",i.lineWidth=1;const fe=(te%j+j)%j,Me=(pe%j+j)%j;for(let de=$+fe;de<$+C;de+=j)i.beginPath(),i.moveTo(Math.round(de)+.5,se),i.lineTo(Math.round(de)+.5,se+ee),i.stroke();for(let de=se+Me;de0&&i[3]>0&&(t=i[2],s=i[3]),a.setAttribute("width",String(t)),a.setAttribute("height",String(s)),o=new XMLSerializer().serializeToString(a)}return{url:`data:image/svg+xml;charset=utf-8,${encodeURIComponent(o)}`,w:t,h:s}}function Jt(e){return new Promise((t,s)=>{const o=new Image;o.onload=()=>t(o),o.onerror=s,o.src=e})}const hs=[512,1024,2048];function fs(e,t){const s=e.state;return!s.svg||!s.report?null:{svg:s.svg,report:{meanDe00:s.report.meanDe00,coordinates:s.report.coordinates,paths:s.report.paths,tracedPx:s.report.tracedPx},palette:s.palette.map(o=>o.kind==="gradient"?{hex:o.hex,traced:o.traced,share:o.share,stops:o.stops}:{hex:o.hex,traced:o.traced,share:o.share}),losses:s.losses.map(o=>({text:o.text})),formats:t}}function ao(e,t,s){const o=ca({svg:!0,svgMinified:!0,pngSizes:[...hs],favicon:!1,assetPack:!0});let a=[],i=e.state.prefs?.outputFolder??null;const r=n("div.sheetbody"),l=n("div.sheet",{onmousedown:u=>{u.target===l&&c()}});l.append(r);const c=()=>{l.remove(),e.set({exportOpen:!1})},d=u=>a.filter(g=>g.group===u).reduce((g,b)=>g+b.bytes,0),p=async()=>{cn({export:{...o,pngSizes:[...o.pngSizes]}});const u=fs(e,o);if(u){try{a=await B.planExport(u)}catch(g){a=[],W(String(g),{kind:"bad"})}f()}},h=(u,g,b,x)=>n("button.format",{onclick:x,"aria-pressed":String(b)},n("span.checkbox",{"aria-checked":String(b)},Z("check",11)),n("span",null,u),n("span.size",null,b?ie(d(g)):"—"));function f(){const u=a.reduce((g,b)=>g+b.bytes,0);F(r,n("div.cardhead",null,n("span.serif",{style:{fontSize:"19px"}},"Export"),n("button.reset",{onclick:c},"Close")),h("SVG","svg",o.svg,()=>{o.svg=!o.svg,p()}),h("SVG (minified)","svgMinified",o.svgMinified,()=>{o.svgMinified=!o.svgMinified,p()}),h("PNG · 512 / 1024 / 2048","png",o.pngSizes.length>0,()=>{o.pngSizes=o.pngSizes.length?[]:[...hs],p()}),h("ICO and favicon set","favicon",o.favicon,()=>{o.favicon=!o.favicon,p()}),h("Asset pack (.zip)","assetPack",o.assetPack,()=>{o.assetPack=!o.assetPack,p()}),n("div.format",{style:{cursor:"default"}},n("span.muted",{style:{fontSize:"11px"}},"To"),n("span",{style:{flex:"1"}},a.length>1?"Your downloads, as one .zip":"Your downloads")),n("button.btn.primary",{disabled:!a.length,onclick:async()=>{let g=i;g="",c(),await s();const b=fs(e,o);if(b)try{const x=await B.writeExport(b,g??"");W(ss?`${x.length} file${x.length===1?"":"s"} written to ${g}`:`Downloaded ${x[0]??"the export"}`,{kind:"good",action:Ot(ss?x[0]??null:null)})}catch(x){W(String(x),{kind:"bad"})}}},a.length?`Download ${a.length} file${a.length===1?"":"s"} · ${ie(u)}`:"Nothing selected"))}e.set({exportOpen:!0}),t.append(l),f(),p()}function lt(){return Ai()}function Ti(){try{return window.self!==window.top}catch{return!0}}function Ai(){const e=n("button.btn.ghost.compact.fullscreen",{"data-ctl":"fullscreen",title:"Fill the screen (Esc to leave)",onclick:()=>{document.fullscreenElement?document.exitFullscreen():document.documentElement.requestFullscreen().catch(()=>o())}},Z("maximize",14),n("span",null,"Full screen")),t=e.querySelector("span"),s=()=>{t&&(t.textContent=document.fullscreenElement?"Exit full screen":"Full screen")};document.addEventListener("fullscreenchange",s);const o=()=>window.open(window.location.href,"_blank","noopener"),a=Ti()?n("button.btn.ghost.compact",{"data-ctl":"own-tab",title:"The same app at its own address, with the whole window",onclick:o},Z("external",14),n("span",null,"Open in its own tab")):null,i=n("a.btn.ghost.compact",{"data-ctl":"home",href:"../index.html",title:"The Inkvec front page: what's new, before and after, results"},Z("globe",14),n("span",null,"Inkvec home")),r=n("a.btn.ghost.compact.desktopapp",{"data-ctl":"desktop-app",href:_s,target:"_blank",rel:"noopener noreferrer",title:va},Z("download",14),n("span",null,"Desktop app"));return n("div.webcontrols",null,i,r,document.fullscreenEnabled?e:null,a)}const gs="https://logolabs.github.io/inkvec/studio/",Xt="./guide/";function io(e){if(e.screen==="settings")return"settings.html";switch(e.tab){case"minify":return"minify.html";case"fabricate":return"fabricate.html";case"batch":return"batch.html";default:return e.source?e.wizard?"getting-started.html#the-custom-wizard":e.railTab==="tune"?"tune.html":"result.html":"getting-started.html"}}function Kt(e){Oo()?ya(e).catch(()=>W("Could not open the link in your browser.",{kind:"bad"})):window.open(e,"_blank","noopener")}let Ne=null;function Pn(e="index.html"){if(Ne){Ne.querySelector("iframe")?.setAttribute("src",Xt+e);return}const t=n("iframe",{title:"Inkvec Studio user guide",src:Xt+e,style:{flex:"1",width:"100%",border:"0",background:"var(--stage)"}}),s=n("div.screenbody",{style:{display:"flex",overflow:"hidden"}},t),o=()=>{try{const a=t.contentWindow?.location,i=a?a.pathname.indexOf("/guide/"):-1;if(a&&i>=0)return a.pathname.slice(i+7)+a.hash}catch{}return""};Ne=n("div.screen",{role:"dialog","aria-label":"User guide"},n("div.screenbar",{"data-tauri-drag-region":""},n("span",{style:{fontSize:"12.5px",fontWeight:"600"}},"User guide"),n("div.spacer"),n("button.btn.compact.ghost",{onclick:()=>t.setAttribute("src",Xt+"index.html")},"Contents"),n("button.btn.compact.ghost",{title:"The same page on logolabs.github.io, in your browser",onclick:()=>Kt(gs+o())},"Open in browser"),n("button.btn.compact",{onclick:pn},"Done"),n("div.sep"),lt()),s),t.addEventListener("load",()=>{const a=t.contentDocument;if(!a||!a.querySelector('meta[name="inkvec-guide"]')){F(s,n("div.firstrun",null,n("span.serif",{style:{fontSize:"22px"}},"The guide is not bundled with this build"),n("span.faint",{style:{maxWidth:"46ch",textAlign:"center"}},"The same guide is published online."),n("button.btn.primary",{onclick:()=>Kt(gs)},"Open the online guide")));return}a.addEventListener("keydown",i=>{i.key==="Escape"&&pn(),i.key==="F1"&&i.preventDefault()}),a.addEventListener("click",i=>{const l=i.target?.closest?.("a[href]")?.getAttribute("href")??"";/^(https?:|mailto:)/i.test(l)&&(i.preventDefault(),Kt(l))})}),window.addEventListener("keydown",ro,!0),(document.getElementById("app")??document.body).append(Ne)}function pn(){window.removeEventListener("keydown",ro,!0),Ne?.remove(),Ne=null}function ro(e){e.stopImmediatePropagation(),(e.key==="Escape"||e.key==="F1")&&(e.preventDefault(),e.key==="Escape"&&pn())}function Ei(e){window.addEventListener("keydown",t=>{t.key!=="F1"||Ne||(t.preventDefault(),Pn(io(e.state)))})}const gt=30,ms=8;function zi(e){const t=n("div.batchbar"),s=n("div.batchhead.eyebrow"),o=n("div.batchviewport"),a=n("div.batchfoot"),i=n("section.stage",{style:{background:"var(--paper)"}},t,s,o,a),r=n("div"),l=n("div"),c=n("div");o.append(r,l,c);const d=()=>{const v=e.state.batch;return v.failuresFirst?[...v.rows].sort((S,y)=>+(y.state==="failed")-+(S.state==="failed")):v.rows},p=()=>n("div.firstrun",{style:{minHeight:"100%",background:"transparent"}},n("div.drop",null,n("span.glyph",null,Z("folder",32)),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.headline",null,"Trace a whole folder"),n("span.faint",{style:{fontSize:"13px",maxWidth:"46ch"}},"Every PNG, JPEG and WebP in it becomes an SVG in the output folder. A row that fails stays listed after the run.")),n("button.btn.primary",{onclick:()=>void u()},"Choose a folder…"))),h=()=>{const v=d();if(s.hidden=v.length===0,!v.length){r.style.height="0px",c.style.height="0px",F(l,p());return}const S=o.scrollTop,y=Math.ceil(o.clientHeight/gt)+ms*2,$=Math.max(0,Math.floor(S/gt)-ms),C=v.slice($,$+y);r.style.height=`${$*gt}px`,c.style.height=`${Math.max(0,(v.length-$-C.length)*gt)}px`,F(l,...C.map(D=>Li(D,e,()=>e.touch("batch"))))};o.addEventListener("scroll",h,{passive:!0});const f=async()=>{const v=await ma({directory:!0,multiple:!1});return typeof v=="string"?v:null},u=async()=>{const v=await f();if(v)try{const S=await B.batchScan(v),y=e.state.batch;y.folder=v,y.outputDir??=`${v}${v.includes("\\")?"\\":"/"}svg`,y.rows=S.map(($,C)=>({id:C,path:$,file:$.split(/[\\/]/).pop()??$,preset:y.preset,state:"queued",de00:null,coordinates:null,outBytes:null,destination:"",message:null,seconds:null})),y.totals=null,e.touch("batch")}catch(S){W(String(S),{kind:"bad"})}},g=async()=>{const v=e.state.batch;if(!v.folder||!v.outputDir){W("Choose a folder of images and an output folder first.");return}v.running=!0,v.paused=!1,e.touch("batch");try{await B.batchStart({files:v.rows.map(S=>S.path),preset:v.preset,overrides:v.rows.filter(S=>S.preset!==v.preset).map(S=>[S.id,S.preset]),outputDir:v.outputDir,skipExisting:v.skipExisting})}catch(S){v.running=!1,e.touch("batch"),W(String(S),{kind:"bad"})}},b=()=>{const v=e.state.batch,S=e.state.caps?.presets??[],y=($,C,D)=>n("button.btn.compact",{onclick:C,title:null},$);F(t,n("select",{style:{height:"28px",padding:"0 8px",borderRadius:"7px",border:"1px solid var(--rule2)",background:"var(--card)",color:"var(--ink)",font:"400 12px var(--font-body)"},"aria-label":"Preset for all",onchange:$=>{const C=$.target.value;v.preset=C;for(const D of v.rows)D.state==="queued"&&(D.preset=C);e.touch("batch")}},...S.map($=>n("option",{value:$.id,selected:v.preset===$.id?"":null},`Preset for all · ${$.name}`))),y(v.folder?`Input · ${v.folder}`:"Choose a folder…",()=>void u()),y(v.outputDir?`Output · ${v.outputDir}`:"Output folder…",async()=>{const $=await f();$&&(v.outputDir=$,e.touch("batch"))}),n("button.btn.compact",{"aria-pressed":String(v.skipExisting),onclick:()=>{v.skipExisting=!v.skipExisting,e.touch("batch")}},"Skip existing"),n("div.spacer"),n("button.btn.compact",{"aria-pressed":String(v.failuresFirst),disabled:!v.rows.some($=>$.state==="failed"),onclick:()=>{v.failuresFirst=!v.failuresFirst,e.touch("batch")}},"Failures first"),v.running?y(v.paused?"Resume":"Pause",()=>{v.paused=!v.paused,B.batchPause(v.paused),e.touch("batch")}):null,v.running?y("Cancel",()=>void B.batchCancel()):n("button.btn.primary.compact",{disabled:!v.rows.length,onclick:()=>void g()},"Start")),F(s,n("span"),n("span",null,"File"),n("span",null,"Preset"),n("span",null,"Status"),n("span.right",null,"dE00"),n("span.right",null,"Coordinates"),n("span.right",null,"Output"),n("span",null,"Destination"))},x=()=>{const v=e.state.batch,S=v.totals,y=S?.finished??0,$=S?.total??v.rows.length,C=S&&S.sourceBytes>0?1-S.bytesWritten/S.sourceBytes:null;F(a,n("div",{style:{flex:"1",maxWidth:"420px",display:"flex",flexDirection:"column",gap:"6px"}},n("div",{style:{display:"flex",justifyContent:"space-between",color:"var(--faint)"}},n("span",null,$?`${y} of ${$} done${S?.failed?` · ${S.failed} failed`:""}`:"Nothing queued"),n("span",null,S?.remaining!=null?`${ia(S.remaining)} left`:"")),n("div.progress",null,n("i",{style:{width:$e($?y/$:0)}}))),S?n("span.faint",null,`mean dE00 ${re(S.meanDe00)} · ${ie(S.bytesWritten)} written${C!==null?` · ${$e(C)} smaller than source`:""}`):null,n("button.reset",{style:{marginLeft:"auto"},disabled:!v.rows.some(D=>D.state!=="queued"),onclick:async()=>{const D=await B.batchStatsCsv(),z=await ba({defaultPath:"stats.csv",filters:[{name:"CSV",extensions:["csv"]}]});z&&(await B.saveBytes(z,[...new TextEncoder().encode(D)]),W(`Stats written to ${z}`,{kind:"good",action:{label:"Show in folder",run:()=>void wa(z)}}))}},"Export stats.csv"))},M=()=>{b(),x(),h()};return e.on(["batch","caps"],M),M(),i}function Li(e,t,s){const o=e.state==="failed"?".failed":e.state==="running"?".running":"",a=e.state==="queued",i=n(`button.reset${a?"":".muted"}`,{style:{textAlign:"left",color:a?"var(--dim)":"var(--muted)"},disabled:!a,title:a?"Use a different preset for this file":"Already run",onclick:()=>Pi(i,e,t,s)},Di(e.preset));return n(`div.batchrow${o}`,{title:e.message??e.path},n("span",{style:{width:"14px",height:"14px",borderRadius:"4px",background:"var(--surface)",display:"block"}}),n("span.trunc.dim",null,e.file),i,n(`span.state.${e.state}`,null,n("i"),e.state),n("span.right",{class:e.de00===null?"muted":"dim"},e.de00===null?"—":re(e.de00)),n("span.right.faint",null,e.coordinates===null?"—":oe(e.coordinates)),n("span.right.faint",null,e.outBytes===null?"—":ie(e.outBytes)),n("span.trunc.muted",null,e.message??e.destination))}function Pi(e,t,s,o){const a=s.state.caps?.presets??[];kn(e,n("div.menu",null,...a.map(i=>n("button.item",{onclick:()=>{const r=s.state.batch.rows.find(l=>l.id===t.id);r&&(r.preset=i.id),ue(),o()}},n("span",{style:{flex:"1"}},i.name),n("span.when",null,i.id===t.preset?"current":i.subtitle)))))}function Di(e){return e.split("-").map((t,s)=>s===0?t[0].toUpperCase()+t.slice(1):t).join(" ")}function Fi(e){const t=n("div.pane",null,n("span.eyebrow.panelabel",null,"Before")),s=n("div.pane",null,n("span.eyebrow.panelabel",null,"After")),o=n("div.wipehandle",{role:"separator","aria-label":"Wipe"}),a=n("div.panes",null,t,s,o),i=n("div.viewertools"),r=n("section.stage",null,i,a);let l="side",c=.5,d=!1,p="auto",h=null,f=null,u=!1;const g=()=>{u||!e.state.prefs||(u=!0,{minifyView:l,minifyBackdrop:p}=la())},b=()=>{g();const A=p==="auto"?h:p;a.classList.toggle("onlight",A==="light"),a.classList.toggle("ondark",A==="dark")},x=()=>{if(g(),a.classList.toggle("stacked",l!=="side"),o.style.display=l==="wipe"?"":"none",l==="side")t.classList.remove("hidden"),s.classList.remove("hidden"),t.style.clipPath="",s.style.clipPath="";else if(l==="wipe"){t.classList.remove("hidden"),s.classList.remove("hidden");const A=`${(c*100).toFixed(2)}%`;o.style.left=A,t.style.clipPath=`inset(0 ${(100-c*100).toFixed(2)}% 0 0)`,s.style.clipPath=`inset(0 0 0 ${A})`}else t.style.clipPath="",s.style.clipPath="",t.classList.toggle("hidden",!d),s.classList.toggle("hidden",d)};o.addEventListener("pointerdown",A=>{o.setPointerCapture(A.pointerId);const P=H=>{const X=a.getBoundingClientRect();c=Math.min(.98,Math.max(.02,(H.clientX-X.left)/X.width)),x()},J=()=>{o.removeEventListener("pointermove",P),o.removeEventListener("pointerup",J)};o.addEventListener("pointermove",P),o.addEventListener("pointerup",J)}),window.addEventListener("keydown",A=>{e.state.tab!=="minify"||e.state.screen||A.key!==" "||A.repeat||A.target instanceof HTMLInputElement||A.target instanceof HTMLTextAreaElement||(A.preventDefault(),d=!0,x())}),window.addEventListener("keyup",A=>{A.key===" "&&d&&(d=!1,x())});const M=()=>{g();const A=e.state.minify,P=A.result;F(i,n("div.seg",null,...["side","wipe","ab"].map(J=>n("button",{"aria-pressed":String(l===J),disabled:!A.before,onclick:()=>{l=J,cn({minifyView:J}),x(),M()}},J==="side"?"Side by side":J==="wipe"?"Wipe":"A/B"))),n("span.muted",{style:{fontSize:"11.5px"}},"hold ",n("kbd",null,"Space")," to flick"),n("div",{style:{display:"flex",alignItems:"center",gap:"8px",marginLeft:"12px"}},n("span.muted",{style:{fontSize:"11.5px"}},"Backdrop"),n("div.seg",{role:"group","aria-label":"Backdrop"},...["auto","light","dark"].map(J=>n("button",{"aria-pressed":String(p===J),disabled:!A.before,onclick:()=>{p=J,cn({minifyBackdrop:J}),b(),M()}},J==="auto"?"Auto":J==="light"?"Light":"Dark")))),n("span.muted",{style:{marginLeft:"auto",fontSize:"11.5px"}},P?`Nothing moved more than ${A.settings.tolerancePx} px${P.differenceDe00===null?"":` · measured ${re(P.differenceDe00)} dE00`}`:""))},v=n("aside.minifyrail"),S=n("div",{style:{flex:"1",minHeight:"0",display:"flex"}},r,v);let y=0;const $=async()=>{const A=e.state.minify;if(!A.before)return;const P=++y;try{const J=await B.minify(A.before,A.settings);if(P!==y)return;e.state.minify.result=J,e.state.minify.error=null}catch(J){if(P!==y)return;e.state.minify.result=null,e.state.minify.error=String(J)}e.touch("minify")},C=async()=>{const[A]=await yn([{name:"SVG",extensions:["svg"]}]);if(A)try{const P=await Ct(A);e.state.minify.name=A.name,e.state.minify.before=P,e.state.minify.result=null,e.state.minify.error=null,e.touch("minify"),await $()}catch(P){W(String(P),{kind:"bad"})}},D=async A=>{if(!A.name.toLowerCase().endsWith(".svg"))return!1;const P=await Ct(A);return e.state.minify.name=A.name,e.state.minify.before=P,e.touch("minify"),await $(),!0};S.acceptDropped=D;const z=()=>n("div.drop.solid",null,n("span.glyph",null,Z("file",32)),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.headline",null,"Drop an SVG here"),n("span.faint",{style:{fontSize:"13px"}},"or choose one to rewrite it in the fewest segments")),n("button.btn.primary",{onclick:C},"Open an SVG")),O=()=>{const A=e.state.minify,P=A.result;if(M(),x(),F(t,n("span.eyebrow.panelabel",null,`Before${P?` · ${ie(P.bytesBefore)}`:""}`),A.before?Be(A.before):z()),F(s,n("span.eyebrow.panelabel",null,`After${P?` · ${ie(P.bytesAfter)}`:""}`),P?Be(P.svg):A.before?null:n("span.faint.panehint",null,"The rewritten drawing appears here, on the same ground, so nothing can hide.")),A.before!==f){const H=A.before;f=H,h=null,b(),H&&co(H).then(X=>{f===H&&(h=X,b())})}if(!A.before){F(v,n("div.railscroll",{style:{justifyContent:"center",alignItems:"center",textAlign:"center",gap:"16px"}},n("span.serif",{style:{fontSize:"20px"}},"Minify an SVG"),n("span.faint",{style:{fontSize:"12.5px",lineHeight:"1.6",maxWidth:"30ch"}},"From Illustrator, Figma, a stock site — anything. It gets rewritten with the fewest segments that draw the same picture, and the result appears as you open it.")));return}const J=P?1-P.bytesAfter/Math.max(1,P.bytesBefore):0;F(v,n("div.railscroll",{style:{gap:"20px",padding:"22px 20px"}},A.error?n("div.card",null,n("span.eyebrow",null,"This file would not parse"),n("span.faint",{style:{fontSize:"12.5px",lineHeight:"1.6"}},A.error)):n("div.minifyresult",null,n("span.eyebrow",null,P?`Result · ${nt((P.ms??0)/1e3)}`:"Working"),n("div",{style:{display:"flex",alignItems:"baseline",gap:"10px"}},n("span.figure",null,P?$e(J):"—"),n("span.dim",{style:{fontSize:"14px",paddingBottom:"6px"}},"smaller")),P?n("span.faint.num",{style:{fontSize:"13px"}},`${ie(P.bytesBefore)} → ${ie(P.bytesAfter)}`):null),P?n("div.stats",null,...[["numbers before",oe(P.numbersBefore)],["numbers after",oe(P.numbersAfter)],["paths",`${P.pathsBefore} → ${P.pathsAfter}`],["segments",`${oe(P.segmentsBefore)} → ${oe(P.segmentsAfter)}`],["difference",P.differenceDe00===null?"—":`${re(P.differenceDe00)} dE00`],["kept as drawn",`${P.guarded} run${P.guarded===1?"":"s"}`]].map(([H,X])=>n("div",null,n("span.k",null,H),n("span.v",null,X)))):null,Ni(e,$),n("div.card",null,n("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},n("span.dim",{style:{fontSize:"12.5px"}},"Corner threshold"),(()=>{const H=n("span.num.faint",{style:{fontSize:"12px",width:"34px"}},`${A.settings.cornerDegrees}°`);return n("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},n("input.slider",{type:"range",min:"5",max:"90",step:"1",style:{width:"120px"},value:String(A.settings.cornerDegrees),"aria-label":"Corner threshold in degrees",oninput:X=>{H.textContent=`${X.target.value}°`},onchange:X=>{A.settings.cornerDegrees=Number(X.target.value),e.touch("minify"),$()}}),H)})()),n("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},n("span.dim",{style:{fontSize:"12.5px"}},"Document cleanup"),n("button.switch",{role:"switch","aria-checked":String(A.settings.documentCleanup),"aria-label":"Document cleanup",onclick:()=>{A.settings.documentCleanup=!A.settings.documentCleanup,e.touch("minify"),$()}})),n("span.muted",{style:{fontSize:"11.5px",lineHeight:"1.5"}},"Removes ids, groups, editor metadata and trailing zeros. Same geometry.")),P&&P.removed.length?n("div",{style:{display:"flex",flexDirection:"column",gap:"7px"}},n("span.eyebrow",null,"Removed"),...P.removed.map(H=>n("div",{style:{display:"flex",alignItems:"baseline",gap:"10px",fontSize:"12px"}},n("span.faint",{style:{flex:"1"}},H.what),n("span.dim.num",null,H.amount)))):null),n("div.railfoot",null,A.before?n("button.btn.ghost",{style:{justifyContent:"flex-start",gap:"8px"},title:"Minifying keeps every path the file has. Re-tracing draws the file again from its render: stacked or translucent paths, broken outlines and thousands of nodes come back as a few clean shapes, and the report measures the result against the original.",onclick:()=>window.dispatchEvent(new CustomEvent("inkvec:retrace",{detail:{svg:A.before,name:A.name??"drawing.svg"}}))},Z("wand",14),"Rebuild it clean by re-tracing"):null,n("div.row",null,n("button.btn.primary",{style:{flex:"1"},disabled:!P,onclick:async()=>{if(!P)return;const H=(A.name??"drawing").replace(/\.svg$/i,""),X=await Rs(`${H}.min.svg`,P.svg);X&&W(X.path?`Saved to ${X.path}`:`Downloaded ${X.name}`,{kind:"good",action:Ot(X.path)})}},"Download SVG"),n("button.btn",{disabled:!P,onclick:async()=>{P&&(await Nt(P.svg),W("SVG copied. Paste straight into Figma or Illustrator."))}},"Copy"),n("button.btn",{onclick:C},"Open…"))))};return e.on(["minify"],O),O(),S}function Ni(e,t){const s=e.state.minify,o=(r,l,c)=>n("input",{class:`inlinefield${l?" accent":""}`,type:"text",inputmode:"decimal",value:String(r),onchange:d=>{const p=Number(d.target.value);Number.isFinite(p)&&p>0&&(c(p),e.touch("minify"),t())}}),a=o(s.settings.tolerancePx,!0,r=>s.settings.tolerancePx=r),i=r=>{const l=Number(r.value)/1e3;return Number((l*l).toFixed(3))};return n("div.card",null,n("span.sentence",null,"Nothing moves more than ",a," px when the drawing is ",o(s.settings.judgePx,!1,r=>s.settings.judgePx=r)," px wide."),n("input.slider",{type:"range",min:"0",max:"1000",step:"1",value:String(Math.round(Math.sqrt(Math.min(1,s.settings.tolerancePx))*1e3)),"aria-label":"Tolerance",oninput:r=>{a.value=String(i(r.target))},onchange:r=>{s.settings.tolerancePx=i(r.target),e.touch("minify"),t()}}),n("div.stops",null,n("span",null,"exact"),n("span",null,"gentle"),n("span",null,"aggressive")))}function Be(e){const t=n("div.art.centred"),s=lo(e);return s&&(s.setAttribute("width","100%"),s.setAttribute("height","100%"),t.append(s)),t}function Ii(e){const t=(e.getAttribute("viewBox")??"").trim().split(/[\s,]+/).map(Number);return t.length===4&&t.every(Number.isFinite)&&t[2]>0&&t[3]>0?[t[0],t[1],t[2],t[3]]:null}function lo(e){const t=new DOMParser().parseFromString(e,"image/svg+xml"),s=t.documentElement;if(!s||s.nodeName!=="svg"||t.querySelector("parsererror"))return null;const o=document.importNode(s,!0);if(Ii(o))return o;const a=o.getAttribute("width")??"",i=o.getAttribute("height")??"",r=parseFloat(a),l=parseFloat(i);if(r>0&&l>0&&!a.includes("%")&&!i.includes("%"))return o.setAttribute("viewBox",`0 0 ${r} ${l}`),o;const c=document.createElement("div");c.style.cssText="position:absolute;visibility:hidden;width:0;height:0;overflow:hidden",o.setAttribute("width","1"),o.setAttribute("height","1"),c.append(o),document.body.append(c);try{const d=o.getBBox();if(d.width>0&&d.height>0){const p=Math.max(d.width,d.height)*.02;o.setAttribute("viewBox",`${d.x-p} ${d.y-p} ${d.width+2*p} ${d.height+2*p}`)}else o.setAttribute("viewBox","0 0 100 100")}catch{o.setAttribute("viewBox","0 0 100 100")}finally{c.remove()}return o}async function co(e){const t=lo(e);if(!t)return null;const s=64;t.setAttribute("width",String(s)),t.setAttribute("height",String(s));const o=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(t))}`;try{const a=await new Promise((h,f)=>{const u=new Image;u.onload=()=>h(u),u.onerror=f,u.src=o}),i=document.createElement("canvas");i.width=s,i.height=s;const r=i.getContext("2d",{willReadFrequently:!0});if(!r)return null;r.drawImage(a,0,0,s,s);const l=r.getImageData(0,0,s,s).data;let c=0,d=0;for(let h=0;h.97*s*s)return null;const p=d/c;return p<.3?"light":p>.8?"dark":null}catch{return null}}const ye=25.4,bs=[["singleColour","One colour","Everything chosen, joined into one sheet of one material."],["layered","Layered","One sheet per colour. Each runs under the colours above it by the bleed, so the stack has no gaps; registration marks line the sheets up."],["inlay","Inlay","One sheet per colour, cut exactly, laid edge to edge. Thinner, but unforgiving to line up."],["sticker","Sticker","The artwork with a contour around it, to print and then cut."],["stencil","Stencil","A sheet with the artwork cut out. Counters that would fall out, like the middle of an O, are held by thin bridges."],["lines","Lines","For a pen, a scoring blade or a laser line: every line in the drawing is followed once, along its centre, instead of round both of its edges. Strokes in the file are used as drawn; lines inside filled shapes are found and checked against the shape."]],ys=[["mm","Millimetres","Exact by the SVG standard, and read correctly by most programs. If yours imports at the wrong size, choose the pixel rate it uses."],["px96","Pixels, 96/in","The CSS pixel: Inkscape, LightBurn, Carbide Create, Fusion and browsers read a bare pixel size at 96 per inch."],["px72","Pixels, 72/in","Cricut Design Space and Silhouette Studio read a bare pixel size at 72 per inch."]],ws=[{id:"vinyl",label:"Adhesive vinyl",glyph:"layers",note:"Cricut, Silhouette, Brother. Filled shapes, stacked by colour with a 0.8 mm overlap, marks to line up, nothing narrower than 0.8 mm.",apply:e=>({mode:e>1?"layered":"singleColour",cutStyle:"filled",mirror:!1,registration:!0,bleedMm:.8,minFeatureMm:.8,kerfMm:0})},{id:"htv",label:"Iron-on",glyph:"zap",note:"Heat-transfer vinyl is cut face down, so every sheet is mirrored. Layers overlap by 0.3 mm (0.25–0.38 mm is the press-safe range) and nothing is narrower than 1 mm. Glitter, holographic and puff go on top only.",apply:e=>({mode:e>1?"layered":"singleColour",cutStyle:"filled",mirror:!0,registration:!0,bleedMm:.3,minFeatureMm:1,kerfMm:0})},{id:"sticker",label:"Sticker",glyph:"square",note:"Print the artwork, then the machine cuts the contour around it.",apply:()=>({mode:"sticker",cutStyle:"filled",mirror:!1,stickerMarginMm:3,kerfMm:0})},{id:"stencil",label:"Stencil",glyph:"copy",note:"Mylar or card with the design cut out, for paint or etching cream. Loose centres are bridged.",apply:()=>({mode:"stencil",cutStyle:"filled",mirror:!1,bridgeMm:1.5,stencilMarginMm:10,minFeatureMm:1.5,kerfMm:0})},{id:"pen",label:"Pen or score",glyph:"pen",note:"A pen in the cutter, a scoring or foil tool, a plotter, or a laser engraving lines: each line is drawn once, down its middle. Set the tip width so the preview shows what the pen will leave.",apply:()=>({mode:"lines",penMm:.4,maxLineMm:0,mirror:!1})},{id:"laser",label:"Laser or sign cutter",glyph:"crosshair",note:"Hairlines in LightBurn's layer colours, inner shapes cut first, parts grown for a 0.15 mm kerf (CO₂ 0.1–0.2 mm, diode 0.15–0.3 mm; measure yours). A DXF is saved too.",apply:()=>({mode:"inlay",cutStyle:"hairline",mirror:!1,registration:!1,kerfMm:.15,minFeatureMm:.3})}];function Oi(e){const t=n("div.viewertools"),s=n("div.pane",null),o=n("div.panes.onlight",null,s),a=n("section.stage",null,t,o),i=n("aside.minifyrail"),r=n("div",{style:{flex:"1",minHeight:"0",display:"flex"}},a,i);let l=0;const c=async()=>{const b=e.state.fab;if(!b.svg)return;const x=++l;try{const M=await B.fabPrepare(b.svg,b.options);if(x!==l)return;b.plan=M,b.error=null,b.shown>=M.layers.length&&(b.shown=-1)}catch(M){if(x!==l)return;b.plan=null,b.error=String(M)}e.touch("fab")},d=async(b,x)=>{const M=e.state.fab;M.name=b,M.svg=x,M.plan=null,M.error=null,M.shown=-1;try{const v=await B.fabAnalyze(x);M.analysis=v;const S=v.colours.filter(y=>!y.background).map(y=>y.hex);M.options.include=S,M.options.order=[...S],S.length>1&&M.options.mode==="singleColour"&&(M.options.mode="layered")}catch(v){M.analysis=null,M.error=String(v)}e.touch("fab"),await c()},p=async()=>{const[b]=await yn([{name:"SVG",extensions:["svg"]}]);if(b)try{await d(b.name,await Ct(b))}catch(x){W(String(x),{kind:"bad"})}},h=async()=>{const b=e.state;b.svg&&await d(b.source?.name?`${b.source.name.replace(/\.[^.]+$/,"")}.svg`:"trace.svg",b.svg)},f=async b=>b.name.toLowerCase().endsWith(".svg")?(await d(b.name,await Ct(b)),!0):!1;r.acceptDropped=f;const u=(b,x=!1)=>{const M=e.state.fab;Object.assign(M.options,b),x||(M.preset=null),e.touch("fab"),c()},g=()=>{const b=e.state.fab;if(Gi(e,t,s),!b.svg){F(i,qi(e,p,h));return}F(i,n("div.railscroll",{style:{gap:"16px",padding:"20px"}},Hi(e,p,h),b.error?n("div.card",null,n("span.eyebrow",null,"This file could not be prepared"),n("span.faint",null,b.error)):null,Ui(e,u),ji(e,u),Vi(e,u),Yi(e,u),Ji(e,u),Xi(e),Ki(e)),Qi(e))};return e.on(["fab"],g),e.on(["svg"],()=>{e.state.tab==="fabricate"&&g()}),g(),r}function uo(e,t,s=1){return e.state.fab.unit==="in"?`${(t/ye).toFixed(s+1)} in`:`${t.toFixed(s)} mm`}function _i(e,t){return e.state.fab.unit!=="in"?t:t.replace(/(\d+(?:\.\d+)?) mm²/g,(s,o)=>`${(Number(o)/(ye*ye)).toFixed(4)} in²`).replace(/(\d+(?:\.\d+)?) mm(?![²\w])/g,(s,o)=>`${(Number(o)/ye).toFixed(3)} in`)}function et(e,t){return e.state.fab.unit==="in"?`${(t[0]/ye).toFixed(2)} × ${(t[1]/ye).toFixed(2)} in`:`${t[0].toFixed(1)} × ${t[1].toFixed(1)} mm`}function Bi(e){return e?e.checks.filter(t=>t.code==="thin"||t.code==="gaps"||t.code==="specks").length:0}function Ri(){return n("div.drop.solid",null,n("span.glyph",null,Z("layers",32)),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.headline",null,"Drop an SVG here"),n("span.faint",{style:{fontSize:"13px"}},"or open one, or bring over a trace, from the panel on the right")))}function Dn(e,t=10){return n("span",{style:{display:"inline-block",width:`${t}px`,height:`${t}px`,borderRadius:"2px",background:e,border:"1px solid var(--rule2)",flex:"none"}})}function Gi(e,t,s){const o=e.state.fab,a=o.plan,i=Bi(a),r=h=>{o.shown=h,e.touch("fab")};F(t,n("div.seg",{role:"group","aria-label":"Sheet"},n("button",{"aria-pressed":String(o.shown===-1),disabled:!a,onclick:()=>r(-1)},"All sheets"),...(a?.layers??[]).map((h,f)=>n("button",{"aria-pressed":String(o.shown===f),title:`${h.name}: ${et(e,h.materialMm)} of material, ${h.parts} pieces, ${oe(h.nodes)} path segments`,onclick:()=>r(f)},Dn(h.hex),n("span",{style:{marginLeft:"6px"}},(a?.layers.length??0)>1?`${f+1}`:h.name)))),a&&i?n("button.btn.ghost.compact",{"aria-pressed":String(o.showProblems),style:{marginLeft:"12px",color:o.showProblems?"#e5484d":void 0},title:"Draw the parts too thin to weed (red), the waste gaps too narrow to weed (amber) and the loose specks (ringed)",onclick:()=>{o.showProblems=!o.showProblems,e.touch("fab")}},Z("alert",13),n("span",{style:{marginLeft:"6px"}},o.showProblems?"Showing problems":"Show problems")):null,n("span.muted",{style:{marginLeft:"auto",fontSize:"11.5px"}},a?`${et(e,a.sizeMm)}${o.options.mode==="lines"?"":o.options.registration&&a.layers.length>1&&(o.options.mode==="layered"||o.options.mode==="inlay")?", marks included":o.options.weedBorderMm>0?", border included":""}`:""));const l=o.options.mode==="lines",c=a?o.shown>=0?a.layers[o.shown]?.svg:l?a.combinedSvg:a.previewSvg:null,d=(h,f=!1)=>(h.style.gridArea="1 / 1",f&&(h.style.pointerEvents="none",h.style.opacity="0.85"),h),p=c?n("div",{style:{display:"grid",placeSelf:"stretch",minWidth:"0",minHeight:"0"}},l&&a&&o.shown<0?Wi(d(Be(a.previewSvg))):null,d(Be(c)),a&&i&&o.showProblems?d(Be(a.problemsSvg),!0):null):o.svg?Be(o.svg):Ri();F(s,n("span.eyebrow.panelabel",null,o.shown>=0&&a?`${l?"Pen":"Sheet"} ${o.shown+1} · ${a.layers[o.shown].name} · ${et(e,a.layers[o.shown].materialMm)}`:l?"What the pen draws, over the drawing":"All sheets, stacked"),p)}function xt(e,t){const s=e.state.fab.options.mode==="lines"?"pen drawing":"sheet";return t===1?s:`${s}s`}function Wi(e){return e.style.opacity="0.18",e.style.pointerEvents="none",e}function qi(e,t,s){const o=!!e.state.svg;return n("div.railscroll",{style:{justifyContent:"center",alignItems:"center",textAlign:"center",gap:"16px"}},n("span.serif",{style:{fontSize:"20px"}},"Make it real"),n("span.faint",{style:{fontSize:"12.5px",lineHeight:"1.6",maxWidth:"32ch"}},"Prepare a drawing for a vinyl cutter, a sticker or a laser: real sizes, one sheet per colour, and a check of what will go wrong before anything is cut."),o?n("button.btn.primary",{onclick:s},"Use the current trace"):null,n(o?"button.btn":"button.btn.primary",{onclick:t},"Open an SVG"))}function Hi(e,t,s){const o=e.state.fab;return n("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},Z("file",14),n("span.dim",{style:{flex:"1",fontSize:"12.5px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}},o.name??"drawing"),e.state.svg?n("button.btn.ghost.compact",{onclick:s,title:"Use what the Vectorize tab traced"},"Current trace"):null,n("button.btn.ghost.compact",{onclick:t},"Open…"))}function Ui(e,t){const s=e.state.fab,o=s.options.include.length,a=ws.find(i=>i.id===s.preset);return n("div.card",null,n("span.eyebrow",null,"Material"),n("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"6px"}},...ws.map(i=>n("button.btn",{"aria-pressed":String(s.preset===i.id),style:{justifyContent:"flex-start",gap:"8px",borderColor:s.preset===i.id?"var(--accent)":void 0,background:s.preset===i.id?"var(--accent-soft)":void 0},onclick:()=>{s.preset=i.id,s.dxf=i.id==="laser"||i.id==="pen",t(i.apply(o),!0)}},Z(i.glyph,14),i.label))),n("span.muted",{style:{fontSize:"11.5px",lineHeight:"1.5"}},a?a.note:"Pick what goes in the machine, or set everything below by hand."))}function ji(e,t){const s=e.state.fab.options,o=bs.find(a=>a[0]===s.mode)?.[2]??"";return n("div.card",null,n("span.eyebrow",null,"Making"),n("div.seg",{role:"group","aria-label":"What is being made",style:{display:"grid",gridTemplateColumns:"repeat(3, 1fr)"}},...bs.map(([a,i])=>n("button",{"aria-pressed":String(s.mode===a),onclick:()=>t({mode:a})},i))),n("span.muted",{style:{fontSize:"11.5px",lineHeight:"1.5"}},o))}function kt(e,t,s="58px",o=0){return n("input",{class:"inlinefield",type:"text",inputmode:"decimal",value:String(e),style:{width:s},onchange:a=>{const i=Number(a.target.value);Number.isFinite(i)&&i>=o&&t(i)}})}function he(e,t,s,o="58px"){const a=e.state.fab.unit==="in",i=Number(a?(t/ye).toFixed(3):t.toFixed(2));return kt(i,r=>s(a?r*ye:r),o)}function Fn(e){return e.state.fab.unit==="in"?"in":"mm"}function Vi(e,t){const s=e.state.fab,o=s.analysis?.aspect??1,a=s.options.widthMm,i=Fn(e);return n("div.card",null,n("div.cardhead",null,n("span.eyebrow",null,"Size"),n("div.seg",{role:"group","aria-label":"Units"},...["mm","in"].map(r=>n("button",{"aria-pressed":String(s.unit===r),onclick:()=>{s.unit=r,e.touch("fab")}},r)))),n("span.sentence",null,"The design is ",he(e,a,r=>r>0&&t({widthMm:r})),` ${i} wide and `,he(e,a*o,r=>r>0&&t({widthMm:r/o})),` ${i} tall.`),Ie("Cut a size-check square",s.options.sizeCheckMm>0,()=>t({sizeCheckMm:s.options.sizeCheckMm>0?0:s.unit==="in"?ye:20}),s.options.sizeCheckMm>0?`A ${uo(e,s.options.sizeCheckMm,1)} square below the design. Measure it once cut: any other size means the program rescaled the file on import.`:"Wrong size on import is the commonest cutter complaint. A small square below the design shows it before a whole sheet is wasted."),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.dim",{style:{fontSize:"12.5px"}},"The files state their size in"),n("div.seg",{role:"group","aria-label":"Size written in",style:{display:"grid",gridTemplateColumns:"repeat(3, 1fr)"}},...ys.map(([r,l])=>n("button",{"aria-pressed":String(s.options.fileUnits===r),onclick:()=>t({fileUnits:r})},l))),n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},ys.find(r=>r[0]===s.options.fileUnits)?.[2]??"")))}function Yi(e,t){const s=e.state.fab,o=s.analysis;if(!o)return null;const a=s.options,i=a.mode==="layered",r=i?[...a.order.filter(d=>a.include.includes(d)),...o.colours.map(d=>d.hex).filter(d=>!a.include.includes(d))]:o.colours.map(d=>d.hex),l=d=>o.colours.find(p=>p.hex===d),c=(d,p)=>{const h=a.order.filter(g=>a.include.includes(g)),f=h.indexOf(d),u=f+p;f<0||u<0||u>=h.length||([h[f],h[u]]=[h[u],h[f]],t({order:h}))};return n("div.card",null,n("div.cardhead",null,n("span.eyebrow",null,i?"Colours, bottom sheet first":"Colours"),n("span.muted",{style:{fontSize:"11px"}},`${a.include.length} of ${o.colours.length}`)),...r.map(d=>{const p=l(d);if(!p)return null;const h=a.include.includes(d),f=[p.background?"page":null,p.gradient?"gradient":null,p.translucent?"translucent":null].filter(Boolean);return n("div",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"12.5px",opacity:h?"1":"0.55"}},n("button.switch",{role:"switch","aria-checked":String(h),"aria-label":`Cut ${d}`,onclick:()=>{const u=h?a.include.filter(b=>b!==d):[...a.include,d],g=h?a.order:[...a.order.filter(b=>b!==d),d];t({include:u,order:g})}}),Dn(d,16),n("span.num",{style:{flex:"1"}},d,f.length?n("span.muted",{style:{marginLeft:"6px",fontSize:"11px"}},f.join(" · ")):null),n("span.muted.num",{style:{fontSize:"11px"}},$e(p.coverage,1)),i&&h?n("span",{style:{display:"flex",gap:"2px"}},n("button.btn.ghost.compact",{title:"Lower in the stack",onclick:()=>c(d,-1)},"↓"),n("button.btn.ghost.compact",{title:"Higher in the stack",onclick:()=>c(d,1)},"↑")):null)}))}function Ie(e,t,s,o){return n("div",{style:{display:"flex",flexDirection:"column",gap:"3px"}},n("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},n("span.dim",{style:{fontSize:"12.5px"}},e),n("button.switch",{role:"switch","aria-checked":String(t),"aria-label":e,onclick:s})),o?n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},o):null)}function Zi(e,t){const s=e.state.fab.options,o=Fn(e);return n("div.card",null,n("span.eyebrow",null,"Drawing"),n("span.sentence",null,"The pen or tool draws ",he(e,s.penMm,a=>a>0&&t({penMm:a})),` ${o} wide.`),n("span.sentence",null,"Lines up to ",he(e,s.maxLineMm,a=>t({maxLineMm:a})),` ${o} wide are drawn once (0 for any width); wider parts are drawn round their outline.`),Ie("Also save a DXF",e.state.fab.dxf,()=>{e.state.fab.dxf=!e.state.fab.dxf,e.touch("fab")},"Lines as open polylines and outlines as closed ones, one layer per colour, in millimetres."),po(e,t),n("span.sentence",null,"Lines stay within ",he(e,s.toleranceMm,a=>a>0&&t({toleranceMm:a})),` ${o} of the drawing.`))}function po(e,t){const s=e.state.fab,o=s.options,a=s.unit==="in"?"in/min":"mm/min",i=s.unit==="in"?Number((o.gcodeFeedMmMin/ye).toFixed(1)):Math.round(o.gcodeFeedMmMin);return n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"}},Ie("Also save G-code",s.gcode,()=>{s.gcode=!s.gcode,e.touch("fab")},"For GRBL lasers and plotters: curves as G2/G3 arcs, holes cut first, origin at the lower left of the job. GRBL's laser mode ($32=1) keeps the beam off between cuts."),s.gcode?n("span.sentence",null,"Cut at ",kt(i,r=>r>0&&t({gcodeFeedMmMin:s.unit==="in"?r*ye:r}),"64px"),` ${a}, power S`,kt(o.gcodePower,r=>t({gcodePower:r}),"58px"),", ",kt(o.gcodePasses,r=>r>=1&&t({gcodePasses:Math.round(r)}),"40px",1),o.gcodePasses===1?" pass.":" passes."):null)}function Ji(e,t){const s=e.state.fab.options;if(s.mode==="lines")return Zi(e,t);const o=Fn(e),a=s.mode==="layered"||s.mode==="inlay";return n("div.card",null,n("span.eyebrow",null,"Cutting"),s.mode==="layered"?n("span.sentence",null,"Each colour runs ",he(e,s.bleedMm,i=>t({bleedMm:i})),` ${o} under the colours above it.`):null,s.mode==="sticker"?n("span.sentence",null,"The contour is ",he(e,s.stickerMarginMm,i=>t({stickerMarginMm:i})),` ${o} outside the artwork.`):null,s.mode==="stencil"?n("span.sentence",null,"Bridges are ",he(e,s.bridgeMm,i=>i>0&&t({bridgeMm:i})),` ${o} wide; the sheet reaches `,he(e,s.stencilMarginMm,i=>t({stencilMarginMm:i})),` ${o} past the artwork.`):null,n("span.sentence",null,"Parts narrower than ",he(e,s.minFeatureMm,i=>i>0&&t({minFeatureMm:i})),` ${o} are too thin to weed.`),Ie("Remove them",s.removeThin,()=>t({removeThin:!s.removeThin}),"Drops the slivers and necks narrower than that from the sheets."),a?Ie("Registration marks",s.registration,()=>t({registration:!s.registration})):null,n("span.sentence",null,"Weeding border ",he(e,s.weedBorderMm,i=>t({weedBorderMm:i})),` ${o} outside (0 for none).`),Ie("Also save a DXF",e.state.fab.dxf,()=>{e.state.fab.dxf=!e.state.fab.dxf,e.touch("fab")},"Every sheet as a layer of closed polylines in millimetres, curves as true arcs (which CAM turns into G2/G3 moves), for CAD, CNC and laser programs."),po(e,t),s.mode==="sticker"||s.mode==="stencil"?null:Ie("Mirror",s.mirror,()=>t({mirror:!s.mirror}),"For heat-transfer vinyl, which is cut from the back."),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.dim",{style:{fontSize:"12.5px"}},"Cut lines as"),n("div.seg",{role:"group","aria-label":"Cut lines as"},n("button",{"aria-pressed":String(s.cutStyle==="filled"),onclick:()=>t({cutStyle:"filled"})},"Filled shapes"),n("button",{"aria-pressed":String(s.cutStyle==="hairline"),onclick:()=>t({cutStyle:"hairline"})},"Hairlines")),n("span.muted",{style:{fontSize:"11px",lineHeight:"1.45"}},s.cutStyle==="filled"?"What Cricut Design Space and Silhouette Studio expect: they cut around every shape.":"The laser and sign-cutter convention: only a hairline is a cut. Each sheet keeps its colour, so a laser program can give it its own operation.")),s.cutStyle==="hairline"?n("span.sentence",null,"The cut removes ",he(e,s.kerfMm,i=>t({kerfMm:i})),` ${o} (kerf); parts are grown by half of it so they come out the drawn size.`):null,n("span.sentence",null,"Router bit ",he(e,2*s.dogboneMm,i=>t({dogboneMm:i/2})),` ${o} across (0 for none): every inside corner gets a dogbone so parts seat square.`),n("span.sentence",null,"Cuts stay within ",he(e,s.toleranceMm,i=>i>0&&t({toleranceMm:i})),` ${o} of the drawing.`))}function Xi(e){const t=e.state.fab.plan;if(!t)return null;const s=t.layers.reduce((i,r)=>i+r.nodes,0),o=t.layers.reduce((i,r)=>i+r.parts,0),a=t.checks.some(i=>i.level==="error")?"error":t.checks.some(i=>i.level==="warn")?"warn":"ok";return n("div.card",{style:a==="ok"?{}:{borderColor:a==="error"?"#e5484d":"var(--accent)"}},n("div.cardhead",null,n("span.eyebrow",null,"Before you cut"),n("span.muted",{style:{fontSize:"11px"}},a==="ok"?"ready":`${t.checks.length} finding${t.checks.length===1?"":"s"}`)),n("div.stats",null,...[[xt(e,2),String(t.layers.length)],["pieces",oe(o)],["path segments",oe(s)],["whole job",et(e,t.sizeMm)]].map(([i,r])=>n("div",null,n("span.k",null,i),n("span.v",null,r)))),t.checks.length?n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"}},...t.checks.map(i=>n("div",{style:{display:"flex",gap:"8px",alignItems:"flex-start",fontSize:"12px",lineHeight:"1.5"}},n("span",{style:{color:i.level==="error"?"#e5484d":i.level==="warn"?"var(--accent-text)":"var(--muted)",flex:"none",paddingTop:"2px"}},Z(i.level==="info"?"info":"alert",14)),n("span.faint",null,_i(e,i.message))))):n("div",{style:{display:"flex",gap:"8px",alignItems:"center",fontSize:"12.5px"}},Z("check",14),n("span.dim",null,"Nothing here will trouble the cutter.")))}function Ki(e){const t=e.state.fab.plan;if(!t||!t.layers.length)return null;const s=e.state.fab;return n("div.card",null,n("span.eyebrow",null,e.state.fab.options.mode==="lines"?"Pens, one per colour":"Cut list"),...t.layers.map((o,a)=>n("button",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"12.5px",background:"none",border:"none",padding:"2px 0",color:"inherit",cursor:"pointer",textAlign:"left",fontWeight:s.shown===a?"600":"400"},title:"Show this sheet",onclick:()=>{s.shown=a,e.touch("fab")}},n("span.muted.num",{style:{width:"16px"}},String(a+1)),Dn(o.hex,14),n("span.num",{style:{flex:"1"}},o.name),n("span.dim.num",null,et(e,o.materialMm)),n("span.muted.num",{style:{width:"54px",textAlign:"right",fontSize:"11px"}},`${o.parts} pc`))))}function Qi(e){const t=e.state.fab,s=t.plan,o=(t.name??"drawing").replace(/\.svg$/i,""),a=(i,r)=>{const l=i.layers[r].name.replace(/[^A-Za-z0-9_-]+/g,"");return i.layers.length>1?`${o}-${r+1}-${l}.svg`:`${o}-cut.svg`};return n("div.railfoot",null,n("div.row",null,n("button.btn.primary",{style:{flex:"1"},disabled:!s||s.layers.length===0,onclick:async()=>{if(!s)return;const i=s.layers.map((d,p)=>({name:a(s,p),data:d.svg}));s.layers.length>1&&i.push({name:`${o}-all.svg`,data:s.combinedSvg}),t.dxf&&i.push({name:`${o}.dxf`,data:s.dxf}),t.gcode&&i.push({name:`${o}.gcode`,data:s.gcode});const r=await xa(i,`${o}-sheets.zip`);if(!r)return;const l=s.layers.length>1,c=`${s.layers.length} ${xt(e,s.layers.length)}${l?" and one combined file":""}`;W(r.folder?`Saved ${c} to ${r.folder}`:`Downloaded ${c}${i.length>1?" as one .zip":""}`,{kind:"good",action:Ot(r.first)})}},`Download ${s&&s.layers.length>1?`${s.layers.length} ${xt(e,2)}`:`the ${xt(e,1)}`}`),n("button.btn",{disabled:!s,title:"Copies the shown sheet, or every sheet in one file",onclick:async()=>{s&&(await Nt(t.shown>=0?s.layers[t.shown].svg:s.combinedSvg),W("Copied. Paste it into Design Space, Silhouette Studio or LightBurn."))}},"Copy")),n("div.note",null,n("span",null,s?`Real size: ${uo(e,s.sizeMm[0])} wide`:"Real sizes"),n("span",null,"Nothing leaves this computer")))}const mt={inkvec:"Inkvec","vtracer-1.0-simplify":"VTracer 1.0, best flags","trazor-auto":"Trazor","vtracer-1.0-default":"VTracer 1.0","vtracer-default":"VTracer 0.6"},ho=e=>fetch(new URL(e,document.baseURI)).then(t=>{if(!t.ok)throw new Error(`${e}: HTTP ${t.status}`);return t.json()});let vs=null;function er(){return vs??=ho("../showcase.json"),vs}function tr(e){return ho(`../showcase/${e}.json`)}function nr(e,t){const s=n("div.screenbody",null,n("div.sc-loading.faint",null,"Loading the showcase…"));return er().then(o=>F(s,sr(o)),o=>F(s,n("div.sc-loading.faint",null,`The showcase could not be loaded: ${String(o)}`))),n("div.screen",null,n("div.screenbar",{"data-tauri-drag-region":""},n("span",{style:{fontSize:"12.5px",fontWeight:"600"}},"Showcase"),n("div.spacer"),n("button.btn.compact",{onclick:t},"Done"),n("div.sep"),lt()),s)}function sr(e){let t=e.sets[0].cases[0],s="vtracer-1.0-simplify",o="fill",a=0;const i=or(),r=n("div.sc-chips"),l=n("p.sc-caption"),c=n("div.sc-list"),d=new Map,p=e.gallery_engines.filter(y=>y!=="inkvec"),h=(y,$,C)=>{const D=n("div.seg"),z=()=>{for(const O of D.querySelectorAll("button"))O.setAttribute("aria-pressed",String(O.dataset.k===$()))};for(const[O,A]of y)D.append(n("button",{"data-k":O,onclick:()=>{C(O),z()}},A));return z(),D},f=async()=>{const y=t,$=++a;for(const[A,P]of d)P.setAttribute("aria-pressed",String(A===y.key));const C=y.what.includes("brand")||y.key.startsWith("brand");F(l,n("b",null,y.label),` · ${y.what}`,C?n("span.faint",{style:{fontSize:"11px",marginLeft:"6px"},title:"Nominative benchmark demonstration. All trademarks belong to their respective owners."},"(nominative benchmark)"):null);const D=y.m.inkvec,z=y.m[s];F(r,n("span.sc-legend.faint",null,n("i.sc-dot.a"),"anchor",n("i.sc-dot.hd"),"control point"),Ke(`Inkvec dE00 ${D.de1024.toFixed(3)}`,!0),Ke(`${mt[s]} dE00 ${z.de1024.toFixed(3)}`),Ke(`Inkvec ${D.paths} paths · ${D.coordinates} coordinates · ${xs(D.bytes)}`,!0),Ke(`${mt[s]} ${z.paths} paths · ${z.coordinates} coordinates · ${xs(z.bytes)}`),Ke(`The artist's file: ${y.artist.paths} paths · ${y.artist.coordinates} coordinates`));const O=await tr(y.key);$===a&&(i.set(O.input,O.svg.inkvec,O.svg[s],"Inkvec",mt[s]??s),i.layer(o))},u=[];new ResizeObserver(()=>{const y=i.el.getBoundingClientRect().height;y>200&&(c.style.height=`${y}px`)}).observe(i.el);const g=(y,$)=>{t=y,f();const C=d.get(y.key);if(!C)return;const D=c.getBoundingClientRect(),z=C.getBoundingClientRect(),O=34;z.topD.bottom&&(c.scrollTop+=z.bottom-D.bottom+6),$&&C.focus({preventScroll:!0})};c.addEventListener("keydown",y=>{const $=u.findIndex(z=>z.key===t.key),D={ArrowDown:3,ArrowUp:-3,ArrowRight:1,ArrowLeft:-1,Home:-$,End:u.length-1-$}[y.key];D!==void 0&&(y.preventDefault(),g(u[Math.min(u.length-1,Math.max(0,$+D))],!0))});for(const y of e.sets)c.append(n("div.sc-sethead",null,y.label,n("span",null,String(y.cases.length)))),c.append(n("div.sc-tiles",null,...y.cases.map($=>{const C=n("button.sc-case",{onclick:()=>g($,!1),title:`${$.label} · ${$.what}`},n("span.sc-thumb",null,n("img",{src:$.thumb,alt:"",loading:"lazy"})),n("span.sc-name",null,$.label));return d.set($.key,C),u.push($),C})));const b=n("div.sc-tools",null,n("span.eyebrow",null,"Compare with"),h(p.map(y=>[y,mt[y]??y]),()=>s,y=>{s=y,f()}),n("span.eyebrow",null,"Show"),h([["fill","Fill"],["wire","Wireframe"],["anchor","Anchors"],["handle","Handles"]],()=>o,y=>{o=y,i.layer(o)}),n("span.eyebrow",null,"Zoom"),...[1,4,12].map(y=>n("button.btn.compact.ghost",{onclick:()=>i.zoom(y)},`${y}×`)));f();const x=e.engines.find(y=>y.id==="inkvec"),M=e.best_de_wins.inkvec??0,v=e.brands.best_de_wins.inkvec??0,S=e.corpus.best_de_wins.inkvec??0;return n("div.sc-page",null,n("div.sc-head",null,n("h1.serif",null,"Before and after, with the geometry showing"),n("p.faint",null,`Real brand logos, the benchmark's cases and more icons and emoji, all in the list on the left, each traced by Inkvec (build ${e.inkvec.git}, ${e.traced}) and by the other engines. Left, the raster; middle, Inkvec's SVG; right, the engine you compare with. Scroll to zoom and drag to pan, all three together; Anchors shows every on-curve point, Handles every control point with its tangent.`)),n("div.sc-main",null,c,n("div.sc-stagecol",null,b,i.el,l,r)),n("div.sc-results",null,n("h2.serif",null,"Inkvec vs the field"),n("p.faint",null,`${e.cases} benchmark cases (real icons, emoji, synthetic probes and real brand logos; selection fixed from the seed ${e.seed}), each rendered from its source SVG at ${e.size} px, traced by every engine from that raster and scored against the render: CIEDE2000 colour difference, DISTS and DINOv3 perceptual similarity, and geometry against the artist's own file (1.00× is exactly the artist's number of parameters). Inkvec had the lowest colour difference on ${M} of ${e.cases}; in the other sets, on ${v} of ${e.brands.cases} brand logos and ${S} of ${e.corpus.cases} more icons and emoji.`),x?n("div.sc-bignums",null,bt("median colour difference",x.de_median.toFixed(3),"dE00; under 1 is invisible"),bt("geometry vs the artist",`${x.geometry_mean.toFixed(2)}×`,`mean; median ${x.geometry_median.toFixed(2)}×`),bt("DINOv3 similarity",x.dino.toFixed(4),"1 is identical"),bt("seconds per case",x.seconds_median.toFixed(2),"median, native command line")):null,Qt(`${e.cases} benchmark cases, re-run at ${e.size} px`,e.engines),Qt(`${e.brands.cases} brand logos`,e.brands.engines),Qt(`${e.corpus.cases} more icons and emoji`,e.corpus.engines),n("p.faint.sc-note",null,`Means over the cases except seconds (median). Every case is rendered at ${e.size} px on white, traced by every engine from that raster and scored at 1024 px: the pictures and the tables are the same traces. Inkvec was traced by the build named above; the other engines have not changed. The benchmark of record is the ${e.date} run at 512 px (out/${e.run}/results.json, bench/crosscompare_competitors.py); these tables re-run its cases at ${e.size} px, where a logo's fine detail survives. The brand logos beyond the first twenty, and the third set, were picked by the seed ${e.pick_seed} alone (hash order, a quota per kind), not by how any engine traces them. Computed by tools/showcase_data.py. `,n("a",{href:"#",onclick:y=>{y.preventDefault(),It("https://github.com/logolabs/inkvec#results")}},"Method and every case")),n("p.faint.sc-note",{style:{marginTop:"10px",borderTop:"1px solid var(--rule2)",paddingTop:"10px",lineHeight:"1.6"}},n("b",{style:{color:"var(--dim)"}},"Trademarks: "),"the logos and brand names shown here are the property of their owners, and appear only to compare how engines trace real artwork. This implies no affiliation with, sponsorship by or endorsement from them.")))}function Qt(e,t){return n("table.sc-table",null,n("caption",null,e),n("thead",null,n("tr",null,...["Engine","mean dE00","median dE00","DISTS","DINO","geometry","coordinates","seconds"].map(s=>n("th",null,s)))),n("tbody",null,...t.map(s=>n(`tr${s.id==="inkvec"?".win":""}`,null,n("td",null,s.name),n("td.num",null,s.de_mean.toFixed(3)),n("td.num",null,s.de_median.toFixed(3)),n("td.num",null,s.dists.toFixed(4)),n("td.num",null,s.dino.toFixed(4)),n("td.num",null,`${s.geometry_mean.toFixed(2)}×`),n("td.num",null,Math.round(s.coordinates).toString()),n("td.num",null,s.seconds_median.toFixed(2))))))}const xs=e=>`${(e/1024).toFixed(1)} KB`,Ke=(e,t=!1)=>n(`span.sc-chip${t?".win":""}`,null,e),bt=(e,t,s)=>n("div.sc-bignum",null,n("span.eyebrow",null,e),n("span.v.serif",null,t),n("span.faint",null,s));function or(){const e=[0,1,2].map(()=>{const r=n("div.sc-cam"),l=n("span.sc-tag");return{pane:n("div.sc-pane",null,r,l),cam:r,tag:l}}),t=n("div.sc-viewer",null,...e.map(r=>r.pane)),s={z:1,x:0,y:0};let o=[];const a=()=>{for(const r of e)r.cam.style.transform=`translate(${s.x}px, ${s.y}px)`,r.cam.style.width=r.cam.style.height=`${s.z*100}%`};let i=null;for(const{pane:r}of e){r.addEventListener("wheel",c=>{c.preventDefault();const d=r.getBoundingClientRect(),p=c.clientX-d.left,h=c.clientY-d.top,f=Math.min(40,Math.max(1,s.z*Math.exp(-c.deltaY*.0015))),u=f/s.z;s.x=p-(p-s.x)*u,s.y=h-(h-s.y)*u,s.z=f,a()},{passive:!1}),r.addEventListener("pointerdown",c=>{i=[c.clientX-s.x,c.clientY-s.y],r.setPointerCapture(c.pointerId)}),r.addEventListener("pointermove",c=>{i&&(s.x=c.clientX-i[0],s.y=c.clientY-i[1],a())});const l=()=>i=null;r.addEventListener("pointerup",l),r.addEventListener("pointercancel",l),r.addEventListener("dblclick",()=>{s.z=1,s.x=s.y=0,a()})}return{el:t,set(r,l,c,d,p){F(e[0].cam,n("img.sc-raster",{src:r,alt:"",draggable:"false"})),o=[$s(l),$s(c)],F(e[1].cam,o[0].root),F(e[2].cam,o[1].root),e[0].tag.textContent="Raster",e[1].tag.textContent=`${d} · ${o[0].anchors} anchors`,e[2].tag.textContent=`${p} · ${o[1].anchors} anchors`,a()},layer(r){for(const l of o)l.set(r)},zoom(r){const l=e[0].pane.getBoundingClientRect();s.z=r,s.x=(l.width-l.width*r)/2,s.y=(l.height-l.height*r)/2,a()}}}const ks="http://www.w3.org/2000/svg";function Ss(e){const s=new DOMParser().parseFromString(e,"image/svg+xml").documentElement;if(!(s instanceof SVGSVGElement))return null;if(!s.getAttribute("viewBox")){const o=parseFloat(s.getAttribute("width")??""),a=parseFloat(s.getAttribute("height")??"");o>0&&a>0&&s.setAttribute("viewBox",`0 0 ${o} ${a}`)}return s.removeAttribute("width"),s.removeAttribute("height"),s.setAttribute("preserveAspectRatio","xMidYMid meet"),document.importNode(s,!0)}function $s(e){const t=Ss(e),s=Ss(e),o=n("div.sc-layer");if(!t||!s)return{root:o,anchors:0,set:()=>{}};s.classList.add("sc-wire");const a=ar(t);o.append(t,s,a.svg);const i=r=>{const l=r==="anchor"||r==="handle";t.style.display=r==="wire"?"none":"",t.style.opacity=l?"0.35":"1",s.style.display=r==="fill"?"none":"",a.anchors.style.display=l?"":"none",a.handles.style.display=a.handleDots.style.display=r==="handle"?"":"none"};return i("fill"),{root:o,anchors:a.count,set:i}}function ar(e){const t=document.createElementNS(ks,"svg");t.setAttribute("viewBox",e.getAttribute("viewBox")??"0 0 100 100"),t.setAttribute("preserveAspectRatio","xMidYMid meet"),t.setAttribute("class","sc-ov");let s="",o="",a="",i=0;const r=h=>h.toFixed(2);for(const h of e.querySelectorAll("path")){const[f,u]=Ms(h),g=ir(h.getAttribute("d")??"",f,u);i+=g.anchors.length;for(const[b,x]of g.anchors)s+=`M${r(b)} ${r(x)}h0.001`;for(const[[b,x],[M,v]]of g.handles)o+=`M${r(b)} ${r(x)}L${r(M)} ${r(v)}`,a+=`M${r(M)} ${r(v)}h0.001`}for(const h of e.querySelectorAll("circle, ellipse")){const[f,u]=Ms(h);s+=`M${r(Number(h.getAttribute("cx")??0)+f)} ${r(Number(h.getAttribute("cy")??0)+u)}h0.001`,i++}const l=(h,f)=>{const u=document.createElementNS(ks,"path");return u.setAttribute("d",h||"M0 0"),u.setAttribute("class",f),t.append(u),u},c=l(o,"h"),d=l(a,"hd"),p=l(s,"a");return{svg:t,anchors:p,handles:c,handleDots:d,count:i}}function Ms(e){let t=0,s=0;for(let o=e;o&&o.tagName.toLowerCase()!=="svg";o=o.parentElement){const a=(o.getAttribute("transform")??"").match(/translate\(\s*(-?[\d.e+-]+)[ ,]*(-?[\d.e+-]+)?\s*\)/i);a&&(t+=parseFloat(a[1]),s+=parseFloat(a[2]??"0"))}return[t,s]}function ir(e,t,s){const o=e.match(/[MLHVCSQTAZmlhvcsqtaz]|-?\d*\.?\d+(?:e[-+]?\d+)?/gi)??[],a=[],i=[];let r=0,l=0,c=0,d=0,p=0,h="",f=null,u=0,g=0;const b=()=>parseFloat(o[r++]),x=(v,S)=>a.push([v+t,S+s]),M=(v,S,y,$)=>i.push([[v+t,S+s],[y+t,$+s]]);for(;r{const a=e.state;if(!a.screen){F(s),s.style.display="none";return}s.style.display="",F(s,a.screen==="settings"?lr(e,t):a.screen==="showcase"?nr(e,t.close):ur(e,t))};return e.on(["screen","prefs","caps"],o),o(),s}function lr(e,t){const s=e.state.prefs,o=e.state.caps?.denoiser;return n("div.screen",null,n("div.screenbar",{"data-tauri-drag-region":""},n("span",{style:{fontSize:"12.5px",fontWeight:"600"}},"Settings"),n("div.spacer"),n("button.btn.compact",{onclick:t.close},"Done"),n("div.sep"),lt()),n("div.screenbody",null,n("div.settingsgrid",null,yt("General",[null,ge("Theme","The light theme matches the dark one token for token.",en([["system","System"],["dark","Dark"],["light","Light"]],s?.theme??"system",a=>t.applyPrefs({theme:a}))),ge("When an image is opened","Auto always starts tracing at once. Ask shows a small card offering the Custom wizard beside it; Custom opens the wizard straight away.",en([["ask","Ask"],["auto","Auto only"],["custom","Custom wizard"]],s?.onOpen??"ask",a=>t.applyPrefs({onOpen:a}))),ge("Language","English only in this release. Nothing in the app is machine-translated, which is why there is one.",en([["en","English"]],"en",()=>{}))]),yt("Performance",[ge("Threads",crossOriginIsolated?"The engine runs on a pool of workers, one per core the browser reports.":"This page is not cross-origin isolated, so the engine runs on one core. Open it in its own tab for all of them.",n("span.muted.num",{style:{fontSize:"12.5px"}},crossOriginIsolated?`${navigator.hardwareConcurrency||4}`:"1")),ge("Draft resolution","Used while you move a control.",tn(s?.draftPx??512,128,2048,a=>t.applyPrefs({draftPx:a}),"px")),ge("Draft time limit","A draft that overruns is abandoned, not shown late.",tn(s?.draftSeconds??.4,.05,10,a=>t.applyPrefs({draftSeconds:a}),"s",2)),ge("Settle before the full trace","How long the controls stay still before the full-resolution trace is queued.",tn(s?.settleMs??800,100,5e3,a=>t.applyPrefs({settleMs:a}),"ms"))]),yt("Denoiser",cr(e,o)),null,yt("Advanced",[null,null,ge("Reset settings","Puts every preference and the trace controls back to their defaults. Your saved presets and your files are untouched.",n("button.btn.compact.danger",{onclick:()=>Ta("Reset settings?","Every preference goes back to its default, and the recent list is cleared. Your saved presets are kept, and nothing you have traced or exported is affected.","Reset",async()=>{const a=await B.resetPrefs();e.set({prefs:a,settings:a.trace}),t.afterReset(a),W("Settings reset.")},!0)},"Reset"))]),n("div.privacycard",null,n("span.eyebrow",null,"Privacy"),n("span.line",null,"Your image never leaves this browser."),n("p",{style:{margin:"0",fontSize:"12.5px",lineHeight:"1.65",color:"var(--faint)"}},"Tracing runs in this tab, on your own processor, as WebAssembly. There is no account, no upload and no telemetry. The page fetches only itself and the denoiser (its weights from Hugging Face and ONNX Runtime Web from jsDelivr, once, in the background, and not at all while the browser asks to save data until you turn it on); your preferences are kept in this browser's storage.")))))}function cr(e,t){return t?t.supported?[ge("Status","Optional. Improves traces from photographs and screenshots.",n("span",{style:{fontSize:"12.5px",color:t.installed?"var(--good)":"var(--muted)"}},t.installed?`Installed · ${ie(t.bytes??0)}`:e.state.denoiserFetch?.phase==="downloading"?"Downloading in the background":"Not installed")),ge(t.installed?"Remove":"Download",t.installed?t.path??"Kept in this browser's storage.":e.state.denoiserFetch?.saveData?"Not downloaded in the background, because this browser asks to save data. Turning the denoiser on downloads it (about 100 MB), once, into this browser's storage. The model comes down, your image never goes up.":"Downloaded in the background when the Studio opens, once, into this browser's storage (about 100 MB), and run in this tab on your GPU where it has one. The model comes down, your image never goes up.",t.installed?n("button.btn.compact.danger",{onclick:async()=>{const s=await B.denoiserRemove(),o=e.state.caps;o&&e.set({caps:{...o,denoiser:s}}),W("Denoiser removed.")}},"Remove"):n("button.btn.compact",{onclick:()=>Nn(e)},"Download")),ge("Verified against",t.repo,n("span.mono.muted",{style:{fontSize:"11px"}},`${t.sha256.slice(0,16)}…`))]:[ge("Status","The denoiser needs a cross-origin isolated page, which this one is not. Open the app in its own tab to use it.",n("span.muted",{style:{fontSize:"12.5px"}},"Not in this build"))]:[ge("Status","",n("span.muted",null,"—"))]}function Nn(e){const t=e.state.caps?.denoiser;if(!t)return;const s=n("div.progress",null,n("i",{style:{width:"0%"}})),o=n("span.faint.num",{style:{fontSize:"12.5px"}},""),a=n("div.modal",{tabindex:"-1"});let i=!1;const r=()=>{F(a,n("h2",null,i?"Downloading the denoiser":"Download the denoiser"),i?n("div",{style:{display:"flex",flexDirection:"column",gap:"10px"}},s,o):n("p",null,"Downloaded once (about 76 MB of weights and 26 MB of ONNX Runtime Web) and kept in this browser; the Studio starts it in the background when it opens, unless the browser asks to save data. It repairs JPEG and screenshot damage before tracing, which usually halves the colour error on photographed logos. It runs in this tab, on your GPU where there is one — the model comes down, your image never goes up."),n("div.stats",null,n("div",null,n("span.k",null,"from"),n("span.v",{style:{fontSize:"12px"}},t.repo)),n("div",null,n("span.k",null,"adds per trace"),n("span.v",null,"seconds"))),n("span.muted",{style:{fontSize:"11.5px",lineHeight:"1.5"}},`SHA-256 verified on arrival against ${t.sha256.slice(0,16)}… · removable in Settings.`),n("div.actions",null,n("button.btn",{onclick:()=>{i&&B.denoiserCancel(),ue()}},i?"Cancel download":"Not now"),i?null:n("button.btn.primary",{onclick:()=>{i=!0,r(),B.denoiserDownload()}},"Download")))};(async()=>{const l=await ke.denoiserProgress(({got:d,total:p})=>{const h=p?d/p*100:0;s.firstElementChild.style.width=`${h.toFixed(1)}%`,o.textContent=p?`${ie(d)} of ${ie(p)} · you can keep tracing`:`${ie(d)} · you can keep tracing`}),c=await ke.denoiserDone(({ok:d,message:p,status:h})=>{const f=e.state.caps;f&&e.set({caps:{...f,denoiser:h}}),l(),c(),ue(),d?W("Denoiser installed and verified.",{kind:"good"}):p!=="cancelled"&&W(p??"The download did not finish.",{kind:"bad"})})})(),Ye(a),r()}function ur(e,t){const s=e.state.caps,o=n("pre",null,"Loading the notices…");B.thirdPartyNotices().then(i=>{o.textContent=i}).catch(()=>{o.textContent="The bundled notices could not be read. They are also published at github.com/logolabs/inkvec in docs/THIRD_PARTY.md."});const a=(i,r)=>n("a",{href:"#",onclick:l=>{l.preventDefault(),It(r)}},i);return n("div.screen",null,n("div.screenbar",{"data-tauri-drag-region":""},n("span",{style:{fontSize:"12.5px",fontWeight:"600"}},"About"),n("div.spacer"),n("button.btn.compact",{onclick:t.close},"Done"),n("div.sep"),lt()),n("div.screenbody",null,n("div.about",null,n("div.aboutleft",null,n("span.mark",null,Ds(72)),n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"}},n("span.serif",{style:{fontSize:"40px",lineHeight:"1"}},Ft),n("span.faint.num",{style:{fontSize:"13px"}},`Version ${s?.version??"—"} · engine Inkvec ${s?.engineVersion??"—"} · Apache-2.0`),n("span.muted.mono",{style:{fontSize:"11px"}},`build target ${s?.buildTarget??"—"}`)),n("p",{style:{margin:"0",fontSize:"13.5px",lineHeight:"1.7",color:"var(--dim)",maxWidth:"380px"}},"Made by LogoLabs, an AI lab working on brand assets and logo creation. Tracing reconstructs a picture; drawing one is a different job, and that is the one we do."),n("p",{style:{margin:"0",fontSize:"13px",lineHeight:"1.7",color:"var(--faint)",maxWidth:"380px"}},"This is the browser edition. The desktop app, Inkvec Studio, is the same interface with folders of images traced in one go, the command line, the right-click menu and traces up to 16384 px."),n("button.btn.compact",{style:{alignSelf:"flex-start"},onclick:()=>e.set({screen:"showcase"})},"Before and after on real logos: the showcase"),n("div",{style:{display:"flex",gap:"18px",fontSize:"13px",flexWrap:"wrap"}},n("a",{href:"#",onclick:i=>{i.preventDefault(),Pn()}},"User guide"),a("GitHub","https://github.com/logolabs/inkvec"),a("Benchmark results","https://github.com/logolabs/inkvec#benchmark"),a("Inkvec Studio for the desktop","https://github.com/logolabs/inkvec/releases"),a("logolabs.org","https://logolabs.org"))),n("div.aboutright",null,n("div.credit",null,n("span.eyebrow",{style:{color:"var(--gold)"}},"Compute acknowledgement"),n("p",{style:{margin:"0",fontSize:"13px",lineHeight:"1.7",color:"var(--dim)"}},"The denoiser was trained on resources provided by EuroHPC JU and NAISS. Training ran on the Arrhenius GPU supercomputer at NAISS, Sweden, under EuroHPC project EHPC-AIF-2026PG01-907."),n("div",{style:{display:"flex",gap:"10px",marginTop:"4px"}},n("span.btn.compact",{style:{cursor:"default"}},"EuroHPC JU"),n("span.btn.compact",{style:{cursor:"default"}},"NAISS"))),n("div.notices",null,n("div.head",null,n("span",null,"Licence and third-party notices"),n("button.reset",{onclick:()=>dr(o.textContent??"")},"Open")),o),n("span.muted",{style:{fontSize:"12px"}},"Your image never leaves this browser."),n("p.muted",{style:{margin:"0",fontSize:"11px",lineHeight:"1.5",color:"var(--faint)"}},"Trademarks, brand marks, and logos depicted in showcase comparisons are property of their respective owners, used solely for nominative, educational benchmarking demonstration.")))))}function dr(e){Ye(Rt("Licence and third-party notices",[n("pre",{style:{margin:"0",maxHeight:"60vh",overflow:"auto",font:"11.5px/1.7 var(--font-mono)",color:"var(--faint)",whiteSpace:"pre-wrap",userSelect:"text"}},e)],[n("button.btn",{onclick:ue},"Close")]))}function yt(e,t){return n("div.settinggroup",null,n("div.head.eyebrow",null,e),...t)}function ge(e,t,s){return n("div.settingrow",null,n("div.about",null,n("span.label",null,e),t?n("span.help",null,t):null),n("div.control",null,s))}function en(e,t,s){return n("select",{onchange:o=>s(o.target.value)},...e.map(([o,a])=>n("option",{value:o,selected:o===t?"":null},a)))}function tn(e,t,s,o,a="",i=0){return n("div",{style:{display:"flex",alignItems:"center",gap:"6px"}},n("input",{type:"number",value:String(i?e.toFixed(i):Math.round(e)),min:String(t),max:String(s),step:i?String(10**-i):"1",style:{width:"90px"},onchange:r=>{const l=Number(r.target.value);Number.isFinite(l)&&o(Math.min(s,Math.max(t,l)))}}),a?n("span.muted",{style:{fontSize:"11.5px"}},a):null)}const pr={M:2,L:2,T:2,H:1,V:1,C:6,S:4,Q:4,A:7,Z:0};function*hr(e){const t=e.match(/[a-zA-Z]|-?\d*\.?\d+(?:[eE][-+]?\d+)?/g)??[];let s=0,o="";for(;s="a"&&l<="z",p=l.toUpperCase(),h=s,f=o,u=b=>d?h+c[b]:c[b],g=b=>d?f+c[b]:c[b];switch(p){case"M":s=u(0),o=g(1),a=s,i=o,t.push({x:s,y:o}),r=null;break;case"L":s=u(0),o=g(1),t.push({x:s,y:o}),r=null;break;case"H":s=d?h+c[0]:c[0],t.push({x:s,y:o}),r=null;break;case"V":o=d?f+c[0]:c[0],t.push({x:s,y:o}),r=null;break;case"C":{const b={x:u(0),y:g(1)},x={x:u(2),y:g(3)};s=u(4),o=g(5),t.push({x:s,y:o,in:b,out:x}),r=x;break}case"S":{const b=r?{x:2*h-r.x,y:2*f-r.y}:{x:h,y:f},x={x:u(0),y:g(1)};s=u(2),o=g(3),t.push({x:s,y:o,in:b,out:x}),r=x;break}case"Q":{const b={x:u(0),y:g(1)};s=u(2),o=g(3),t.push({x:s,y:o,in:b,out:b}),r=b;break}case"T":{const b=r?{x:2*h-r.x,y:2*f-r.y}:{x:h,y:f};s=u(0),o=g(1),t.push({x:s,y:o,in:b,out:b}),r=b;break}case"A":s=u(5),o=g(6),t.push({x:s,y:o}),r=null;break;case"Z":s=a,o=i,r=null;break}}return t}const gr="path, circle, ellipse, rect, line, polyline, polygon";function mr(e){const t=[];for(const s of e.querySelectorAll(gr)){const o=yr(s);o&&t.push(o)}return t}function ce(e,t,s=0){const o=e.getAttribute(t);if(o===null||o.trim()==="")return s;const a=Number(o);return Number.isFinite(a)?a:s}function br(e){const t=/rotate\(\s*(-?[\d.]+)/.exec(e.getAttribute("transform")??"");return t?Number(t[1]):0}function yr(e){const t=s=>(Math.round(s*1e3)/1e3).toString();switch(e.nodeName.toLowerCase()){case"path":return e.getAttribute("d");case"circle":case"ellipse":{const s=ce(e,"cx"),o=ce(e,"cy"),a=e.nodeName.toLowerCase()==="circle",i=a?ce(e,"r"):ce(e,"rx"),r=a?ce(e,"r"):ce(e,"ry");if(!(i>0)||!(r>0))return null;const l=br(e),c=l*Math.PI/180,d=Math.cos(c),p=Math.sin(c),h=(u,g)=>`${t(s+u*d-g*p)},${t(o+u*p+g*d)}`,f=`A${t(i)},${t(r)} ${t(l)} 0 1 `;return`M${h(i,0)}${f}${h(0,r)}${f}${h(-i,0)}${f}${h(0,-r)}${f}${h(i,0)}Z`}case"rect":{const s=ce(e,"x"),o=ce(e,"y"),a=ce(e,"width"),i=ce(e,"height");if(!(a>0)||!(i>0))return null;const r=Math.min(ce(e,"rx",ce(e,"ry")),a/2),l=Math.min(ce(e,"ry",ce(e,"rx")),i/2);if(!(r>0)||!(l>0))return`M${t(s)},${t(o)}L${t(s+a)},${t(o)}L${t(s+a)},${t(o+i)}L${t(s)},${t(o+i)}Z`;const c=`A${t(r)},${t(l)} 0 0 1 `;return`M${t(s+r)},${t(o)}L${t(s+a-r)},${t(o)}${c}${t(s+a)},${t(o+l)}L${t(s+a)},${t(o+i-l)}${c}${t(s+a-r)},${t(o+i)}L${t(s+r)},${t(o+i)}${c}${t(s)},${t(o+i-l)}L${t(s)},${t(o+l)}${c}${t(s+r)},${t(o)}Z`}case"line":return`M${t(ce(e,"x1"))},${t(ce(e,"y1"))}L${t(ce(e,"x2"))},${t(ce(e,"y2"))}`;case"polyline":case"polygon":{const s=(e.getAttribute("points")??"").trim().split(/[\s,]+/).map(Number);if(s.length<4||s.some(a=>!Number.isFinite(a)))return null;const o=[`M${t(s[0])},${t(s[1])}`];for(let a=2;a+1Number.isFinite(i)))return{x:a[0],y:a[1],w:a[2],h:a[3]}}const s=/\swidth="([\d.]+)"/.exec(e),o=/\sheight="([\d.]+)"/.exec(e);return s&&o?{x:0,y:0,w:Number(s[1]),h:Number(o[1])}:null}function wr(e){const t=Math.min(1,Math.max(0,(e-1)/11));return{r:1.2+t*1.2,opacity:.4+t*.6}}const Ts=.25,As=64;function vr(e){const t=n("div.art"),s=n("div.art.compare");s.style.display="none";const o=n("span.eyebrow.panelabel",null,"Source"),a=n("div.art"),i=xe("svg.overlay"),r=n("div.pane.source",null,o,t,s);a.append(i);const l=n("canvas.anchors"),c=n("span.eyebrow.panelabel",null,"Vector · SVG"),d=n("div.pane.vector",null,c,a,l),p=n("div.wipehandle",{role:"separator","aria-label":"Wipe"}),h=n("div.panes",null,r,d,p),f=h;let u={x:0,y:0,w:1,h:1},g=null,b,x,M,v=0,S=0,y=Number.NaN,$,C=null,D={x:0,y:0,w:1,h:1};const z={certainty:xe("g.certainty"),wireframe:xe("g.wireframe"),handles:xe("g.handles")},O={certainty:!1,wireframe:!1,handles:!1};for(const w of Object.values(z))w.style.display="none";l.style.display="none",i.append(z.certainty,z.wireframe,z.handles);let A=null,P=null;function J(){const w=e.state,L=w.svg,T=w.source!==x,_=L!==b;if((T||_)&&(u=(L?Cs(L):null)??{x:0,y:0,w:w.source?.width??1,h:w.source?.height??1},y=Number.NaN),T&&(x=w.source,F(t,w.source?n("img",{src:w.source.preview,alt:"",draggable:"false"}):null)),_){if(b=L,g=null,L){const G=new DOMParser().parseFromString(L,"image/svg+xml").documentElement;G&&G.nodeName==="svg"&&!G.querySelector("parsererror")&&(G.setAttribute("width",String(u.w)),G.setAttribute("height",String(u.h)),g=document.importNode(G,!0))}F(a,g,i);const q=++v;(L?co(L):Promise.resolve(null)).then(G=>{q===v&&(h.classList.toggle("onlight",G==="light"),h.classList.toggle("ondark",G==="dark"))}),i.setAttribute("viewBox",`${u.x} ${u.y} ${u.w} ${u.h}`),A=null,P=null;for(const G of["wireframe","handles"])F(z[G]),O[G]=!1;te(!0)}(w.result!==M||_&&!L)&&se(),T||_?(Me(),ze()):w.show.certainty&&Me()}function H(){const w=e.state.compare;if(w===$)return;$=w,C&&URL.revokeObjectURL(C),C=null,w?(D=Cs(w.svg)??u,C=URL.createObjectURL(new Blob([w.svg],{type:"image/svg+xml"})),F(s,n("img",{src:C,alt:"",draggable:"false",style:{imageRendering:"auto"}}))):F(s),s.style.display=w?"":"none",t.style.display=w?"none":"",o.textContent=w?w.label:"Source",c.textContent=w?"Yours":"Vector · SVG",r.classList.toggle("comparing",!!w);const L=`translate(${e.state.pan.x}px, ${e.state.pan.y}px)`;(w?s:t).style.transform=L,X()}function X(){const w=s.querySelector("img");if(!w)return;const L=e.state.zoom;w.style.position="absolute",w.style.left=`${(D.x-u.x)*L}px`,w.style.top=`${(D.y-u.y)*L}px`,w.style.width=`${D.w*L}px`,w.style.height=`${D.h*L}px`,w.style.maxWidth="none"}function se(){M=void 0,S++,F(z.certainty),O.certainty=!1}function ee(){return A??=g?mr(g):[],A}function Q(){if(P)return P;const w=[],L=[],T=[];for(const _ of ee()){const q=new Set;for(const G of fr(_)){const le=Math.round(G.x*1e3)*1e8+Math.round(G.y*1e3);q.has(le)||(q.add(le),w.push({x:G.x,y:G.y}));for(const me of[G.in,G.out])me&&(L.push(`M${G.x} ${G.y}L${me.x} ${me.y}`),T.push(`M${me.x} ${me.y}l.001 .001`))}}return P={lines:L.join(""),knobs:T.join(""),dots:w},P}function j(w){if(O[w]=!0,!g)return;const L=z[w];if(w==="wireframe"){const T=xr(ee());T&&L.append(xe("path",{d:T}))}else{const{lines:T,knobs:_}=Q();T&&L.append(xe("path.lines",{d:T}),xe("path.knobs",{d:_}))}}let ne="",ae=!1;function te(w=!1){w&&(ne=""),!ae&&(ae=!0,requestAnimationFrame(()=>{ae=!1,pe()}))}function pe(){const w=e.state,L=!!g&&w.show.anchors,T=Math.round(d.clientWidth),_=Math.round(d.clientHeight);if(L&&(!T||!_))return;const q=window.devicePixelRatio||1,G=L?`${w.zoom} ${w.pan.x} ${w.pan.y} ${T} ${_} ${q}`:"off";if(G===ne)return;ne=G;const le=l.getContext("2d");if(!le)return;if(!L){l.width=0,l.height=0;return}const me=Math.max(1,Math.round(T*q)),Rn=Math.max(1,Math.round(_*q));(l.width!==me||l.height!==Rn)&&(l.width=me,l.height=Rn),l.style.width=`${T}px`,l.style.height=`${_}px`,le.setTransform(q,0,0,q,0,0),le.clearRect(0,0,T,_);const{dots:Gn}=Q();if(!Gn.length)return;const Wn=getComputedStyle(d),Eo=Wn.getPropertyValue("--stage").trim()||"#808080",zo=Wn.getPropertyValue("--anchor").trim()||"#ff5c00",{r:qn,opacity:Lo}=wr(w.zoom);l.style.opacity=String(Lo);const Po=w.pan.x-u.x*w.zoom,Do=w.pan.y-u.y*w.zoom,Ze=qn*1.15,Fo=qn*.85,ut=[],Ht=[];for(const Je of Gn){const Xe=Po+Je.x*w.zoom,we=Do+Je.y*w.zoom;Xe<-Ze||we<-Ze||Xe>T+Ze||we>_+Ze||(ut.push(Xe),Ht.push(we))}for(const[Je,Xe]of[[Ze,Eo],[Fo,zo]]){le.beginPath();for(let we=0;we{T===S&&e.set({bandsMissing:!0,show:{...e.state.show,certainty:!1}})};B.traceBands(w.resultGeneration).then(q=>{T===S&&(q?fe(q):_())},_)}function fe(w){F(z.certainty);for(const{cls:L,d:T}of Tr(kr(w)))z.certainty.append(xe(`path.${L}`,{d:T}))}function Me(){const{show:w}=e.state,L=(T,_)=>{_&&!O[T]&&(T==="certainty"?Te():j(T)),z[T].style.display=_?"":"none"};L("wireframe",w.wireframe),L("handles",w.handles),L("certainty",w.certainty),l.style.display=w.anchors?"":"none",te(),g&&(g.style.opacity=w.fill?"1":"0.12")}function ze(){const w=e.state,L=`translate(${w.pan.x}px, ${w.pan.y}px)`;for(const T of $?[s,a]:[t,a])T.style.transform=L;if(w.zoom!==y){y=w.zoom;const T=u.w*w.zoom,_=u.h*w.zoom,q=t.querySelector("img");q&&(q.style.width=`${T}px`,q.style.height=`${_}px`),g&&(g.setAttribute("width",String(T)),g.setAttribute("height",String(_))),i.setAttribute("width",String(T)),i.setAttribute("height",String(_)),X()}w.show.anchors&&te(),Le()}function Le(){const w=e.state;if(h.classList.toggle("stacked",w.view!=="side"),p.style.display=w.view==="wipe"?"":"none",w.view==="side")r.classList.remove("hidden"),d.classList.remove("hidden"),r.style.clipPath="",d.style.clipPath="";else if(w.view==="wipe"){r.classList.remove("hidden"),d.classList.remove("hidden");const q=`${(w.wipe*100).toFixed(2)}%`;p.style.left=q,r.style.clipPath=`inset(0 ${(100-w.wipe*100).toFixed(2)}% 0 0)`,d.style.clipPath=`inset(0 0 0 ${q})`}else r.style.clipPath="",d.style.clipPath="",r.classList.toggle("hidden",!w.flicked),d.classList.toggle("hidden",w.flicked);const L=w.detail&&w.zoom>=6;let T=d.querySelector(".pixelgrid");L&&!T&&(T=n("div.pixelgrid"),d.append(T)),T&&(T.style.display=L?"":"none",T.style.backgroundSize=`${w.zoom}px ${w.zoom}px`,T.style.backgroundPosition=`${w.pan.x}px ${w.pan.y}px`);const _=t.querySelector("img");_&&(_.style.imageRendering=w.zoom>=4?"pixelated":"auto")}function de(){const w=d.getBoundingClientRect();return{w:w.width||1,h:w.height||1}}function Ae(){const{w,h:L}=de(),T=Math.min(w/u.w,L/u.h)*.88;e.set({zoom:T,pan:{x:(w-u.w*T)/2,y:(L-u.h*T)/2},fitted:!0})}function k(w,L,T){const{w:_,h:q}=de();e.set({zoom:T,pan:{x:_/2-w*T,y:q/2-L*T},fitted:!1})}function N(w){const{w:L,h:T}=de();E(L/2,T/2,Math.min(As,Math.max(Ts,w))/e.state.zoom)}function E(w,L,T){const _=e.state,q=Math.min(As,Math.max(Ts,_.zoom*T)),G=q/_.zoom;e.set({zoom:q,pan:{x:w-(w-_.pan.x)*G,y:L-(L-_.pan.y)*G},fitted:!1})}for(const w of[r,d])w.addEventListener("wheel",L=>{L.preventDefault();const T=w.getBoundingClientRect();E(L.clientX-T.left,L.clientY-T.top,Math.exp(-L.deltaY*.0016))},{passive:!1}),w.addEventListener("dblclick",Ae),w.addEventListener("pointerdown",L=>{if(L.button!==0)return;w.setPointerCapture(L.pointerId);let T=L.clientX,_=L.clientY;const q=le=>{const me=e.state;e.set({pan:{x:me.pan.x+(le.clientX-T),y:me.pan.y+(le.clientY-_)},fitted:!1}),T=le.clientX,_=le.clientY},G=()=>{w.removeEventListener("pointermove",q),w.removeEventListener("pointerup",G),w.removeEventListener("pointercancel",G)};w.addEventListener("pointermove",q),w.addEventListener("pointerup",G),w.addEventListener("pointercancel",G)});p.addEventListener("pointerdown",w=>{w.stopPropagation(),p.setPointerCapture(w.pointerId);const L=_=>{const q=h.getBoundingClientRect();e.set({wipe:Math.min(.98,Math.max(.02,(_.clientX-q.left)/q.width))})},T=()=>{p.removeEventListener("pointermove",L),p.removeEventListener("pointerup",T)};p.addEventListener("pointermove",L),p.addEventListener("pointerup",T)});const V=n("style",{"data-owner":"viewer-isolate"});document.head.append(V);function K(){const w=e.state.hoverFill;d.classList.toggle("isolating",!!w?.length),V.textContent=w?.length?Ar(w):""}e.on(["svg","source","result"],J),e.on(["compare"],H),e.on(["zoom","pan","view","wipe","flicked","detail"],ze),e.on(["show"],Me),e.on(["hoverFill"],K),new ResizeObserver(()=>{!e.state.svg&&!e.state.source||(Le(),e.state.show.anchors&&te(),e.state.fitted&&Ae())}).observe(h),new MutationObserver(()=>{e.state.show.anchors&&te(!0)}).observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]});const U=()=>{window.matchMedia(`(resolution: ${window.devicePixelRatio||1}dppx)`).addEventListener("change",()=>{e.state.show.anchors&&te(),U()},{once:!0})};return U(),{el:f,redraw:J,transform:ze,fit:Ae,goTo:k,zoomTo:N}}function xr(e){let t="";for(const s of e){const o=s.trim();o&&(t+=o[0]==="M"?o:`M0 0${o}`)}return t}function kr(e){const t=[],s=/]*)>/g;for(let o=s.exec(e);o;o=s.exec(e)){const a=o[1],i=/(?:^|\s)d="([^"]*)"/.exec(a)?.[1]?.trim();if(!i)continue;const r=$r(i);if(!r)continue;const l=Number(/\sdata-sigma-mean="([^"]*)"/.exec(a)?.[1]??0);t.push({d:i[0]==="M"?i:`M0 0${i}`,cls:l<=.1?"sure":l<=.3?"soft":"unsure",x0:r.x0,y0:r.y0,x1:r.x1,y1:r.y1})}return t}const Sr=[1,.1,.01,.001,1e-4,1e-5,1e-6,1e-7,1e-8,1e-9,1e-10,1e-11,1e-12,1e-13,1e-14,1e-15];function $r(e){let t=1/0,s=1/0,o=-1/0,a=-1/0,i=!1;const r=e.length;let l=0;for(;l=48&&c<=57||c===46)){p||l++;continue}let h=0;for(;c>=48&&c<=57;)h=h*10+(c-48),c=e.charCodeAt(++l);if(c===46){let f=0,u=0;for(c=e.charCodeAt(++l);c>=48&&c<=57;)u<15&&(f=f*10+(c-48),u++),c=e.charCodeAt(++l);h+=f*Sr[u]}if(c===101||c===69){c=e.charCodeAt(++l);const f=c===45;(f||c===43)&&(c=e.charCodeAt(++l));let u=0;for(;c>=48&&c<=57;)u=u*10+(c-48),c=e.charCodeAt(++l);h*=10**(f?-u:u)}d&&(h=-h),i?(ha&&(a=h)):(ho&&(o=h)),i=!i}return t<=o&&s<=a?{x0:t,y0:s,x1:o,y1:a}:null}const Mr=1,wt=16,Cr=256;function Tr(e,t=Cr){const s=new Map,o=new Int32Array(e.length),a=[],i={sure:[],soft:[],unsure:[]},r=[],l=Mr/2;for(let d=0;dh&&O.y0-lf&&(O.cls===p.cls?r[o[z]]=d+1:o[z]>S&&(S=o[z]))}}let y=-1;for(const $ of i[p.cls])if($>S&&r[$]!==d+1){y=$;break}y<0&&(y=a.length,a.push(p.cls),i[p.cls].push(y)),o[d]=y;for(let $=M;$<=v;$++)for(let C=b;C<=x;C++){const D=C*65536+$,z=s.get(D);z?z.push(d):s.set(D,[d])}}const c=new Map;for(let d=0;dd[0]-p[0]).map(d=>d[1])}const nn=".pane.vector > .art > svg:not(.overlay)";function Ar(e){const t=new Set;for(const o of e){const a=o.replace(/["\\\n\r]/g,"").trim();if(!a)continue;t.add(a);const i=/^#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/i.exec(a);i&&t.add(`#${i[1]}${i[2]}${i[3]}`)}if(!t.size)return"";const s=o=>[...t].map(a=>`[${o}="${a}" i]`).join(",");return[`${nn} * { fill-opacity: 0.12 !important; stroke-opacity: 0.12 !important; }`,`${nn} :is(${s("fill")}) { fill-opacity: 1 !important; stroke: var(--ink, #faf8f5) !important; stroke-opacity: 1 !important; stroke-width: 3px !important; vector-effect: non-scaling-stroke !important; paint-order: stroke !important; }`,`${nn} :is(${s("stroke")}) { stroke-opacity: 1 !important; }`].join(` -`)}function Er(e){const t=n("aside.activity",{"aria-label":"Engine activity","aria-live":"off"}),s=n("button.activityhead",{type:"button","data-ctl":"activity-toggle",onclick:()=>e.set({logOpen:!e.state.logOpen})}),o=n("div.activitybody",{role:"log"});t.append(s,o);const a=(r,l)=>{if(r.kind==="note")return n("div.activityline.note",null,n("span.t",null,as(r.at)),n("span.w",null,r.text??""));const c=l&&r.step?` · ${wn(r.step)}`:"";return n(`div.activityline${l?".running":".done"}`,null,n("span.t",null,as(r.at)),n("span.w",null,`${r.what}${c}`),l?n("span.r.num",{"data-live":"stage"}):n("span.r.num",null,r.ms===void 0?"":Ge(r.ms)))},i=()=>{const r=e.state,l=r.tab==="vectorize"&&!!r.source&&!r.screen&&(r.tracing||r.traceLog.length>0);if(t.hidden=!l,!l)return;t.classList.toggle("open",r.logOpen),t.classList.toggle("busy",r.tracing);const c=r.traceEnded&&!r.tracing?Ge(r.traceEnded-r.traceStarted):null;if(F(s,r.tracing?n("span.pulse"):null,n("span.title",null,"Engine log"),r.tracing?n("span.num.muted",{"data-live":"elapsed"},Ge(performance.now()-r.traceStarted)):c?n("span.num.muted",null,c):null,n("span.chev",null,r.logOpen?"Hide":"Show")),!r.logOpen){F(o);return}const d=o.scrollHeight-o.scrollTop-o.clientHeight<24,p=r.traceLog.slice(-160);let h=-1;if(r.tracing)for(let f=p.length-1;f>=0&&h<0;f--)p[f].kind==="stage"&&p[f].ms===void 0&&(h=f);F(o,...p.map((f,u)=>a(f,u===h))),d&&(o.scrollTop=o.scrollHeight)};return e.on(["traceLog","tracing","logOpen","source","tab","screen","liveNow"],i),i(),t}function zr(e,t,s){const o=vr(e),a=n("div.viewertools"),i=n("div",{style:{flex:"1",minHeight:"0",position:"relative",display:"flex"}}),r=n("div.statusstrip"),l=n("section.stage",null,a,i,r);let c="";const d=()=>{const u=e.state,g=!!u.source,b=[1,4,12].map(v=>!u.fitted&&Math.abs(u.zoom-v)<.01),x=JSON.stringify([g,u.view,u.show,u.fitted,b,u.detail,!!u.svg,u.bandsMissing,u.settings.mode]);if(x===c)return;c=x;const M=(v,S)=>n("button",{"aria-pressed":String(S==="fit"?u.fitted:!u.fitted&&Math.abs(u.zoom-S)<.01),disabled:!g,onclick:()=>S==="fit"?o.fit():o.zoomTo(S)},v);F(a,n("div.seg",null,...["side","wipe","ab"].map(v=>n("button",{"aria-pressed":String(u.view===v),disabled:!g,onclick:()=>e.set({view:v})},v==="side"?"Side by side":v==="wipe"?"Wipe":"A/B"))),n("span.muted",{style:{fontSize:"11.5px"}},"hold ",n("kbd",null,"Space")," to flick"),n("div.sep"),n("span.eyebrow",null,"Show"),n("div",{style:{display:"flex",gap:"6px"}},...[["fill","Fill"],["wireframe","Wireframe"],["anchors","Anchors"],["handles","Handles"],["certainty","Certainty"]].map(([v,S])=>n("button.toggle",{"aria-pressed":String(u.show[v]),disabled:!u.svg||v==="certainty"&&u.bandsMissing,title:v==="certainty"?"Where each boundary could be: a band two sigmas either side, measured from the pixels. Thin is certain; a wide band is a soft, blurred or compressed edge, and the curve there is a best guess.":void 0,onclick:()=>{u.show[v]=!u.show[v],e.touch("show")}},S))),n("div",{style:{marginLeft:"auto",display:"flex",alignItems:"center",gap:"12px"}},u.show.wireframe||u.show.anchors||u.show.handles||u.show.certainty?n("div.legend",null,u.show.anchors?n("span",null,n("i.anchor"),"anchor"):null,u.show.handles?n("span",null,n("i.handle"),"handle"):null,u.show.wireframe?n("span",null,n("i.wire"),"wireframe"):null,...u.show.certainty?[n("span",null,n("i.band.sure"),"sure"),n("span",null,n("i.band.soft"),"soft"),n("span",null,n("i.band.unsure"),"unsure")]:[]):null,n("button.toggle",{"aria-pressed":String(u.detail),disabled:!u.svg,title:"Show the source's pixel grid under the vector edge",onclick:()=>{const v=!u.detail;e.set({detail:v}),v&&u.zoom<12&&o.zoomTo(12)}},"Detail"),n("div.seg.num",null,M("Fit","fit"),M("1×",1),M("4×",4),M("12×",12))))},p=n("div",{style:{display:"contents"}});i.append(o.el,p,Er(e));const h=()=>{const u=e.state;if(!u.source){o.el.style.display="none",F(p,Ir(e,t,s()));return}o.el.style.display="",F(p,Fr(u.stageState,u,t),u.svg?Pr(e):null,u.detail&&u.svg?Dr(e,t):null),u.zoom===1&&u.pan.x===0&&u.pan.y===0&&o.fit()},f=()=>{const u=e.state,g=u.report;let b="Ready",x="",M="var(--dim)",v=null;if(u.tracing)b=_t(u),x=`· ${u.settings.mode==="fast"?"fast":"quality"} · ${bn(u,u.tracingTier)??"—"} px ·`,v=n("span",null,n("span.num",{"data-live":"elapsed"},Bt(u))," elapsed · Esc to cancel"),M="var(--state-stale)";else if(g&&u.result){const S=u.result.tier==="draft",y=u.settings.mode==="fast"?"Fast":"Quality";b=S?`${y} draft`:`${y} in ${nt(g.seconds)}`,x=S?`· ${g.tracedPx} px · full trace queued`:`· ${g.tracedPx} px · final · ${oe(g.segments)} segments`,M=S?"var(--state-draft)":"var(--state-final)"}else u.source&&(b="Opened",x=`· ${u.source.width} × ${u.source.height} · ${u.source.container}`);F(r,n("span.lead",{style:{color:M}},b),x?n("span",null,x):null,v,u.update?.newer?n("span",{style:{display:"flex",alignItems:"center",gap:"8px",marginLeft:"12px"}},n("span.updatechip",null,n("i"),`${u.update.latest} available`),n("button.reset",{onclick:t.showUpdate},"What changed")):null,Lr(e,t),n("span.privacy",null,"Your image never leaves this computer."))};return e.on(["source","view","show","zoom","fitted","detail","svg","bandsMissing","settings"],d),e.on(["source","svg","stageState","result","detail","worstCorner","prefs"],h),e.on(["tracing","liveStages","liveNow","report","result","source","update","denoiserFetch","settings"],f),d(),h(),f(),window.addEventListener("keydown",u=>{if(e.state.screen||e.state.tab!=="vectorize"||u.target instanceof HTMLInputElement||u.target instanceof HTMLTextAreaElement)return;const g=e.state;u.key===" "&&!u.repeat?(u.preventDefault(),e.set({flicked:!0})):u.key==="0"?o.fit():u.key==="+"||u.key==="="?o.zoomTo(g.zoom*1.4):u.key==="-"&&o.zoomTo(g.zoom/1.4)}),window.addEventListener("keyup",u=>{u.key===" "&&e.set({flicked:!1})}),{el:l,viewer:o,mount:u=>i.append(u)}}function Lr(e,t){const s=e.state,o=s.denoiserFetch;if(!o||!s.caps?.denoiser.supported)return null;const a=s.settings.cleanUpDamage!=="off",i=r=>Math.round(r/(1024*1024));if(o.phase==="downloading"){const r=En(o);return n("span.fetchchip",{"data-ctl":"denoiser-chip",title:"The denoiser downloads in the background, once; later visits read it from this browser"},Et(o),o.total?`Denoiser ${i(o.got)} of ${i(o.total)} MB · ${r}%`:`Denoiser ${i(o.got)} MB`)}return(o.phase==="stored"||o.phase==="preparing")&&a?n("span.fetchchip",{"data-ctl":"denoiser-chip"},Et(o),"Preparing the denoiser…"):o.phase==="failed"?n("span.fetchchip.bad",{"data-ctl":"denoiser-chip",title:o.message??""},"Denoiser download failed",n("button.reset",{"data-ctl":"denoiser-chip-retry",onclick:t.retryDenoiser},"Retry")):null}function Pr(e){const t=e.state,s=t.tracing?"stale":t.result?.tier==="draft"?"draft":"final",o=t.tracing?`re-tracing · ${bn(t,t.tracingTier)??"—"} px`:`${t.result?.tier??"final"} · ${t.report?.tracedPx??"—"} px`;return n(`span.chip.${s}${t.justSwapped?".swapped":""}`,{style:{position:"absolute",top:"46px",right:"20px"}},n("i"),o)}function Dr(e,t){const s=e.state;return n("div.callout",null,n("div.head",null,n("span.eyebrow",null,s.worstCorner?"Worst corner · 12×":"Detail"),s.worstCorner?n("button.reset",{onclick:t.jumpToWorst},"Jump to it"):null),n("span",null,Or(e)))}function Fr(e,t,s){const o=(a,i,r,l,c)=>n("div.stagestate",null,n("span",{style:{color:i,display:"flex"}},Z(a,24)),n("span.title",null,r),n("span.body",null,l),c?n("button.action.reset",{onclick:c[1]},c[0]):null);switch(e.kind){case"decoding":return o("clock","var(--faint)",`Reading ${t.source?.name??"the image"}`,`${t.source?.width} × ${t.source?.height}. Tracing starts as soon as the pixels are in memory.`);case"flat":return o("info","var(--gold)","This image is one flat colour","There are no boundaries to trace. A single rectangle would be the whole output.",["Open another image",s.openFile]);case"undecodable":return o("alert","var(--bad)","This file will not decode",e.message,["Open another image",s.openFile]);case"outOfMemory":return o("alert","var(--bad)","The trace would run out of memory",`About ${e.neededGb.toFixed(1)} GB is needed at this trace size. Lowering it to ${e.suggestPx} px usually costs nothing visible.`,[`Retry at ${e.suggestPx} px`,()=>s.retryAt(e.suggestPx)]);case"failed":return o("alert","var(--bad)","The trace stopped",e.message,["Trace again",s.traceNow]);case"cancelled":return o("x","var(--faint)","Trace cancelled","The last full trace is still on screen and still exportable. Nothing was discarded.",["Trace again",s.traceNow]);case"denoiserMissing":return o("download","var(--faint)","This preset wants the denoiser","Photo or scan works without it, but compression damage stays in the colours. The download is explained before anything is fetched, and it runs locally like everything else.",["Download the denoiser",s.openDenoiser]);case"offline":return o("globe","var(--faint)","No network",`Tracing is unaffected — it never needed one. ${e.message}`);default:return null}}function Nr(e,t){const s=e.state.prefs;if(!s||s.seenFirstRun)return null;const o=(a,i,r)=>n("li",null,n("span.introstep.num",null,a),n("div",null,n("span.introtitle",null,i),n("span.faint",null,r)));return n("div.intro",{role:"note"},n("span.eyebrow",null,"How opening an image works"),n("ol",null,o("1","Auto traces it at once.","The moment it opens, with nothing to choose first."),o("2","A small card offers Custom.","A few short steps, each explained, with the result beside Auto's as you go. Ignore it and Auto stands."),o("3","Everything is an ordinary setting.","What Custom chooses is under Tune afterwards, to change like any other control.")),n("div.introfoot",null,n("span.muted",null,"Settings has a switch to always use Auto, or always Custom."),n("button.btn.compact",{"data-ctl":"intro-seen",onclick:t.markSeen},"Got it")))}function Ir(e,t,s){const o=Os(e.state.caps?.platform);return n("div.firstrun",null,Nr(e,t),n("div.drop",null,n("span.glyph",null,Z("image",32)),n("div",{style:{display:"flex",flexDirection:"column",gap:"6px"}},n("span.headline.resting",null,"Drop a PNG, JPEG or WebP"),n("span.headline.release",null,"Release to trace"),n("span.faint.resting",{style:{fontSize:"13px"}},"or press ",n("kbd",null,`${o}+O`)," to choose a file")),n("button.btn.primary.resting",{onclick:t.openFile},"Open an image")),s.length?n("div.samples",null,n("span.eyebrow",null,"Or try one of these"),n("div.samplerow",null,...s.map(a=>n("button.sample",{onclick:()=>t.openSample(a.file)},n("span.thumb",null,n("img",{src:a.preview,alt:""})),n("span.label",null,a.label))))):null,n("p.desktopnote.faint",null,"For the fastest engine, whole folders in one batch, no browser limits and offline use, get ",n("a",{href:_s,target:"_blank",rel:"noopener noreferrer"},"Inkvec Studio for the desktop"),"."))}function fo(e,t){const s=e.state.worstCorner;s&&(e.set({detail:!0}),t.goTo(s.x,s.y,12))}function Or(e){const t=e.state.worstCorner;return t?`Worst disagreement: ${re(t.de00)} dE00, at ${t.x} × ${t.y} in the traced raster.`:"The trace and the source agree everywhere we can measure."}const go={mode:"quality",precision:.1,speckleFloor:2,traceSize:2048,timeLimit:0,maxColours:64,colourMerging:.035,flatFills:!1,blackAndWhite:!1,monochrome:!1,cleanUpDamage:"off",matchRepeatedShapes:!0,matchThreshold:.92,fewerPaths:!1,lineArt:!1,repairRings:!0,editability:!1,bezierCost:6,cornerAngle:10,minify:!1,transparentBackground:!1,margin:0,holesAsCutouts:!1,traceTransparency:!0};function mo(e,t,s){e[t]=s}const m=new aa(oa(go,{tolerancePx:.1,judgePx:1024,cornerDegrees:30,documentCleanup:!0}));let bo=[],ot=0,In=0,zt=0;const Oe=new Map,St=new Map,yo=new Sa;function _r(){const e={...m.state.settings};return delete e.colourGroups,m.state.colourGroups.length&&(e.colourGroups=m.state.colourGroups),e}async function Ee(e){if(!m.state.source)return;const t=performance.now();try{const s=m.state.colourGroups,o=await B.startTrace(_r(),e);if(ovoid Ee("final"),m.state.prefs?.settleMs??800))}function vo(){return new Promise(e=>{if(!m.state.source){e();return}const t=m.on(["result","stageState"],()=>{m.state.tracing||(t(),e())});window.clearTimeout(ot),Ee("final")})}let sn=0;function xo(e,t=m.state.generation){const s=++sn;if(e.state!=="traced"||!m.state.snaps.length){on(e,t);return}ko(e).then(o=>{s===sn&&on(e,t,o)},o=>{s===sn&&(W(`The snapped colours could not be applied to this trace: ${o}`,{kind:"bad"}),on(e,t))})}async function ko(e){for(;;){const t=m.state.snaps;if(!t.length)return{svg:e.svg,inks:e.palette};const s=e.report.tracedPx,o=await B.snapInks(e.svg,t,s,s);if(m.state.snaps===t)return o}}function on(e,t,s=null){const o=t!==m.state.generation&&m.state.tracing;if(e.state!=="cancelled"){if(o||m.set({liveNow:null,traceEnded:performance.now()}),e.state==="traced"){const a=m.state.result?.tier==="draft",i=m.state.result?.tier==="final"?m.state.report:m.state.previous;m.set({result:e,resultGeneration:t,resultGroups:St.get(t)??m.state.resultGroups,bandsMissing:!1,previous:e.tier==="final"?i:m.state.previous,svg:s?.svg??e.svg,report:e.report,palette:s?.inks??e.palette,losses:e.losses,worstCorner:e.worstCorner,stageState:{kind:"drawing"},tracing:o,justSwapped:a&&e.tier==="final"}),m.state.justSwapped&&window.setTimeout(()=>m.set({justSwapped:!1}),600),e.tier==="final"&&m.set({groupSuggestions:Hs(m.state)});const r=m.state.auto;r&&t===r.generation&&e.tier==="final"&&!r.report&&(m.set({auto:{...r,svg:e.svg,report:e.report,palette:e.palette,losses:e.losses}}),B.sourceFacts(r.settings.traceSize).then(l=>{m.state.auto?.generation===t&&m.set({facts:l})},()=>{})),e.oversized&&e.tier==="final"&&W(`Traced at ${e.tracedPx} px; the SVG is still ${e.sourcePx[0]} × ${e.sourcePx[1]} and scales without limit. This is normal.`);return}if(!o)switch(m.set({tracing:!1}),e.state){case"flat":m.set({stageState:{kind:"flat"},svg:null,report:null,palette:[],losses:[]});break;case"undecodable":m.set({stageState:{kind:"undecodable",message:e.message}});break;case"outOfMemory":m.set({stageState:{kind:"outOfMemory",neededGb:e.neededGb,suggestPx:e.suggestPx}});break;case"failed":m.set({stageState:{kind:"failed",message:e.message}});break}}}async function Ue(e,t=!0){je.close(),window.clearTimeout(ot),In=0,m.set({tracing:!1,liveNow:null,liveStages:[],traceLog:[],stageState:{kind:"decoding"},svg:null,report:null,previous:null,palette:[],losses:[],result:null,snaps:[],colourGroups:[],resultGroups:[],groupSuggestions:null,dismissedGroups:[],simplifyTo:null,paletteSelection:[],hoverFill:null,auto:null,facts:null,chooser:!1,compare:null,autoNoteHidden:!1});try{await e(),m.set({zoom:1,pan:{x:0,y:0},stageState:{kind:"drawing"}}),await Ee("final"),yo.reset();const s=m.state;s.tracing&&m.set({auto:{generation:s.generation,settings:{...s.settings},preset:s.preset,svg:null,report:null,palette:[],losses:[]}}),t&&Br()}catch(s){m.set({stageState:{kind:"undecodable",message:String(s)}})}}function Br(){const e=m.state.prefs?.onOpen??"ask";e==="custom"?je.open():e==="ask"&&m.set({chooser:!0})}async function So(e){await Ue(async()=>{const t=await B.openPath(e);m.set({source:t});const s=await B.loadPrefs();m.set({prefs:s})})}async function $t(e){if(e.path)return So(e.path);const t=e.file;t&&await Ue(async()=>{const s=await B.openBytes(new Uint8Array(await t.arrayBuffer()),e.name);m.set({source:s})})}async function _n(){const[e]=await yn([{name:"Images and SVG",extensions:["png","jpg","jpeg","webp","gif","bmp","tif","tiff","svg"]}]);e&&await $t(e)}async function Rr(e,t){m.set({tab:"vectorize"}),Lt(),await Ue(async()=>{const s=await B.openBytes([...new TextEncoder().encode(e)],t);m.set({source:s})},!1)}window.addEventListener("inkvec:retrace",e=>{const{svg:t,name:s}=e.detail;Rr(t,s)});const hn=n("header.appbar"),Qe=n("div.body"),Gr=rr(m,{applyPrefs:e=>void Ve(e),afterReset:e=>{Pt(e.theme),To(e)},close:()=>m.set({screen:null})});function $o(e,t){mo(m.state.settings,e,t),m.touch("settings"),m.state.caps?.denoiser,e==="cleanUpDamage"&&t!=="off"&&Ao(),We()}const Wt=zr(m,{openFile:()=>void _n(),openSample:e=>void Ue(async()=>{const t=await B.openSample(e);m.set({source:t})}),traceNow:()=>void Ee("final"),cancel:On,retryAt:e=>{m.state.settings.traceSize=e,m.touch("settings"),Ee("final")},jumpToWorst:()=>fo(m,Wt.viewer),openDenoiser:()=>Nn(m),retryDenoiser:()=>ct.retryDenoiser(),showUpdate:()=>{const e=m.state.update;e&&It(e.url)},markSeen:()=>void Ve({seenFirstRun:!0}),changeSetting:$o},()=>bo);function Bn(e){const t=m.state.caps?.presets.find(o=>o.id===e);if(t)return{settings:t.settings,wantsDenoiser:t.wantsDenoiser};const s=m.state.prefs?.saved.find(o=>o.id===e);return s?{settings:s.settings,wantsDenoiser:!1}:null}function Es(){const e=m.state.preset;return e&&Bn(e)?.settings||go}function Mo(e){m.set({settings:{...e.settings},preset:e.preset,colourGroups:[...e.colourGroups]}),We()}const ct={setPreset:e=>{const t=Bn(e);t&&(m.set({preset:e,settings:{...t.settings}}),t.settings.cleanUpDamage!=="off"&&Ao(),We())},savePreset:e=>{const t=m.state.prefs;if(!t)return;const s=`saved:${Date.now().toString(36)}${Math.random().toString(36).slice(2,6)}`,o=[...t.saved,{id:s,name:e,settings:{...m.state.settings}}];Ve({saved:o}).then(()=>{const a=m.state.prefs?.saved.find(i=>i.id===s);a?(m.set({preset:s}),W(`Saved as "${a.name}".`,{kind:"good"})):W("The preset tray is full. Forget one and try again.",{kind:"bad"})})},deletePreset:e=>{const t=m.state.prefs;t&&(Ve({saved:t.saved.filter(s=>s.id!==e)}),m.state.preset===e&&m.set({preset:null}))},baseSettings:Es,changeSetting:$o,resetGroup:e=>{const t=Es(),s={...m.state.settings};for(const o of m.state.caps?.controls??[])o.group===e&&!Ln.has(o.key)&&it(o,s)&&mo(m.state.settings,o.key,t[o.key]);m.touch("settings"),We()},traceNow:()=>void Ee("final"),cancel:On,snap:e=>void Wr(e),setColourGroups:e=>{m.set({colourGroups:e,paletteSelection:[]}),We()},openExport:()=>ao(m,qt,vo),copySvg:async()=>{m.state.svg&&(await Nt(m.state.svg),W("SVG copied. Paste straight into Figma or Illustrator."))},saveCard:()=>Mi(m),openDenoiser:()=>Nn(m),retryDenoiser:()=>void B.denoiserDownload().catch(e=>W(String(e),{kind:"bad"})),jumpToWorst:()=>fo(m,Wt.viewer),backToAuto:()=>{const e=m.state.auto;e&&Mo({settings:e.settings,preset:e.preset,colourGroups:m.state.colourGroups})},hideAutoNote:()=>m.set({autoNoteHidden:!0}),openWizard:()=>je.open()},qt=Qa(m,ct),Co={...ct,restore:Mo,setOnOpen:e=>void Ve({onOpen:e}),markSeen:()=>void Ve({seenFirstRun:!0})},je=$i(m,qt,Co,yo);Wt.mount(xi(m,Co,()=>je.open()));async function Wr(e){const t=m.state,s=t.result;if(!s||!e.length)return;const o=t.snaps,a=new Set(e.map(r=>r.from.toLowerCase())),i=[...t.snaps.filter(r=>!a.has(r.from.toLowerCase())),...e.filter(r=>r.to.toLowerCase()!==r.from.toLowerCase())];m.set({snaps:i});try{const r=await ko(s);if(m.state.result!==s)return;m.set({svg:r.svg,palette:r.inks})}catch(r){m.state.snaps===i&&m.set({snaps:o}),W(String(r),{kind:"bad"})}}let fn=null,gn=null,zs=null;function Lt(){const e=m.state;e.tab==="vectorize"?F(Qe,Wt.el,qt):e.tab==="minify"?(fn??=Fi(m),F(Qe,fn)):e.tab==="fabricate"?(gn??=Oi(m),F(Qe,gn)):(zs??=zi(m),F(Qe,zs))}function Ls(){const e=m.state;F(hn,n("span.brand",null,Ds(18),Ft),e.tab==="vectorize"&&e.source?n("div.filechip",null,n("span.name",null,e.source.name),n("span.muted.num",null,`${e.source.width} × ${e.source.height} · ${e.source.container}`)):null,n("div.seg",{style:{marginLeft:"auto"}},...[["vectorize","Vectorize"],["minify","Minify SVG"],["fabricate","Fabricate"]].map(([t,s])=>n("button",{"aria-pressed":String(e.tab===t),onclick:()=>m.set({tab:t})},s))),n("div",{style:{marginLeft:"auto",display:"flex",alignItems:"center",gap:"2px"}},n("button.btn.ghost.compact",{onclick:()=>void _n()},"Open…"),null,n("button.btn.ghost.compact",{onclick:()=>m.set({screen:"settings"})},"Settings"),n("button.btn.ghost.compact",{onclick:()=>m.set({screen:"about"})},"About"),n("button.btn.ghost.compact",{title:"Before and after on real logos, and how Inkvec compares",onclick:()=>m.set({screen:"showcase"})},"Showcase"),n("button.btn.ghost.compact",{title:"The user guide (F1)",onclick:()=>Pn(io(m.state))},"Help"),n("div.sep"),lt())),hn.setAttribute("data-tauri-drag-region","")}async function Ve(e){const t=m.state.prefs;if(!t)return;const s={...t,trace:dn(m.state.settings),ui:un(m.state),...e},o=await B.savePrefs(s);m.set({prefs:o}),Pt(o.theme)}async function qr(e){const t=m.state.prefs;if(t)try{const s=await B.savePrefs({...t,...e});m.state.prefs===t&&(m.state.prefs=s)}catch{}}const Hr=["vectorize","minify","fabricate"];function To(e){ua(m,e,Hr,t=>Bn(t)!==null)}function Pt(e){const t=e==="dark"||e==="system"&&window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.dataset.theme=t?"dark":"light"}function Ur(e){const t=e.metaKey||e.ctrlKey,s=e.target instanceof HTMLInputElement||e.target instanceof HTMLTextAreaElement;if(t&&e.key.toLowerCase()==="o"){e.preventDefault(),_n();return}if(t&&e.key.toLowerCase()==="e"&&m.state.svg){e.preventDefault(),ao(m,qt,vo);return}if(t&&e.key===","){e.preventDefault(),m.set({screen:"settings"});return}if(!s){if(t&&/^[0-9]$/.test(e.key)){const o=m.state.caps?.presets[(Number(e.key)+9)%10];o&&(e.preventDefault(),m.set({preset:o.id,settings:{...o.settings}}),We());return}e.key==="Escape"&&(m.state.screen?m.set({screen:null}):je.isOpen()?je.close(!0):m.state.chooser?m.set({chooser:!1}):m.state.tracing&&On())}}async function jr(){{Go(m,e=>void Vr(e.map(Bs)));return}}async function Vr(e){if(!e.length)return;const t=e.filter(s=>s.name.toLowerCase().endsWith(".svg"));if(t.length===1&&e.length===1&&m.state.tab==="vectorize"){await $t(t[0]);return}if(t.length){const s=m.state.tab==="fabricate"?"fabricate":"minify";m.set({tab:s}),Lt(),await(s==="fabricate"?gn:fn)?.acceptDropped?.(t[0]);return}if(e.length>1){W(`${e.length} files dropped. ${Ft} opens one image at a time; the desktop app has Batch.`),await $t(e[0]);return}m.set({tab:"vectorize"}),await $t(e[0])}async function Yr(){const e=document.getElementById("app");if(!e)return;e.append(hn,Qe,Gr),Ho(e),Xo(),Ls(),Lt(),m.on(["tab","source","prefs","caps"],Ls),m.on(["tab"],Lt),window.addEventListener("keydown",Ur),Ei(m),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>Pt(m.state.prefs?.theme??"system")),document.addEventListener("click",i=>{const r=i.target?.closest?.("a[href]"),l=r?.getAttribute("href")??"";r&&/^(https?:|mailto:)/i.test(l)&&(i.preventDefault(),It(l).catch(()=>W("Could not open the link in your browser.",{kind:"bad"})))});const t=Ma(()=>m.state.tracing,()=>m.state.traceStarted,()=>m.state.liveNow?.since??null);m.on(["tracing","liveNow","traceLog","liveStages","tab","railTab"],t),await ke.traceProgress(i=>{if(i.generation>zt){const r=Oe.get(i.generation)??{progress:[]};r.progress.push(i),Oe.set(i.generation,r);return}wo(i)}),await ke.traceDone(({generation:i,outcome:r})=>{if(i>zt){const l=Oe.get(i)??{progress:[]};l.done=r,Oe.set(i,l);return}i!==m.state.generation||!m.state.tracing||xo(r,i)}),await ke.openPath(i=>void Ps(i)),await ke.batchRow(i=>{const r=m.state.batch,l=r.rows.findIndex(c=>c.id===i.id);l>=0&&(r.rows[l]=i),m.touch("batch")}),await ke.batchTotals(i=>{m.state.batch.totals=i,m.touch("batch")}),await ke.batchFinished(i=>{const r=m.state.batch;r.rows=i,r.running=!1,r.paused=!1,m.touch("batch");const l=i.filter(c=>c.state==="failed").length;W(l?`Batch finished · ${l} failed. Sort failures first to see them.`:`Batch finished · ${i.filter(c=>c.state==="done").length} written.`,{kind:l?"bad":"good"})}),await ke.denoiserFetch(Jr);const[s,o]=await Promise.all([B.capabilities(),B.loadPrefs()]);m.set({caps:s,prefs:o,settings:o.trace}),Pt(o.theme),To(o),pa(m,i=>void qr(i)),an(`Engine ${s.engineVersion} · ${s.buildTarget}`,.55),bo=await B.listSamples(),m.touch("source"),an("Preparing the workspace…",.85),await jr(),an("Ready",1),B.appReady().catch(()=>{});const a=await B.launchPath().catch(()=>null);a&&Ps(a),Uo().then(Zr),o.checkUpdates&&B.checkUpdate().then(i=>m.set({update:i})).catch(()=>{})}async function Zr(e){e&&(m.set({tab:"vectorize",screen:null}),"sample"in e?await Ue(async()=>m.set({source:await B.openSample(e.sample)})):await Ue(async()=>m.set({source:await B.openBytes(e.bytes,e.name)})))}async function Ps(e){m.set({tab:"vectorize",screen:null}),await So(e)}function Ao(){const e=m.state.denoiserFetch;if(!m.state.caps?.denoiser.supported||e?.phase==="ready")return;const t=At(e,!0);W(e?.phase==="failed"?"The denoiser did not download. The trace runs without it.":`${zn(t??"Starting the denoiser…")} The trace shown is without it until it is ready, then it traces again by itself.`,e?.phase==="failed"?{kind:"bad",action:{label:"Retry",run:()=>ct.retryDenoiser()}}:{})}function Jr(e){const t=m.state.denoiserFetch?.phase;m.set({denoiserFetch:e});const s=m.state.caps;(e.phase==="stored"||e.phase==="ready")&&s&&!s.denoiser.installed&&B.denoiserStatus().then(a=>{const i=m.state.caps;i&&m.set({caps:{...i,denoiser:a}})});const o=m.state.settings.cleanUpDamage!=="off";e.retrace&&o&&m.state.source?(W("The denoiser is ready. Tracing again with it.",{kind:"good"}),Ee("final")):e.phase==="failed"&&t!=="failed"&&o&&W(`The denoiser did not download: ${e.message??"the connection dropped"}.`,{kind:"bad",action:{label:"Retry",run:()=>ct.retryDenoiser()}})}function an(e,t){B.startupProgress(e,t).catch(()=>{})}Yr().catch(e=>{B.appReady().catch(()=>{});const t=document.getElementById("app");t&&F(t,n("div.firstrun",null,n("span.serif",{style:{fontSize:"24px"}},`${Ft} could not start`),n("span.faint",{style:{maxWidth:"50ch",textAlign:"center"}},String(e))))});