diff --git "a/studio/assets/main-Bt_M4BWC.js" "b/studio/assets/main-Bt_M4BWC.js" deleted file mode 100644--- "a/studio/assets/main-Bt_M4BWC.js" +++ /dev/null @@ -1,15 +0,0 @@ -var no=Object.defineProperty;var so=(e,t,s)=>t in e?no(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var K=(e,t,s)=>so(e,typeof t!="symbol"?t+"":t,s);import{m as oo,i as yt,a as ao}from"./mark-DZ7OzD-p.js";var mn;(function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"})(mn||(mn={}));var bn;(function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"})(bn||(bn={}));var yn;(function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"})(yn||(yn={}));var wn;(function(e){e.Default="default",e.FluentOverlay="fluentOverlay"})(wn||(wn={}));var vn;(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"})(vn||(vn={}));var xn;(function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"})(xn||(xn={}));function n(e,t=null,...s){const[o,...a]=e.split("."),i=document.createElement(o||"div");return a.length&&(i.className=a.join(" ")),io(i,t),Yt(i,s),i}function ye(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 Yt(i,s),i}function io(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 Yt(e,t){for(const s of t.flat(8))s==null||s===!1||e.append(s instanceof Node?s:document.createTextNode(String(s)))}function N(e,...t){e.replaceChildren(),Yt(e,t)}const kn={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 W(e,t=16){const s=kn[e]??kn.info;return ye("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"},ye("path",{d:s}))}function os(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(oo,"image/svg+xml").documentElement;return document.importNode(e,!0)})();function lo(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)}),window.addEventListener("paste",i=>{if(i.target?.closest?.("input, textarea, [contenteditable]"))return;const l=[...i.clipboardData?.files??[]];l.length&&(i.preventDefault(),t(l))})}const co=760;function uo(e){const t=window.matchMedia(`(max-width: ${co}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 po(){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 as=!1,is=!1,Sn=!1;function ho(){return document.querySelector("#boot iframe")}function rs(e){ho()?.contentWindow?.postMessage(e,window.location.origin)}function fo(e,t){rs({type:"splash-status",text:e,progress:t})}function Gt(e){if(Sn||!(e||as&&is))return;Sn=!0;const t=document.getElementById("boot");rs({type:"splash-done"}),t?.classList.add("leaving"),window.setTimeout(()=>t?.remove(),300)}function go(){as=!0,Gt(!1),window.setTimeout(()=>Gt(!0),6e3)}window.addEventListener("message",e=>{e.origin!==window.location.origin||e.data?.type!=="splash-animation-done"||(is=!0,Gt(!1))});function mo(){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 Be(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)}const $n="inkvec-studio-lite:prefs",Mn="inkvec-denoiser-v1",Cn="Not available in the browser. Inkvec Studio, the desktop app, has it.",Tn=[["flat-logo.png","Flat logo"],["icon-64.png","Icon, 64 px"],["crest-filigree.png","Crest, filigree"],["signature-bw.png","Signature, B&W"]];class bo{constructor(){K(this,"worker",null);K(this,"ready",null);K(this,"queue",[]);K(this,"running",null);K(this,"nextId",1);K(this,"listeners",new Map);K(this,"generation",0);K(this,"previewGeneration",0);K(this,"shared",null);K(this,"source",null);K(this,"prefs",null);K(this,"prefsTimer",0);K(this,"denoiser",null);K(this,"denoiserPort",null);K(this,"downloading",!1);K(this,"info",null);K(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-Cn2MnOae.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==="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==="stage"?c.generation===this.generation&&this.emit("trace:stage",{generation:c.generation,name:c.name,ms:c.ms}):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:"385495021c9e",generations:this.shared?this.shared.buffer:null,denoiserPort:o},a),this.ready}whenReady(){return this.ready??this.start()}crashed(t){const s=this.running;this.running=null,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;this.whenReady().then(async()=>{o&&await this.enqueue("open_bytes",-1,{bytes:o.bytes.slice(),name:o.name}).catch(()=>{}),this.pump()})}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,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 fo(String(s.text??""),Number(s.progress??0)),null;case"app_ready":return go(),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(!Tn.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 Tn.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=await this.loadPrefs();return this.enqueue("trace",1,{request:o,draftPx:i.draftPx??512,draftSeconds:i.draftSeconds??.4,generation:a},"trace").then(r=>{r&&a===this.generation&&this.emit("trace:done",{generation:a,outcome:r})},r=>{a===this.generation&&!/superseded/.test(r.message)&&this.emit("trace:done",{generation:a,outcome:{state:"failed",message:r.message}})}),a}case"cancel_trace":return this.bump(),this.queue=this.queue.filter(o=>o.kind==="trace"?(o.resolve(null),!1):!0),null;case"start_preview":{const o=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 Be(a[0].name,a[0].data),[a[0].name];const r=`${i}-inkvec.zip`;return a[0]&&Be(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 Be(o.split(/[\\/]/).pop()||"download",new Uint8Array(s.bytes)),null}case"batch_scan":case"batch_start":case"batch_stats_csv":throw new Error(Cn);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.downloading&&(this.downloading=!1,this.emit("denoiser:done",{ok:!1,message:"Cancelled.",status:await this.denoiserStatus()})),null;case"denoiser_remove":try{await caches.delete(Mn)}catch{}return this.denoiserStatus();case"load_prefs":return this.loadPrefs();case"save_prefs":{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:Cn}}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.enqueue("open_bytes",0,{bytes:t,name:s})}async loadPrefs(){if(this.prefs)return this.prefs;let t=null;try{const s=localStorage.getItem($n);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($n,JSON.stringify(this.prefs))}catch{}}startDenoiser(t,s){if(this.denoiser||!this.denoiserPort)return;const o=new Worker(new URL(""+new URL("denoiser.worker-ER2QG0XM.js",import.meta.url).href,import.meta.url),{type:"module",name:"inkvec-denoiser"});this.denoiser=o,o.onmessage=async a=>{const i=a.data;this.downloading&&(i.type==="progress"&&this.emit("denoiser:progress",{got:i.got,total:i.total}),i.type==="done"&&(this.downloading=!1,this.emit("denoiser:done",{ok:i.ok,message:i.message,status:await this.denoiserStatus()})))},o.postMessage({type:"init",base:this.base(),token:"385495021c9e",url:t,sha256:s,port:this.denoiserPort},[this.denoiserPort])}async denoiserStatus(){const t=await this.whenReady();let s=!1,o=null;try{const a=await(await caches.open(Mn)).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.denoiser.postMessage({type:"download"}),null}}let nt=null;function ls(){return nt||(nt=new bo,globalThis.__inkvecStudioLite=nt),nt}function O(e,t){return ls().invoke(e,t)}function $e(e,t){return ls().listen(e,t)}const R={capabilities:()=>O("capabilities"),startupProgress:(e,t)=>O("startup_progress",{text:e,progress:t}),appReady:()=>O("app_ready"),openPath:e=>O("open_path",{path:e}),openBytes:(e,t)=>O("open_bytes",{bytes:e,name:t??null}),openSample:e=>O("open_sample",{name:e}),listSamples:()=>O("list_samples"),startTrace:(e,t)=>O("start_trace",{request:{settings:e,tier:t}}),cancelTrace:()=>O("cancel_trace"),startPreview:e=>O("start_preview",{request:{settings:e,tier:"draft"}}),cancelPreviews:()=>O("cancel_previews"),sourceFacts:e=>O("source_facts",{maxDim:e}),launchPath:()=>O("launch_path"),traceBands:e=>O("trace_bands",{generation:e}),snapInks:(e,t,s,o)=>O("snap_inks",{svg:e,snaps:t,width:s,height:o}),matchPalette:(e,t)=>O("match_palette",{traced:e,pasted:t}),planExport:e=>O("plan_export",{request:e}),writeExport:(e,t)=>O("write_export",{request:e,destination:t}),minify:(e,t)=>O("minify_svg",{svg:e,settings:t}),fabAnalyze:e=>O("fab_analyze",{svg:e}),fabPrepare:(e,t)=>O("fab_prepare",{svg:e,options:t}),readTextFile:e=>O("read_text_file",{path:e}),saveBytes:(e,t)=>O("save_bytes",{path:e,bytes:t}),batchScan:e=>O("batch_scan",{folder:e}),batchStart:e=>O("batch_start",{plan:e}),batchPause:e=>O("batch_pause",{paused:e}),batchCancel:()=>O("batch_cancel"),batchStatsCsv:()=>O("batch_stats_csv"),denoiserStatus:()=>O("denoiser_status"),denoiserDownload:()=>O("denoiser_download"),denoiserCancel:()=>O("denoiser_cancel"),denoiserRemove:()=>O("denoiser_remove"),loadPrefs:()=>O("load_prefs"),savePrefs:e=>O("save_prefs",{prefs:e}),resetPrefs:()=>O("reset_prefs"),thirdPartyNotices:()=>O("third_party_notices"),checkUpdate:()=>O("check_update"),cliStatus:()=>O("cli_status"),installCli:()=>O("install_cli"),removeCli:()=>O("remove_cli"),contextMenuStatus:()=>O("context_menu_status"),installContextMenu:()=>O("install_context_menu"),removeContextMenu:()=>O("remove_context_menu")},Me={traceStage:e=>$e("trace:stage",t=>e(t.payload)),traceDone:e=>$e("trace:done",t=>e(t.payload)),previewDone:e=>$e("preview:done",t=>e(t.payload)),openPath:e=>$e("open:path",t=>e(t.payload)),batchRow:e=>$e("batch:row",t=>e(t.payload)),batchTotals:e=>$e("batch:totals",t=>e(t.payload)),batchFinished:e=>$e("batch:finished",t=>e(t.payload)),denoiserProgress:e=>$e("denoiser:progress",t=>e(t.payload)),denoiserDone:e=>$e("denoiser:done",t=>e(t.payload))};function yo(e,t){return{caps:null,prefs:null,tab:"vectorize",screen:null,source:null,settings:e,preset:"logo",generation:0,tracing:!1,tracingTier:"final",liveStages:[],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:wo(),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,auto:null,facts:null,chooser:!1,wizard:null,compare:null,autoNoteHidden:!1}}function wo(){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 vo{constructor(t){K(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 ne(e){return e.toLocaleString("en-US")}function ae(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function ie(e){return e==null?"—":e.toFixed(2)}function Te(e){return e<1?`${Math.round(e*1e3)} ms`:`${e.toFixed(2)} s`}function Zt(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 ve(e,t=0){return`${(e*100).toFixed(t)}%`}function xo(e){const t=Math.max(0,Math.round(e));return t<60?`${t} s`:`${Math.floor(t/60)} min ${t%60} s`}function cs(e){return e==="macos"?"⌘":"Ctrl"}const ko="modulepreload",So=function(e,t){return new URL(e,t).href},An={},$o=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=So(p,o),p in An)return;An[p]=!0;const h=p.endsWith(".css"),f=h?'[rel="stylesheet"]':"";if(!!o)for(let m=l.length-1;m>=0;m--){const k=l[m];if(k.href===p&&(!h||k.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${p}"]${f}`))return;const g=document.createElement("link");if(g.rel=h?"stylesheet":ko,h||(g.as="script"),g.crossOrigin="",g.href=p,d&&g.setAttribute("nonce",d),document.head.appendChild(g),h)return new Promise((m,k)=>{g.addEventListener("load",m),g.addEventListener("error",()=>k(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 Mo(e={}){return typeof e=="object"&&Object.freeze(e),await yt("plugin:dialog|open",{options:e})}async function Co(e={}){return typeof e=="object"&&Object.freeze(e),await yt("plugin:dialog|save",{options:e})}async function To(e,t){await yt("plugin:opener|open_url",{url:e,with:t})}async function Ao(e){return yt("plugin:opener|reveal_item_in_dir",{paths:typeof e=="string"?[e]:e})}const wt="Inkvec Studio Lite",us="https://github.com/logolabs/inkvec/releases",zo="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 ds(e){return{name:e.name||"pasted image",path:null,file:e}}async function Jt(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(ds))),o.addEventListener("cancel",()=>i([])),document.body.append(o),o.click()})}async function gt(e){if(e.file)return e.file.text();if(e.path)return R.readTextFile(e.path);throw new Error("nothing to read")}const zn=!1;async function ps(e,t,s){const o=typeof t=="string"?new TextEncoder().encode(t):t;return Be(e,o),{path:null,name:e}}async function Lo(e,t,s){const o=new TextEncoder,a=i=>typeof i=="string"?o.encode(i):i;{if(e.length===1)Be(e[0].name,a(e[0].data));else{const{zipStore:i}=await $o(async()=>{const{zipStore:r}=await import("./zip-DlX9-WSC.js");return{zipStore:r}},[],import.meta.url);Be(t,i(e.map(r=>({name:r.name,data:a(r.data)}))))}return{folder:null,first:null}}}const Po=!1;async function vt(e){await navigator.clipboard.writeText(e)}async function xt(e){{window.open(e,"_blank","noopener,noreferrer");return}}function kt(e){return e&&Po?{label:"Show in folder",run:()=>void 0}:void 0}function Ae(e){const t={...e,cleanUpDamage:"off"};return JSON.stringify(Object.keys(t).sort().map(s=>[s,t[s]]))}class Eo{constructor(){K(this,"results",new Map);K(this,"queue",[]);K(this,"running",null);K(this,"epoch",0);K(this,"early",new Map);K(this,"listeners",new Set);Me.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&&R.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(),R.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":"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 Xt=()=>document.getElementById("overlays"),Do=()=>document.getElementById("toasts");function q(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,W(o,16)),n("span.text",null,e),t.action&&n("button",{class:"reset",onclick:()=>{t.action?.run(),a.remove()}},t.action.label));Do().append(a),window.setTimeout(()=>a.remove(),6e3)}function le(){N(Xt()),document.removeEventListener("keydown",Kt,!0)}function Kt(e){e.key==="Escape"&&(e.stopPropagation(),le())}function We(e){const t=n("div.scrim",{onmousedown:s=>{s.target===t&&le()}});e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),t.append(e),N(Xt(),t),document.addEventListener("keydown",Kt,!0),(e.querySelector("button, input, [tabindex]")??e).focus()}function St(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 Qt(e,t){const s=n("div.popover",null,t),o=n("div",{style:{position:"fixed",inset:"0",zIndex:"44"},onmousedown:le});N(Xt(),o,s),document.addEventListener("keydown",Kt,!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 Ee=null,en=0;function Ke(){window.cancelAnimationFrame(en),Ee?.node.remove(),Ee=null}function hs(){const e=Ee;if(!e)return;if(!(e.el.isConnected&&(e.el.matches(":hover")||e.el.matches(":focus-visible")))){Ke();return}en=window.requestAnimationFrame(hs)}function Je(e,t){let s=0;const o=()=>{if(!e.isConnected||Ee?.el===e)return;Ke();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`,Ee={node:i,el:e},en=window.requestAnimationFrame(hs)},a=()=>{window.clearTimeout(s),Ee?.el===e&&Ke()};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"&&Ee&&Ke()},!0);window.addEventListener("blur",Ke);function No(e,t,s,o,a=!1){We(St(e,[t],[n("button.btn",{onclick:le},"Cancel"),n(`button.btn.primary${a?".danger":""}`,{onclick:()=>{le(),o()}},s)]))}function fs(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 gs([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 Fo(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,m=Math.hypot(u,a),k=Math.hypot(g,l),T=(ue,ke)=>{if(ue===0&&ke===0)return 0;const de=Math.atan2(ke,ue)/c;return de<0?de+360:de},w=T(u,a),$=T(g,l),v=i-s,M=k-m;let A=0;m*k!==0&&(A=$-w,A>180?A-=360:A<-180&&(A+=360));const L=2*Math.sqrt(m*k)*Math.sin(A*c/2),x=(s+i)/2,C=(m+k)/2;let _=w+$;m*k!==0&&(_=Math.abs(w-$)<=180?(w+$)/2:w+$<360?(w+$+360)/2:(w+$-360)/2);const G=1-.17*Math.cos((_-30)*c)+.24*Math.cos(2*_*c)+.32*Math.cos((3*_+6)*c)-.2*Math.cos((4*_-63)*c),V=30*Math.exp(-(((_-275)/25)**2)),se=C**7,xe=2*Math.sqrt(se/(se+25**7)),ee=1+.015*(x-50)**2/Math.sqrt(20+(x-50)**2),J=1+.045*C,Y=1+.015*C*G,H=-Math.sin(2*V*c)*xe,te=v/ee,Q=M/J,oe=L/Y;return Math.sqrt(te*te+Q*Q+oe*oe+H*Q*oe)}function mt(e){return e.kind==="gradient"&&e.stops.length>1?e.stops.map(t=>t.toLowerCase()).join(">"):e.traced.toLowerCase()}function Oe(e){return e.split(">").map(t=>t.trim().toLowerCase())}function Qe(e){return e.includes(">")}function tn(e,t=!1){const s=Oe(e);return s.length<2?s[0]??"transparent":t?`radial-gradient(circle, ${s.join(", ")})`:`linear-gradient(90deg, ${s.join(", ")})`}const st=new Map;function Ln(e){const t=st.get(e);if(t)return t;const s=fs(e);if(!s)return null;const o=gs(s);return st.size>4096&&st.clear(),st.set(e,o),o}function nn(e,t){const s=Oe(e).map(Ln),o=Oe(t).map(Ln);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=>Fo(p,h))),0)/l.length;return Math.max(r(a,i),r(i,a))}const Io=2;function $t(e,t){const s=Qe(e),o=t.find(r=>mt(r)===e);if(o)return o;let a=null,i=Io;for(const r of t){if(r.kind==="gradient"!==s)continue;const l=nn(e,mt(r));l<=i&&(i=l,a=r)}return a}function we(e,t){return e===t||nn(e,t)<.5}const _o=2,Bo=240;function Oo(e,t={}){const s=t.skip??[],o=e.filter(p=>p.share>0),a=[...o].sort((p,h)=>h.share-p.share).slice(0,Bo).map(p=>({member:mt(p),share:p.share})).filter(p=>!s.some(h=>we(h,p.member))),i=a.length;if(i<2)return[];const r=a.map((p,h)=>a.map((f,u)=>h===u?0:nn(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;u_o)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 sn(e){return[...e.members].sort().join(",")}function Ro(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 Go(e,t){return e?e.unmatched.some(s=>s===t||Oe(t).includes(s)||we(s,t)):!1}function ms(e){const t=(e.groupSuggestions??[]).filter(a=>a.edited),s=[...e.colourGroups.flatMap(a=>a.members),...t.flatMap(a=>a.members)],o=Oo(e.palette,{count:e.simplifyTo,skip:s}).filter(a=>!e.dismissedGroups.includes(sn(a)));return[...t,...o]}function Ho(e,t){const s=e.members.filter(a=>$t(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 on(e){return(e.groupSuggestions??[]).map((t,s)=>({g:Ho(t,e.palette),index:s})).filter(t=>t.g!==null)}function bs(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 ge(e,t,s=-1,o=!1){return e.map((a,i)=>{if(i===s)return a;const r=a.members.findIndex(l=>we(l,t));return r<0?a:{...bs(a,r),...o?{edited:!0}:{}}})}const Re=e=>e.filter(t=>t.members.length>1);function qo(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"&&we(c?.dataset.member??"",s))return;if(d==="group")r=ge(r,s,p),l=ge(l,s,-1,!0),r[p].members.some(f=>we(f,s))||r[p].members.push(s);else if(d==="sugg")r=ge(r,s),l=ge(l,s,p,!0),l[p].members.some(f=>we(f,s))||(l[p]={...l[p],members:[...l[p].members,s],edited:!0});else if(d==="ink"){const f=c?.dataset.member??"";r=ge(r,s),l=ge(l,s,-1,!0);const u=r.findIndex(g=>g.members.some(m=>we(m,f)));u>=0?r[u].members.push(s):(l=ge(l,f,-1,!0),r.push({members:[f,s],target:null}))}else{if(o.kind==="palette")return;o.kind==="group"?r=ge(r,s):l=ge(l,s,-1,!0)}const h=Re(r);e.set({groupSuggestions:Re(l),paletteSelection:[]}),JSON.stringify(h)!==JSON.stringify(i.colourGroups)&&t.setColourGroups(h)}function Wo(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=>we(d,c))));let r=s.groupSuggestions??[];for(const l of o)a=ge(a,l,i),r=ge(r,l,-1,!0);if(i>=0)for(const l of o)a[i].members.some(c=>we(c,l))||a[i].members.push(l);else a.push({members:[...o],target:null});e.set({groupSuggestions:Re(r),paletteSelection:[]}),t.setColourGroups(Re(a))}const Uo=4;function ys(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){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 m=k=>k.stopPropagation();window.addEventListener("click",m,!0),window.setTimeout(()=>window.removeEventListener("click",m,!0),0),g.type!=="pointercancel"&&qo(t,s,o,a,document.elementFromPoint(g.clientX,g.clientY))};window.addEventListener("pointermove",f),window.addEventListener("pointerup",u),window.addEventListener("pointercancel",u)})}function ot(e,t){return $t(e,t)?.keys??[]}function ws(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 vs(e,t){const s=e.state,o=on(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"}},W("palette",12),`Palette · ${s.palette.length} ink${s.palette.length===1?"":"s"}`),n("button.reset",{disabled:!a.length,onclick:()=>ea(e,t)},"Paste brand palette")),s.colourGroups.length?jo(e,t):null,s.palette.length?Zo(e,t,o):null,n("div.inklist",{"data-drop":"list","aria-label":"Inks"},...s.palette.map(r=>Jo(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:()=>Wo(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 Qo(a)},"Copy all as CSS")):n("span.muted",{style:{fontSize:"11px"}},"Trace an image to see its inks."))}function Pn(e,t=!1){return n(`span.swatch.mini${Qe(e)?".ramp":""}`,null,n("i",{style:{background:tn(e,t)}}))}function At(e){const t=Oe(e);return t.length>1?`${t.length} stops`:t[0].toUpperCase()}function jo(e,t){const s=e.state,a=JSON.stringify(s.resultGroups)===JSON.stringify(s.colourGroups)&&s.result?Ro(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)=>xs(e,t,i,r,"group",a[r]??null)))}function Vo(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:Qe(a)?"extends this gradient":"becomes this"}}let s=e.members[0],o=-1;for(const a of e.members){const i=$t(a,t)?.share??0;i>o&&(o=i,s=a)}return{member:s,label:"most-used"}}function xs(e,t,s,o,a,i){const r=e.state,l=r.palette,c=k=>$t(k,l)?.gradient==="radial",d=Vo(s,l),p=[...new Set([...s.members,d.member].flatMap(k=>ot(k,l)))],h=k=>{if(a==="group"){const T=r.colourGroups.map((w,$)=>$===o?k:w);t.setColourGroups(Re(T))}else{const T=(r.groupSuggestions??[]).map((w,$)=>$===o?{...k,edited:!0}:w);e.set({groupSuggestions:Re(T)})}},f=k=>Go(i,k),u=k=>{const T=ot(k,l);return T.length||a==="sugg"?T:ot(d.member,l)},g=s.members.map((k,T)=>{const w=s.target===`@${T+1}`,$=n(`div.cchip${w?".target":""}${f(k)?".unmatched":""}`,{"data-fill":JSON.stringify(u(k))},n("button.cchipmain",{"data-ctl":`${a}${o}:${k}`,"aria-pressed":String(w),title:w?"The group becomes this. Press again for the most-used colour.":Qe(k)?"Make the group this gradient: it is extended over the other members":"Make the group this colour",onclick:()=>h({...s,target:w?null:`@${T+1}`})},Pn(k,c(k)),n("span.num",null,At(k))),n("button.cchipx",{"data-ctl":`${a}${o}:${k}:x`,"aria-label":`Take ${At(k)} out of the group`,title:"Take it out of the group",onclick:()=>h(bs(s,T))},W("x",11)));return ys($,e,t,k,{kind:a,index:o},c(k)),$}),m=n("button.reset",{title:"Paint the whole group one colour of your choosing",onclick:k=>Xo(k.currentTarget,d.member,T=>h({...s,target:T}))},"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(ot(d.member,l))},Pn(d.member,c(d.member)),n("span.num",null,At(d.member))),n("span.muted",null,d.label),n("span",{style:{flex:"1"}}),m),a==="sugg"?n("div.cactions",null,n("button.btn.compact.primary",{onclick:()=>{const k=(r.groupSuggestions??[]).filter((T,w)=>w!==o);e.set({groupSuggestions:k}),t.setColourGroups([...r.colourGroups,{members:s.members,target:s.target}])}},"Accept"),n("button.btn.compact",{onclick:()=>e.set({groupSuggestions:(r.groupSuggestions??[]).filter((k,T)=>T!==o),dismissedGroups:[...r.dismissedGroups,sn(s)]})},"Dismiss")):[Yo(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((k,T)=>T!==o))},"Ungroup"))])}function Yo(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},W("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=>Qe(o)?"a gradient":o.toUpperCase()).join(", ");return n("span.creport.warn",{title:`${e.line}; not found: ${e.unmatched.join(", ")}`},W("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 Zo(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:ms({...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)},W("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})=>xs(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 Jo(e,t,s){const o=e.state,a=mt(s),i=s.kind==="gradient",r=s.snappedDe00!==null,l=o.paletteSelection.includes(a),c=o.colourGroups.some(f=>f.members.some(u=>we(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||Ko(f.currentTarget,s,t)}},n(`span.swatch${r?".snapped":""}${i?".ramp":""}`,null,n("i",{style:{background:i?tn(a,s.gradient==="radial"):s.hex}})),n("span.hex",null,i?s.gradient==="radial"?"radial":"linear":s.hex.toUpperCase()),n("span.share",null,ve(s.share)),n(`span.snap${r?".on":""}`,null,i?`${s.stops.length} stops`:r?`snapped · ΔE ${ie(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?W("check",11):null),d);return ys(h,e,t,a,{kind:"palette"},s.gradient==="radial"),h}function Xo(e,t,s){const o=Oe(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)){q(`${a.value} is not a colour; write it as #RRGGBB.`,{kind:"bad"});return}s(c),le()};Qt(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 Ko(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.");Qt(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()} · ${ve(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()}]),le()}},"Snap")),i))}async function Qo(e){const t=e.map((s,o)=>` --ink-${o+1}: ${s.hex.toLowerCase()}; /* ${ve(s.share)} of canvas */`).join(` -`);try{await vt(`:root { -${t} -} -`),q(`${e.length} ink${e.length===1?"":"s"} copied as CSS.`,{kind:"good"})}catch(s){q(String(s),{kind:"bad"})}}function ea(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 R.matchPalette(s.map(l=>l.traced),o.value),N(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":`${ie(l.de00)} dE00`))))};o.addEventListener("input",()=>void r()),We(St("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:le},"Cancel"),n("button.btn.primary",{onclick:()=>{t.snap(i.map(l=>({from:l.from,to:l.to}))),le()}},`Snap ${s.length} inks`)]))}const ks=2.5,ta=256;function Ss(e,t){return!!e?.lossy||t!==null&&t.noiseLevels>=ks}function na(e){return e.filter(t=>t.share>=.005)}function sa(e){if(e.length!==2||e.some(l=>l.kind==="gradient"))return!1;const t=e.map(l=>fs(l.hex)).map(l=>l?gs(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 oa(e){const{source:t,report:s,palette:o,losses:a,facts:i,settings:r}=e;if(!t||!s)return[];const l=[];if(Ss(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=na(o);!r.blackAndWhite&&sa(c)&&l.push({id:"bw",preset:"black-and-white",title:"Two colours, black and white",evidence:`${c.length} inks cover the canvas`,action:"Black & white"}),!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<=ta&&!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 ze(e,t,s){return(e?.controls??[]).filter(o=>t[o.key]!==s[o.key]).map(o=>o.key)}function $s(e,t,s){const o=e?.presets.find(a=>a.id===s);if(o){const a=ze(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?oa({source:t.source,report:s.report,palette:s.palette,losses:s.losses,facts:t.facts,settings:s.settings,preset:s.preset}):[]}function Ms(e){const t=e.state;return t.auto?t.preset!==t.auto.preset||ze(t.caps,t.settings,t.auto.settings).length>0:!1}function aa(e){const t=e.state;return t.auto?.report?Ms(e)||on(t).length>0||an(e).length>0:!1}function Cs(e,t,s=!1){const o=e.state,a=o.auto;if(!a?.report)return null;const i=an(e),r=on(o),l=Ms(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})=>sn(h))]})},"Dismiss"))))}return n("div.autochose",null,s?null:n("span.autolead",null,`Auto traced with ${$s(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 ia=new Set(["cleanUpDamage","editability"]),zt={axisHandles:.34,smoothJoins:.89,alignedNodes:.86};function ra(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,N(a,...u.railTab==="result"?[u.tracing?ya(e):null,...fa(e,t)]:ua(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){const m=u.liveStages[u.liveStages.length-1];g.querySelector(".stage").textContent=m?.name??"starting",g.querySelector(".elapsed").textContent=Te(u.liveStages.reduce((k,T)=>k+T.ms,0)/1e3);return}N(i,...Ma(e,t))},h=()=>{const u=document.activeElement instanceof HTMLElement&&o.contains(document.activeElement)?document.activeElement.getAttribute("data-ctl"):null;N(o,...la(e,t)),u&&o.querySelector(`[data-ctl="${u}"]`)?.focus({preventScroll:!0})},f=()=>{N(s,...ca(e,t)),h(),d(),p()};return e.on(["caps","prefs","preset","settings","railTab","groupsOpen"],f),e.on(["tracing","liveStages","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=As(e))}),ws(a,e),f(),r}function la(e,t){const s=e.state,o=f=>s.caps?.controls.find(u=>u.key===f)?.help??"",a=s.caps?.denoiser,i=s.settings.cleanUpDamage,r=a?.supported??!1,l=r&&a!==void 0&&!a.installed&&i!=="off",c={off:"pixels as they are",auto:"only if damaged",on:"always"},d=n("div.mode",null,n("div.modehead",null,Je(n("span.modetitle",{tabindex:"0"},"Denoiser"),o("cleanUpDamage")),l?n("button.reset",{onclick:t.openDenoiser,title:"It runs on this computer; nothing is uploaded"},"Download it"):n("span.modestate",null,r?c[i]:"needs its own tab")),n("div.seg.big",{role:"group","aria-label":"Denoiser"},...["off","auto","on"].map(f=>n("button",{"aria-pressed":String(i===f),disabled:!r,"data-ctl":`cleanUpDamage:${f}`,onclick:()=>t.changeSetting("cleanUpDamage",f)},f==="off"?"Off":f==="auto"?"Auto":"On")))),p=s.settings.editability,h=n("div.mode",null,n("div.modehead",null,Je(n("span.modetitle",{tabindex:"0"},"Editable"),o("editability"))),n("button.bigswitch",{role:"switch","aria-checked":String(p),"aria-label":"Editable structure","data-ctl":"editability:switch",onclick:()=>t.changeSetting("editability",!p)},n("span.knob"),n("span.state",null,p?"On":"Off")));return[d,h]}function Ts(e,t){const s=t.baseSettings(),o=e.state;return(o.caps?.controls??[]).filter(a=>o.settings[a.key]!==s[a.key]).length}function As(e){const t=e.state.report;return t?`${ie(t.meanDe00)} dE00`:""}function ca(e,t){const s=e.state,o=Ts(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",As(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 ua(e,t){return[da(e,t),pa(e,t),...$a(e,t)]}function da(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 pa(e,t){const s=e.state,o=s.caps?.presets??[],a=s.prefs?.saved??[],i=cs(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:m=>{m.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=Ts(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:()=>ha(e,t)},"Save as preset")))}function ha(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)),le()};We(St("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:le},"Cancel"),n("button.btn.primary",{onclick:a},"Save")])),o.focus(),o.addEventListener("keydown",i=>{i.key==="Enter"&&a()})}function fa(e,t){return e.state.source?[ga(e,t),wa(e,t),xa(e,t),ka(e),vs(e,t),ba()]:[ma()]}function ga(e,t){const s=e.state;if(s.chooser||s.wizard||s.autoNoteHidden||!aa(e))return null;const o=Cs(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 ma(){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 ba(){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 ya(e){const t=e.state,s=t.caps?.stages??[],o=new Map(t.liveStages.map(i=>[i.name,i.ms])),a=t.liveStages.reduce((i,r)=>i+r.ms,0)/1e3;return n("div.card",null,n("div.cardhead",null,n("span",{style:{fontSize:"12px",fontWeight:"500"}},`Tracing at ${Zt(t,t.tracingTier)??"—"} px`),n("span.muted.num",{style:{fontSize:"11px"}},Te(a))),n("div.sweep",null,n("i")),n("div.stagelist",null,...s.map(i=>{const r=o.get(i);return 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 wa(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,ie(o?.meanDe00)),n("div.of",null,n("span.dim",{style:{fontSize:"11.5px"}},"mean colour difference"),n("span.muted.num",{style:{fontSize:"11px"}},`median ${ie(o?.medianDe00)} · dE00`))),n("div",{style:{display:"flex",flexDirection:"column",gap:"5px"}},n("div.meter",{role:"img","aria-label":`Colour difference ${ie(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,...va(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 · ${ie(s.worstCorner.de00)} dE00 →`):null)}function va(e){const t=e.state.report;return t?[["coordinates",ne(t.coordinates)],["paths",ne(t.paths)],["colours found",ne(t.colours)],["segments",ne(t.segments)],["file size",ae(t.bytes)],["minified",t.minifiedBytes===null?"already":ae(t.minifiedBytes)],["traced at",`${t.tracedPx} px`],["time taken",Te(t.seconds)]]:[["coordinates","—"],["paths","—"],["colours found","—"],["segments","—"],["file size","—"],["minified","—"]]}function xa(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:zt.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:zt.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:zt.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,ve(d),n("span.of",null,`${c.part} of ${c.whole}`)),n("div.sbar",{role:"img","aria-label":`${c.label}: ${ve(d)}, ${c.part} of ${c.whole}; hand-drawn files ${ve(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 ka(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=>Sa(o,s(o.kind))),n("div.nothinglost",null,n("span.glyph",null,W("checkCircle",15)),n("span",null,t.losses.length?"Nothing else we can detect was lost.":"Nothing we can detect was lost.")))}function Sa(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,W(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 $a(e,t){const s=e.state,o=(s.caps?.controls??[]).filter(r=>!ia.has(r.key)),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")}},W(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=>zs(e,p,t,a))):null)})}function zs(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,Je(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,Je(n("span.label",{tabindex:"0"},t.label),t.help),n("div.seg",null,...u.map(([g,m])=>n("button",{"aria-pressed":String(a===g),"data-ctl":`${t.key}:${g}`,onclick:()=>s.changeSetting(t.key,g)},m)))))}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,m=h(g);p.value=d(m),g.setAttribute("aria-valuetext",`${d(m)} ${t.unit}`.trim())},onchange:u=>s.changeSetting(t.key,h(u.target))});return r(n("div.controlhead",null,Je(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 Ma(e,t){const s=e.state,o=!!s.svg&&!s.tracing;return[Ca(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"},W("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 Ca(e,t){const s=e.state,o=s.report;if(s.tracing){const r=s.liveStages[s.liveStages.length-1],l=s.liveStages.reduce((c,d)=>c+d.ms,0)/1e3;return n("div.readout.busy",null,n("span.pulse"),n("span.stage",null,r?.name??"starting"),n("span.muted.num.elapsed",null,Te(l)),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=(r,l,c)=>n("div.cell",null,n("span.v.num",null,r),n("span.k",null,l),c);return n("div.readout",{"aria-live":"polite"},i(ie(o.meanDe00),"dE00",a?Lt(o.meanDe00,a.meanDe00,r=>r.toFixed(2),.005,!0):null),i(ne(o.coordinates),"coordinates",a?Lt(o.coordinates,a.coordinates,ne,0,!1):null),i(ae(o.bytes),"file",a?Lt(o.bytes,a.bytes,ae,16,!1):null))}function Lt(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 Ta="https://logolabs.github.io/inkvec/";function Aa(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){const h=d.liveStages[d.liveStages.length-1],f=d.liveStages.reduce((u,g)=>u+g.ms,0)/1e3;return`Tracing now · ${h?.name??"starting"} · ${Te(f)}`}const p=d.auto?.report;return p?`Done · ${ie(p.meanDe00)} dE00 · ${ne(p.coordinates)} coordinates · ${ae(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){N(o);return}const h=!d.prefs?.seenFirstRun,f=Cs(e,t);N(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")},W("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,W("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,W("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","generation"],c),l(),o}const at={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"]}},En={detail:["precision","speckleFloor"],shape:["editability","fewerPaths","matchRepeatedShapes","bezierCost","cornerAngle"],output:["transparentBackground","margin","minify"]},za=[{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:"black-and-white",name:"Black & white",why:"Two inks: stamps, signatures, scans of print."},{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 La(e){const t=e.state,s=!!t.caps?.denoiser.supported&&Ss(t.source,t.facts);return["kind","cleanup","colours","detail","shape","output"].filter(o=>o!=="cleanup"||s)}function Pa(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"),m=n("div.wizguide"),k=n("div.wizscroll",null,f,u,g,m),T=n("div.wiznav");ws(g,e);const w=()=>La(e),$=()=>e.state.wizard?.step??"kind",v=S=>{S!==$()&&($()==="kind"&&o.pause(),e.set({wizard:{step:S},hoverFill:null}),k.scrollTop=0)},M=()=>{const S=e.state.auto?.svg;return c!=="auto"||!S?null:(d?.svg!==S&&(d={svg:S,label:"Auto"}),d)},A=()=>e.set({compare:M()}),L=()=>{const S=e.state.auto?.svg;return S?(p?.svg!==S&&(p&&URL.revokeObjectURL(p.url),p={svg:S,url:URL.createObjectURL(new Blob([S],{type:"image/svg+xml"}))}),p.url):null},x=()=>{const S=w(),D=Math.max(0,S.indexOf($())),P=at[$()],U=r!==null&&(e.state.preset!==r.preset||ze(e.state.caps,e.state.settings,r.settings).length>0||JSON.stringify(e.state.colourGroups)!==JSON.stringify(r.colourGroups));N(h,n("div.cardhead",null,n("span.eyebrow",null,`Custom · step ${D+1} of ${S.length}`),n("div",{style:{display:"flex",gap:"10px",alignItems:"baseline"}},U?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:()=>Fe(!0)},"Close"))),n("h2.wiztitle.serif",null,P.title),n("div.wizdots",{role:"tablist","aria-label":"Steps"},...S.map((j,I)=>n("button",{role:"tab","aria-selected":String(j===$()),"aria-label":`Step ${I+1}: ${at[j].title}`,title:at[j].title,class:Iv(j)}))),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",A(),x()}},"Auto's trace"),n("button",{"aria-pressed":String(c==="source"||!e.state.auto?.svg),onclick:()=>{c="source",A(),x()}},"Source"))))},C=()=>{const S=at[$()];N(f,n("p.wizlead",null,S.does)),N(m,n("dl.wizexplain",null,n("dt",null,"Change it when"),n("dd",null,S.when),n("dt",null,"What it costs"),n("dd",null,S.costs)),n("a.wizlearn",{href:Ta+S.learn[1]},`Learn more: ${S.learn[0]} →`))},_=()=>{const S=w(),D=S.indexOf($()),P=D===S.length-1;N(T,n("button.btn",{"data-ctl":"wiz-back",disabled:D<=0,onclick:()=>v(S[D-1])},"Back"),P?null:n("button.reset",{"data-ctl":"wiz-skip",title:"Keep what is chosen so far and go to the last step",onclick:()=>v(S[S.length-1])},"Skip to finish"),n("span",{style:{flex:"1"}}),P?n("button.btn",{"data-ctl":"wiz-export",disabled:!e.state.svg,onclick:()=>Fe(!0,!0)},"Export now"):null,P?n("button.btn.primary",{"data-ctl":"wiz-finish",onclick:()=>Fe(!0)},"Finish"):n("button.btn.primary",{"data-ctl":"wiz-next",onclick:()=>v(S[D+1])},"Next"))},G=()=>{if(!a)return;const S=w();if(!S.includes($())){e.set({wizard:{step:S[Math.min(S.length-1,1)]}});return}x(),C(),_(),xe(),H()},V=S=>e.state.caps?.controls.find(D=>D.key===S),se=S=>{const D=s.baseSettings();return S.map(P=>V(P)).filter(P=>!!P).map(P=>zs(e,P,s,D))},xe=()=>{const S=document.activeElement instanceof HTMLElement&&u.contains(document.activeElement)?document.activeElement.getAttribute("data-ctl"):null,D=e.state,P=$();let U=[];P==="cleanup"?U=J():P==="colours"?U=se(D.facts?.hasAlpha?["maxColours","traceTransparency"]:["maxColours"]):P==="detail"?U=[ee(),...se(En.detail??[])]:(P==="shape"||P==="output")&&(U=se(En[P]??[])),N(u,...U),u.hidden=U.length===0,S&&u.querySelector(`[data-ctl="${S}"]`)?.focus({preventScroll:!0}),a&&x()},ee=()=>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(S=>n("button",{"aria-pressed":String(e.state.view===S),"data-ctl":`wiz-view:${S}`,onclick:()=>e.set({view:S})},S==="side"?"Side by side":"Wipe")))),J=()=>{const S=e.state,D=S.caps?.denoiser,P=S.settings.cleanUpDamage,U=[S.source?.lossy?`the file is a ${S.source.container}, which stores colour approximately`:null,S.facts&&S.facts.noiseLevels>=ks?`its pixels measure ${S.facts.noiseLevels.toFixed(1)} levels of noise (a clean file reads about 0.5)`:null].filter(Boolean),j=(I,b,z)=>n("button.wizchoice",{"aria-pressed":String(P===I||I==="auto"&&P==="on"),"data-ctl":`wiz-cleanup:${I}`,onclick:()=>s.changeSetting("cleanUpDamage",I)},n("span.name",null,b),n("span.muted",null,z));return[U.length?n("p.wizwhy",null,`Offered because ${U.join(", and ")}.`):null,n("div.wizchoices",null,j("off","Off","Trace the pixels as they are, damage and all."),j("auto","Auto","Clean the damage first, where it is found.")),D&&!D.installed&&P!=="off"?n("div.wiznote",null,n("span",null,`The denoiser is not downloaded yet. It is a one-time download from ${D.repo}, checked against its SHA-256 on arrival, and it runs on this computer: the model comes down, your image never goes up. Until then the trace runs without it.`),n("button.btn.compact",{"data-ctl":"wiz-download",onclick:s.openDenoiser},"Download the denoiser…")):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 Y=null;const H=()=>{const S=$();if(S==="kind"){Ce();return}Y=null;const D=e.state;S==="colours"?N(g,D.palette.length?vs(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.")):S==="detail"||S==="shape"?N(g,te()):S==="output"?N(g,Q(),te()):N(g)},te=()=>{const S=e.state,D=S.auto?.report??null,P=S.report,U=S.result?.tier==="draft",j=b=>b?b.structure?.nodes||Math.round(b.coordinates/2):null,I=(b,z,E,F,B=!0,X=0)=>{const Z=z!==null&&E!==null&&!U&&!S.tracing?E-z:null,he=Z===null||Math.abs(Z)<=X?"same":Z<0===B?"better":"worse";return n("tr",null,n("th",null,b),n("td.num",null,z===null?"—":F(z)),n("td.num",null,E===null?"—":F(E)),n(`td.num.delta.${he}`,null,Z===null?"":Math.abs(Z)<=X?"same":`${Z<0?"−":"+"}${F(Math.abs(Z))}`))};return n("div.card.wizreadout",{"aria-live":"polite"},n("div.cardhead",null,n("span.eyebrow",null,"Auto · yours"),n("span.muted",{style:{fontSize:"11px"}},S.tracing?"tracing…":U?"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",D?.meanDe00??null,P?.meanDe00??null,b=>b.toFixed(2),!0,.005),I("nodes",j(D),j(P),ne),I("paths",D?.paths??null,P?.paths??null,ne),I("file size",D?.bytes??null,P?.bytes??null,ae,!0,16))),n("span.muted.wizsmall",null,"Colour difference in dE00: under 1 is invisible side by side."))},Q=()=>{const S=e.state,D=S.auto,P=D?ze(S.caps,D.settings,S.settings):[],U=(I,b)=>typeof b=="boolean"?b?"on":"off":typeof b=="number"?`${I.decimals?b.toFixed(I.decimals):Math.round(b)}${I.unit?` ${I.unit}`:""}`:String(b),j=S.caps?.presets.find(I=>I.id===S.preset);return n("div.card.wizchanges",null,n("span.eyebrow",null,"What you changed from Auto"),j&&D&&j.id!==D.preset?n("div.wizchange",null,n("span",null,"Preset"),n("span.num",null,j.name)):null,...P.map(I=>{const b=V(I);return!b||!D?null:n("div.wizchange",null,n("span",null,b.label),n("span.num",null,`${U(b,D.settings[I])} → ${U(b,S.settings[I])}`))}),S.colourGroups.length?n("div.wizchange",null,n("span",null,"Colour groups"),n("span.num",null,String(S.colourGroups.length))):null,!P.length&&!S.colourGroups.length&&!(j&&D&&j.id!==D.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."))},oe=()=>{const S=e.state,D=S.auto,P=S.caps,U=new Set(an(e).map(F=>F.preset)),j=S.colourGroups.length?{colourGroups:S.colourGroups}:{},I=D?Ae({...D.settings,...j}):null,b=[];let z=null;for(const F of za){const B=P?.presets.find(Z=>Z.id===F.preset);if(!B)continue;const X={...B.settings,...j};if(I&&Ae(X)===I&&F.preset!=="photo-or-scan"&&z===null){z=F.name;continue}b.push({id:F.preset,name:F.name,why:F.why,note:F.note,settings:X,preset:F.preset,suggested:U.has(F.preset)})}return[{id:"auto",name:z?`${z} · Auto`:"As Auto traced",why:D?`Auto used ${$s(P,D.settings,D.preset)}.`:"Auto's trace.",settings:null,preset:null,suggested:!1},...b.filter(F=>F.suggested),...b.filter(F=>!F.suggested)]},ue=S=>{const D=e.state;if(S.id==="auto"){const P=D.auto;return!!P&&D.preset===P?.preset&&ze(D.caps,D.settings,P.settings).length===0}return D.preset===S.preset},ke=S=>{S.id==="auto"?s.backToAuto():S.preset&&s.setPreset(S.preset)},de=S=>{if(!S){A();return}const D=e.state;if(S.id==="auto"||Se(S)){D.auto?.svg&&e.set({compare:M()??{svg:D.auto.svg,label:"Auto"}});return}const P=S.settings?o.get(Ae(S.settings)):void 0;P?.state==="done"&&e.set({compare:{svg:P.svg,label:`Preview · ${S.name} (draft)`}})},Se=S=>{const D=e.state.auto;if(!D||!S.settings)return!1;const P=e.state.colourGroups.length?{colourGroups:e.state.colourGroups}:{};return Ae(S.settings)===Ae({...D.settings,...P})},Ne=S=>{const D=e.state;if(S.id==="auto"||Se(S)){const U=D.auto?.report;return U?{url:L(),text:`${ie(U.meanDe00)} dE00 · ${ne(U.paths)} paths · ${ae(U.bytes)}`,busy:!1}:D.tracing?{url:null,text:"tracing…",busy:!0}:{url:null,text:"no trace yet",busy:!1}}const P=S.settings?o.get(Ae(S.settings)):void 0;return!P||P.state==="queued"?{url:null,text:D.auto?.report?"queued":"after Auto",busy:!0}:P.state==="running"?{url:null,text:"tracing a draft…",busy:!0}:P.state==="failed"?{url:null,text:"no preview",busy:!1}:{url:P.url,text:`${ie(P.report.meanDe00)} dE00 · ${ne(P.report.paths)} paths · ${ae(P.report.bytes)}`,busy:!1}},Ce=()=>{const S=oe(),D=S.map(I=>I.id).join(",");if(!Y||Y.dataset.signature!==D){Y=n("div.kinds",{"data-signature":D,role:"radiogroup","aria-label":"What is it?"});for(const I of S){const b=n("button.kind",{role:"radio","data-key":I.id,"data-ctl":`wiz-kind:${I.id}`,onclick:()=>ke(I),onpointerenter:()=>de(I),onpointerleave:()=>de(null),onfocus:()=>de(I),onblur:()=>de(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"));Y.append(b)}N(g,Y,n("p.muted.wizsmall.kqueue"))}const P=e.state;P.auto&&!P.auto.report&&P.tracing||o.want(S.filter(I=>I.settings&&!Se(I)).map(I=>({key:Ae(I.settings),settings:I.settings})));for(const I of S){const b=Y.querySelector(`[data-key="${I.id}"]`);if(!b)continue;const z=Ne(I),E=b.querySelector("img");z.url&&E.getAttribute("src")!==z.url&&(E.src=z.url),E.hidden=!z.url,b.classList.toggle("busy",z.busy),b.setAttribute("aria-checked",String(ue(I))),b.querySelector(".kstat").textContent=z.text}const U=o.pending(),j=g.querySelector(".kqueue");j&&(j.textContent=U?`Previews are small drafts of this image, traced one at a time behind the main trace · ${U} 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 ce(){if(a)return;if(!e.state.auto&&e.state.svg&&e.state.report&&!e.state.tracing){const P=e.state;e.set({auto:{generation:P.resultGeneration,settings:{...P.settings},preset:P.preset,svg:P.svg,report:P.report,palette:P.palette,losses:P.losses}})}const S=e.state;r={settings:{...S.settings},preset:S.preset,colourGroups:[...S.colourGroups]},l=S.view,c="auto",Y=null;const D=n("div.sheetbody.wizbody",null,h,k,T);a=n("div.sheet.wizsheet",null,D),t.classList.add("wizarding"),t.append(a),e.set({wizard:{step:w()[0]},chooser:!1,view:S.view==="ab"?"side":S.view,compare:M()}),S.prefs?.seenFirstRun||s.markSeen(),i=[e.on(["wizard","facts","caps","prefs"],G),e.on(["settings","preset","colourGroups","view"],()=>{xe(),($()==="kind"||$()==="output")&&H()}),e.on(["report","result","tracing","palette","groupSuggestions","resultGroups","paletteSelection","simplifyTo","losses","auto","svg"],()=>{H(),(e.state.compare===null||e.state.compare===d||e.state.compare?.label==="Auto")&&A(),_()}),o.on(()=>{$()==="kind"&&H()}),e.on(["auto"],x)],G(),(T.querySelector("[data-ctl=wiz-next]")??D).focus?.()}function Fe(S=!1,D=!1){if(!a)return;for(const I of i)I();i=[],o.pause(),a.remove(),a=null,Y=null,t.classList.remove("wizarding"),p&&URL.revokeObjectURL(p.url),p=null,d=null;const P=e.state,U=r!==null&&(P.preset!==r.preset||ze(P.caps,P.settings,r.settings).length>0||JSON.stringify(P.colourGroups)!==JSON.stringify(r.colourGroups)),j=P.auto?ze(P.caps,P.auto.settings,P.settings).length:0;e.set({wizard:null,compare:null,hoverFill:null,view:l,railTab:U?"tune":P.railTab}),r=null,S&&U&&q(j?`Custom settings kept: ${j} control${j===1?"":"s"} changed from Auto, all of them under Tune.`:"Custom settings kept. Everything is under Tune.",{kind:"good"}),D&&s.openExport()}return{open:ce,close:Fe,isOpen:()=>a!==null}}const Dn=4,Nn=24;function Ea(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 v=++i;o.classList.add("busy");try{const M=await In(e,t);if(v!==i)return;s.src=M.toDataURL("image/png")}catch{v===i&&q("The card could not be drawn.",{kind:"bad"})}finally{v===i&&o.classList.remove("busy")}},l=()=>{const[v,M]=Ls(t.size);o.style.setProperty("--ar",String(v/M)),a.textContent=`${v} × ${M} px · PNG · ${t.theme}`},c=[],d=()=>{c.forEach(v=>v()),l(),r()},p=(v,M,A)=>{const L=A.map(x=>n("button",{type:"button",role:"radio",onclick:()=>{t[v]=x.value,d()}},n("span",null,x.label),x.sub?n("small",null,x.sub):null));return c.push(()=>L.forEach((x,C)=>x.setAttribute("aria-checked",String(t[v]===A[C].value)))),Fn(M,n("div.cardseg",{role:"radiogroup","aria-label":M},...L))},h=(v,M,A)=>{const L=n("span.switch"),x=n("button.optrow",{type:"button",role:"switch",onclick:()=>{t[v]=!t[v],d()}},n("span.optext",null,n("span.optlabel",null,M),n("span.optnote",null,A)),L);return c.push(()=>{x.setAttribute("aria-checked",String(t[v])),L.setAttribute("aria-checked",String(t[v]))}),x},f=n("span.num",null,`${t.zoom}×`),u=n("input.slider",{type:"range",min:String(Dn),max:String(Nn),step:"1",value:String(t.zoom),"aria-label":"Detail crop magnification",oninput:v=>{f.textContent=`${v.target.value}×`},onchange:v=>{t.zoom=Number(v.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,`${Dn}×`),n("span",null,`${Nn}×`)));c.push(()=>{u.disabled=!t.detail,g.classList.toggle("off",!t.detail)});const m=(v,M,A)=>async()=>{const L=v.lastChild?.textContent??"";v.disabled=!0,v.lastChild&&(v.lastChild.textContent=M);try{await A()}finally{v.disabled=!1,v.lastChild&&(v.lastChild.textContent=L)}},k=async()=>{const v=await In(e,t);return new Promise(M=>v.toBlob(M,"image/png"))},T=n("button.btn",{type:"button"},W("copy",15),n("span",null,"Copy image"));T.onclick=m(T,"Copying…",async()=>{const v=await k();if(v)try{await navigator.clipboard.write([new ClipboardItem({"image/png":v})]),q("Card copied.",{kind:"good"})}catch{q("This system would not take an image on the clipboard; save it instead.",{kind:"bad"})}});const w=n("button.btn.primary",{type:"button"},W("download",15),n("span",null,"Download PNG"));w.onclick=m(w,"Saving…",async()=>{const v=e.state.source?.name.replace(/\.[^.]+$/,"")??"inkvec",M=await k();if(!M)return;const A=await ps(`${v}-card.png`,new Uint8Array(await M.arrayBuffer()),[{name:"PNG",extensions:["png"]}]);A&&(le(),q(A.path?`Card saved to ${A.path}`:`Downloaded ${A.name}`,{kind:"good",action:kt(A.path)}))});const $=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:le},W("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"}]),Fn("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,w,T))));We($),d()}function Fn(e,t){return n("div.optgroup",null,n("span.eyebrow",null,e),t)}function Ls(e){return e==="1600x900"?[1600,900]:[1080,1080]}async function In(e,t){const[s,o]=Ls(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 m=Math.round(s*.042),k=t.size==="1080x1080",T=k?s-m*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",m,m+14),i.letterSpacing="0px",i.fillStyle=c;const w=Math.round(s*(k?.05:.039));i.font=`500 ${w}px "Playfair Studio", Georgia, serif`,i.fillText("A logo, measured",m,m+w+26),i.fillText("rather than guessed.",m,m+w*2+30);let $=m+w*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,m,$),$+=40);const v=e.state.report;if(t.numbers&&v){const ee=[[ie(v.meanDe00),"mean dE00"],[ne(v.coordinates),"coordinates"],[`${(v.bytes/1024).toFixed(1)} KB`,"SVG"]];let J=m;for(const[Y,H]of ee.slice(0,k?2:3)){i.fillStyle=Y===ie(v.meanDe00)?h:c;const te=Math.round(s*.026);i.font=`500 ${te}px "Playfair Studio", Georgia, serif`,i.fillText(Y,J,$+te),i.fillStyle=d,i.font=`400 ${Math.round(s*.0085)}px "Inter Studio", Inter, sans-serif`,i.fillText(H,J,$+te+20),J+=Math.max(i.measureText(H).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",m,o-m);const M=k?m:m+T,A=s-M-m,L=k?Math.round(o*.42):m,x=Math.round(k?o*.3:o*.44),C=(ee,J,Y,H)=>{for(let Q=0;Q*14{i.save(),i.beginPath(),i.rect(J,L,_,x),i.clip(),C(J,L,_,x);const te=await Pt(ee),Q=Math.min((_-40)/te.width,(x-40)/te.height),oe=te.width*Q,ue=te.height*Q;i.drawImage(te,J+(_-oe)/2,L+(x-ue)/2,oe,ue),i.restore(),i.fillStyle=H,i.font=`500 ${Math.round(s*.0085)}px "Inter Studio", Inter, sans-serif`,i.letterSpacing="2px",i.fillText(Y,J+12,L+22),i.letterSpacing="0px"};if(G&&await xe(G.preview,M,G.container.toUpperCase(),d),se&&await xe(se.url,M+_+12,"SVG",h),t.detail&&G&&se){const ee=L+x+14,J=o-ee-m-40;if(J>60){const Y=e.state.worstCorner,H=t.zoom,te=Y?.x??se.w/2,Q=Y?.y??se.h/2,oe=M+A/2-te*H,ue=ee+J/2-Q*H;i.save(),i.beginPath(),i.rect(M,ee,A,J),i.clip(),i.fillStyle=r?"#141210":"#f0ece6",i.fillRect(M,ee,A,J);const ke=await Pt(G.preview);i.imageSmoothingEnabled=!1,i.drawImage(ke,oe,ue,se.w*H,se.h*H),i.imageSmoothingEnabled=!0,i.strokeStyle=r?"rgba(250,248,245,.09)":"rgba(26,24,22,.12)",i.lineWidth=1;const de=(oe%H+H)%H,Se=(ue%H+H)%H;for(let ce=M+de;ce0&&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 Pt(e){return new Promise((t,s)=>{const o=new Image;o.onload=()=>t(o),o.onerror=s,o.src=e})}const _n=[512,1024,2048];function Bn(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 Ps(e,t,s){const o={svg:!0,svgMinified:!0,pngSizes:[..._n],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,m)=>g+m.bytes,0),p=async()=>{const u=Bn(e,o);if(u){try{a=await R.planExport(u)}catch(g){a=[],q(String(g),{kind:"bad"})}f()}},h=(u,g,m,k)=>n("button.format",{onclick:k,"aria-pressed":String(m)},n("span.checkbox",{"aria-checked":String(m)},W("check",11)),n("span",null,u),n("span.size",null,m?ae(d(g)):"—"));function f(){const u=a.reduce((g,m)=>g+m.bytes,0);N(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?[]:[..._n],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 m=Bn(e,o);if(m)try{const k=await R.writeExport(m,g??"");q(zn?`${k.length} file${k.length===1?"":"s"} written to ${g}`:`Downloaded ${k[0]??"the export"}`,{kind:"good",action:kt(zn?k[0]??null:null)})}catch(k){q(String(k),{kind:"bad"})}}},a.length?`Download ${a.length} file${a.length===1?"":"s"} · ${ae(u)}`:"Nothing selected"))}e.set({exportOpen:!0}),t.append(l),f(),p()}function et(){return Fa()}function Na(){try{return window.self!==window.top}catch{return!0}}function Fa(){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())}},W("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=Na()?n("button.btn.ghost.compact",{"data-ctl":"own-tab",title:"The same app at its own address, with the whole window",onclick:o},W("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"},W("globe",14),n("span",null,"Inkvec home")),r=n("a.btn.ghost.compact.desktopapp",{"data-ctl":"desktop-app",href:us,target:"_blank",rel:"noopener noreferrer",title:zo},W("download",14),n("span",null,"Desktop app"));return n("div.webcontrols",null,i,r,document.fullscreenEnabled?e:null,a)}const On="https://logolabs.github.io/inkvec/studio/",Et="./guide/";function Es(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 Dt(e){ao()?To(e).catch(()=>q("Could not open the link in your browser.",{kind:"bad"})):window.open(e,"_blank","noopener")}let Le=null;function rn(e="index.html"){if(Le){Le.querySelector("iframe")?.setAttribute("src",Et+e);return}const t=n("iframe",{title:"Inkvec Studio user guide",src:Et+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""};Le=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",Et+"index.html")},"Contents"),n("button.btn.compact.ghost",{title:"The same page on logolabs.github.io, in your browser",onclick:()=>Dt(On+o())},"Open in browser"),n("button.btn.compact",{onclick:Ht},"Done"),n("div.sep"),et()),s),t.addEventListener("load",()=>{const a=t.contentDocument;if(!a||!a.querySelector('meta[name="inkvec-guide"]')){N(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:()=>Dt(On)},"Open the online guide")));return}a.addEventListener("keydown",i=>{i.key==="Escape"&&Ht(),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(),Dt(l))})}),window.addEventListener("keydown",Ds,!0),(document.getElementById("app")??document.body).append(Le)}function Ht(){window.removeEventListener("keydown",Ds,!0),Le?.remove(),Le=null}function Ds(e){e.stopImmediatePropagation(),(e.key==="Escape"||e.key==="F1")&&(e.preventDefault(),e.key==="Escape"&&Ht())}function Ia(e){window.addEventListener("keydown",t=>{t.key!=="F1"||Le||(t.preventDefault(),rn(Es(e.state)))})}const it=30,Rn=8;function _a(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 w=e.state.batch;return w.failuresFirst?[...w.rows].sort(($,v)=>+(v.state==="failed")-+($.state==="failed")):w.rows},p=()=>n("div.firstrun",{style:{minHeight:"100%",background:"transparent"}},n("div.drop",null,n("span.glyph",null,W("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 w=d();if(s.hidden=w.length===0,!w.length){r.style.height="0px",c.style.height="0px",N(l,p());return}const $=o.scrollTop,v=Math.ceil(o.clientHeight/it)+Rn*2,M=Math.max(0,Math.floor($/it)-Rn),A=w.slice(M,M+v);r.style.height=`${M*it}px`,c.style.height=`${Math.max(0,(w.length-M-A.length)*it)}px`,N(l,...A.map(L=>Ba(L,e,()=>e.touch("batch"))))};o.addEventListener("scroll",h,{passive:!0});const f=async()=>{const w=await Mo({directory:!0,multiple:!1});return typeof w=="string"?w:null},u=async()=>{const w=await f();if(w)try{const $=await R.batchScan(w),v=e.state.batch;v.folder=w,v.outputDir??=`${w}${w.includes("\\")?"\\":"/"}svg`,v.rows=$.map((M,A)=>({id:A,path:M,file:M.split(/[\\/]/).pop()??M,preset:v.preset,state:"queued",de00:null,coordinates:null,outBytes:null,destination:"",message:null,seconds:null})),v.totals=null,e.touch("batch")}catch($){q(String($),{kind:"bad"})}},g=async()=>{const w=e.state.batch;if(!w.folder||!w.outputDir){q("Choose a folder of images and an output folder first.");return}w.running=!0,w.paused=!1,e.touch("batch");try{await R.batchStart({files:w.rows.map($=>$.path),preset:w.preset,overrides:w.rows.filter($=>$.preset!==w.preset).map($=>[$.id,$.preset]),outputDir:w.outputDir,skipExisting:w.skipExisting})}catch($){w.running=!1,e.touch("batch"),q(String($),{kind:"bad"})}},m=()=>{const w=e.state.batch,$=e.state.caps?.presets??[],v=(M,A,L)=>n("button.btn.compact",{onclick:A,title:null},M);N(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:M=>{const A=M.target.value;w.preset=A;for(const L of w.rows)L.state==="queued"&&(L.preset=A);e.touch("batch")}},...$.map(M=>n("option",{value:M.id,selected:w.preset===M.id?"":null},`Preset for all · ${M.name}`))),v(w.folder?`Input · ${w.folder}`:"Choose a folder…",()=>void u()),v(w.outputDir?`Output · ${w.outputDir}`:"Output folder…",async()=>{const M=await f();M&&(w.outputDir=M,e.touch("batch"))}),n("button.btn.compact",{"aria-pressed":String(w.skipExisting),onclick:()=>{w.skipExisting=!w.skipExisting,e.touch("batch")}},"Skip existing"),n("div.spacer"),n("button.btn.compact",{"aria-pressed":String(w.failuresFirst),disabled:!w.rows.some(M=>M.state==="failed"),onclick:()=>{w.failuresFirst=!w.failuresFirst,e.touch("batch")}},"Failures first"),w.running?v(w.paused?"Resume":"Pause",()=>{w.paused=!w.paused,R.batchPause(w.paused),e.touch("batch")}):null,w.running?v("Cancel",()=>void R.batchCancel()):n("button.btn.primary.compact",{disabled:!w.rows.length,onclick:()=>void g()},"Start")),N(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"))},k=()=>{const w=e.state.batch,$=w.totals,v=$?.finished??0,M=$?.total??w.rows.length,A=$&&$.sourceBytes>0?1-$.bytesWritten/$.sourceBytes:null;N(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,M?`${v} of ${M} done${$?.failed?` · ${$.failed} failed`:""}`:"Nothing queued"),n("span",null,$?.remaining!=null?`${xo($.remaining)} left`:"")),n("div.progress",null,n("i",{style:{width:ve(M?v/M:0)}}))),$?n("span.faint",null,`mean dE00 ${ie($.meanDe00)} · ${ae($.bytesWritten)} written${A!==null?` · ${ve(A)} smaller than source`:""}`):null,n("button.reset",{style:{marginLeft:"auto"},disabled:!w.rows.some(L=>L.state!=="queued"),onclick:async()=>{const L=await R.batchStatsCsv(),x=await Co({defaultPath:"stats.csv",filters:[{name:"CSV",extensions:["csv"]}]});x&&(await R.saveBytes(x,[...new TextEncoder().encode(L)]),q(`Stats written to ${x}`,{kind:"good",action:{label:"Show in folder",run:()=>void Ao(x)}}))}},"Export stats.csv"))},T=()=>{m(),k(),h()};return e.on(["batch","caps"],T),T(),i}function Ba(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:()=>Oa(i,e,t,s)},Ra(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?"—":ie(e.de00)),n("span.right.faint",null,e.coordinates===null?"—":ne(e.coordinates)),n("span.right.faint",null,e.outBytes===null?"—":ae(e.outBytes)),n("span.trunc.muted",null,e.message??e.destination))}function Oa(e,t,s,o){const a=s.state.caps?.presets??[];Qt(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),le(),o()}},n("span",{style:{flex:"1"}},i.name),n("span.when",null,i.id===t.preset?"current":i.subtitle)))))}function Ra(e){return e.split("-").map((t,s)=>s===0?t[0].toUpperCase()+t.slice(1):t).join(" ")}function Ga(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;const u=()=>{const x=p==="auto"?h:p;a.classList.toggle("onlight",x==="light"),a.classList.toggle("ondark",x==="dark")},g=()=>{if(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 x=`${(c*100).toFixed(2)}%`;o.style.left=x,t.style.clipPath=`inset(0 ${(100-c*100).toFixed(2)}% 0 0)`,s.style.clipPath=`inset(0 0 0 ${x})`}else t.style.clipPath="",s.style.clipPath="",t.classList.toggle("hidden",!d),s.classList.toggle("hidden",d)};o.addEventListener("pointerdown",x=>{o.setPointerCapture(x.pointerId);const C=G=>{const V=a.getBoundingClientRect();c=Math.min(.98,Math.max(.02,(G.clientX-V.left)/V.width)),g()},_=()=>{o.removeEventListener("pointermove",C),o.removeEventListener("pointerup",_)};o.addEventListener("pointermove",C),o.addEventListener("pointerup",_)}),window.addEventListener("keydown",x=>{e.state.tab!=="minify"||e.state.screen||x.key!==" "||x.repeat||x.target instanceof HTMLInputElement||x.target instanceof HTMLTextAreaElement||(x.preventDefault(),d=!0,g())}),window.addEventListener("keyup",x=>{x.key===" "&&d&&(d=!1,g())});const m=()=>{const x=e.state.minify,C=x.result;N(i,n("div.seg",null,...["side","wipe","ab"].map(_=>n("button",{"aria-pressed":String(l===_),disabled:!x.before,onclick:()=>{l=_,g(),m()}},_==="side"?"Side by side":_==="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(_=>n("button",{"aria-pressed":String(p===_),disabled:!x.before,onclick:()=>{p=_,u(),m()}},_==="auto"?"Auto":_==="light"?"Light":"Dark")))),n("span.muted",{style:{marginLeft:"auto",fontSize:"11.5px"}},C?`Nothing moved more than ${x.settings.tolerancePx} px${C.differenceDe00===null?"":` · measured ${ie(C.differenceDe00)} dE00`}`:""))},k=n("aside.minifyrail"),T=n("div",{style:{flex:"1",minHeight:"0",display:"flex"}},r,k);let w=0;const $=async()=>{const x=e.state.minify;if(!x.before)return;const C=++w;try{const _=await R.minify(x.before,x.settings);if(C!==w)return;e.state.minify.result=_,e.state.minify.error=null}catch(_){if(C!==w)return;e.state.minify.result=null,e.state.minify.error=String(_)}e.touch("minify")},v=async()=>{const[x]=await Jt([{name:"SVG",extensions:["svg"]}]);if(x)try{const C=await gt(x);e.state.minify.name=x.name,e.state.minify.before=C,e.state.minify.result=null,e.state.minify.error=null,e.touch("minify"),await $()}catch(C){q(String(C),{kind:"bad"})}},M=async x=>{if(!x.name.toLowerCase().endsWith(".svg"))return!1;const C=await gt(x);return e.state.minify.name=x.name,e.state.minify.before=C,e.touch("minify"),await $(),!0};T.acceptDropped=M;const A=()=>n("div.drop.solid",null,n("span.glyph",null,W("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:v},"Open an SVG")),L=()=>{const x=e.state.minify,C=x.result;if(m(),g(),N(t,n("span.eyebrow.panelabel",null,`Before${C?` · ${ae(C.bytesBefore)}`:""}`),x.before?Ie(x.before):A()),N(s,n("span.eyebrow.panelabel",null,`After${C?` · ${ae(C.bytesAfter)}`:""}`),C?Ie(C.svg):x.before?null:n("span.faint.panehint",null,"The rewritten drawing appears here, on the same ground, so nothing can hide.")),x.before!==f){const G=x.before;f=G,h=null,u(),G&&Wa(G).then(V=>{f===G&&(h=V,u())})}if(!x.before){N(k,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 _=C?1-C.bytesAfter/Math.max(1,C.bytesBefore):0;N(k,n("div.railscroll",{style:{gap:"20px",padding:"22px 20px"}},x.error?n("div.card",null,n("span.eyebrow",null,"This file would not parse"),n("span.faint",{style:{fontSize:"12.5px",lineHeight:"1.6"}},x.error)):n("div.minifyresult",null,n("span.eyebrow",null,C?`Result · ${Te((C.ms??0)/1e3)}`:"Working"),n("div",{style:{display:"flex",alignItems:"baseline",gap:"10px"}},n("span.figure",null,C?ve(_):"—"),n("span.dim",{style:{fontSize:"14px",paddingBottom:"6px"}},"smaller")),C?n("span.faint.num",{style:{fontSize:"13px"}},`${ae(C.bytesBefore)} → ${ae(C.bytesAfter)}`):null),C?n("div.stats",null,...[["numbers before",ne(C.numbersBefore)],["numbers after",ne(C.numbersAfter)],["paths",`${C.pathsBefore} → ${C.pathsAfter}`],["segments",`${ne(C.segmentsBefore)} → ${ne(C.segmentsAfter)}`],["difference",C.differenceDe00===null?"—":`${ie(C.differenceDe00)} dE00`],["kept as drawn",`${C.guarded} run${C.guarded===1?"":"s"}`]].map(([G,V])=>n("div",null,n("span.k",null,G),n("span.v",null,V)))):null,Ha(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 G=n("span.num.faint",{style:{fontSize:"12px",width:"34px"}},`${x.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(x.settings.cornerDegrees),"aria-label":"Corner threshold in degrees",oninput:V=>{G.textContent=`${V.target.value}°`},onchange:V=>{x.settings.cornerDegrees=Number(V.target.value),e.touch("minify"),$()}}),G)})()),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(x.settings.documentCleanup),"aria-label":"Document cleanup",onclick:()=>{x.settings.documentCleanup=!x.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.")),C&&C.removed.length?n("div",{style:{display:"flex",flexDirection:"column",gap:"7px"}},n("span.eyebrow",null,"Removed"),...C.removed.map(G=>n("div",{style:{display:"flex",alignItems:"baseline",gap:"10px",fontSize:"12px"}},n("span.faint",{style:{flex:"1"}},G.what),n("span.dim.num",null,G.amount)))):null),n("div.railfoot",null,x.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:x.before,name:x.name??"drawing.svg"}}))},W("wand",14),"Rebuild it clean by re-tracing"):null,n("div.row",null,n("button.btn.primary",{style:{flex:"1"},disabled:!C,onclick:async()=>{if(!C)return;const G=(x.name??"drawing").replace(/\.svg$/i,""),V=await ps(`${G}.min.svg`,C.svg);V&&q(V.path?`Saved to ${V.path}`:`Downloaded ${V.name}`,{kind:"good",action:kt(V.path)})}},"Download SVG"),n("button.btn",{disabled:!C,onclick:async()=>{C&&(await vt(C.svg),q("SVG copied. Paste straight into Figma or Illustrator."))}},"Copy"),n("button.btn",{onclick:v},"Open…"))))};return e.on(["minify"],L),L(),T}function Ha(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 Ie(e){const t=n("div.art.centred"),s=Ns(e);return s&&(s.setAttribute("width","100%"),s.setAttribute("height","100%"),t.append(s)),t}function qa(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 Ns(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(qa(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 Wa(e){const t=Ns(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.8?"dark":null}catch{return null}}const me=25.4,Gn=[["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."]],Hn=[["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."]],qn=[{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 Ua(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 m=e.state.fab;if(!m.svg)return;const k=++l;try{const T=await R.fabPrepare(m.svg,m.options);if(k!==l)return;m.plan=T,m.error=null,m.shown>=T.layers.length&&(m.shown=-1)}catch(T){if(k!==l)return;m.plan=null,m.error=String(T)}e.touch("fab")},d=async(m,k)=>{const T=e.state.fab;T.name=m,T.svg=k,T.plan=null,T.error=null,T.shown=-1;try{const w=await R.fabAnalyze(k);T.analysis=w;const $=w.colours.filter(v=>!v.background).map(v=>v.hex);T.options.include=$,T.options.order=[...$],$.length>1&&T.options.mode==="singleColour"&&(T.options.mode="layered")}catch(w){T.analysis=null,T.error=String(w)}e.touch("fab"),await c()},p=async()=>{const[m]=await Jt([{name:"SVG",extensions:["svg"]}]);if(m)try{await d(m.name,await gt(m))}catch(k){q(String(k),{kind:"bad"})}},h=async()=>{const m=e.state;m.svg&&await d(m.source?.name?`${m.source.name.replace(/\.[^.]+$/,"")}.svg`:"trace.svg",m.svg)},f=async m=>m.name.toLowerCase().endsWith(".svg")?(await d(m.name,await gt(m)),!0):!1;r.acceptDropped=f;const u=(m,k=!1)=>{const T=e.state.fab;Object.assign(T.options,m),k||(T.preset=null),e.touch("fab"),c()},g=()=>{const m=e.state.fab;if(Za(e,t,s),!m.svg){N(i,Xa(e,p,h));return}N(i,n("div.railscroll",{style:{gap:"16px",padding:"20px"}},Ka(e,p,h),m.error?n("div.card",null,n("span.eyebrow",null,"This file could not be prepared"),n("span.faint",null,m.error)):null,Qa(e,u),ei(e,u),ti(e,u),ni(e,u),oi(e,u),ai(e),ii(e)),ri(e))};return e.on(["fab"],g),e.on(["svg"],()=>{e.state.tab==="fabricate"&&g()}),g(),r}function Fs(e,t,s=1){return e.state.fab.unit==="in"?`${(t/me).toFixed(s+1)} in`:`${t.toFixed(s)} mm`}function ja(e,t){return e.state.fab.unit!=="in"?t:t.replace(/(\d+(?:\.\d+)?) mm²/g,(s,o)=>`${(Number(o)/(me*me)).toFixed(4)} in²`).replace(/(\d+(?:\.\d+)?) mm(?![²\w])/g,(s,o)=>`${(Number(o)/me).toFixed(3)} in`)}function Xe(e,t){return e.state.fab.unit==="in"?`${(t[0]/me).toFixed(2)} × ${(t[1]/me).toFixed(2)} in`:`${t[0].toFixed(1)} × ${t[1].toFixed(1)} mm`}function Va(e){return e?e.checks.filter(t=>t.code==="thin"||t.code==="gaps"||t.code==="specks").length:0}function Ya(){return n("div.drop.solid",null,n("span.glyph",null,W("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 ln(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 Za(e,t,s){const o=e.state.fab,a=o.plan,i=Va(a),r=h=>{o.shown=h,e.touch("fab")};N(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}: ${Xe(e,h.materialMm)} of material, ${h.parts} pieces, ${ne(h.nodes)} path segments`,onclick:()=>r(f)},ln(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")}},W("alert",13),n("span",{style:{marginLeft:"6px"}},o.showProblems?"Showing problems":"Show problems")):null,n("span.muted",{style:{marginLeft:"auto",fontSize:"11.5px"}},a?`${Xe(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?Ja(d(Ie(a.previewSvg))):null,d(Ie(c)),a&&i&&o.showProblems?d(Ie(a.problemsSvg),!0):null):o.svg?Ie(o.svg):Ya();N(s,n("span.eyebrow.panelabel",null,o.shown>=0&&a?`${l?"Pen":"Sheet"} ${o.shown+1} · ${a.layers[o.shown].name} · ${Xe(e,a.layers[o.shown].materialMm)}`:l?"What the pen draws, over the drawing":"All sheets, stacked"),p)}function dt(e,t){const s=e.state.fab.options.mode==="lines"?"pen drawing":"sheet";return t===1?s:`${s}s`}function Ja(e){return e.style.opacity="0.18",e.style.pointerEvents="none",e}function Xa(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 Ka(e,t,s){const o=e.state.fab;return n("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},W("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 Qa(e,t){const s=e.state.fab,o=s.options.include.length,a=qn.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"}},...qn.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)}},W(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 ei(e,t){const s=e.state.fab.options,o=Gn.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)"}},...Gn.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 pt(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 pe(e,t,s,o="58px"){const a=e.state.fab.unit==="in",i=Number(a?(t/me).toFixed(3):t.toFixed(2));return pt(i,r=>s(a?r*me:r),o)}function cn(e){return e.state.fab.unit==="in"?"in":"mm"}function ti(e,t){const s=e.state.fab,o=s.analysis?.aspect??1,a=s.options.widthMm,i=cn(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 ",pe(e,a,r=>r>0&&t({widthMm:r})),` ${i} wide and `,pe(e,a*o,r=>r>0&&t({widthMm:r/o})),` ${i} tall.`),Pe("Cut a size-check square",s.options.sizeCheckMm>0,()=>t({sizeCheckMm:s.options.sizeCheckMm>0?0:s.unit==="in"?me:20}),s.options.sizeCheckMm>0?`A ${Fs(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)"}},...Hn.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"}},Hn.find(r=>r[0]===s.options.fileUnits)?.[2]??"")))}function ni(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(m=>m!==d):[...a.include,d],g=h?a.order:[...a.order.filter(m=>m!==d),d];t({include:u,order:g})}}),ln(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"}},ve(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 Pe(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 si(e,t){const s=e.state.fab.options,o=cn(e);return n("div.card",null,n("span.eyebrow",null,"Drawing"),n("span.sentence",null,"The pen or tool draws ",pe(e,s.penMm,a=>a>0&&t({penMm:a})),` ${o} wide.`),n("span.sentence",null,"Lines up to ",pe(e,s.maxLineMm,a=>t({maxLineMm:a})),` ${o} wide are drawn once (0 for any width); wider parts are drawn round their outline.`),Pe("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."),Is(e,t),n("span.sentence",null,"Lines stay within ",pe(e,s.toleranceMm,a=>a>0&&t({toleranceMm:a})),` ${o} of the drawing.`))}function Is(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/me).toFixed(1)):Math.round(o.gcodeFeedMmMin);return n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"}},Pe("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 ",pt(i,r=>r>0&&t({gcodeFeedMmMin:s.unit==="in"?r*me:r}),"64px"),` ${a}, power S`,pt(o.gcodePower,r=>t({gcodePower:r}),"58px"),", ",pt(o.gcodePasses,r=>r>=1&&t({gcodePasses:Math.round(r)}),"40px",1),o.gcodePasses===1?" pass.":" passes."):null)}function oi(e,t){const s=e.state.fab.options;if(s.mode==="lines")return si(e,t);const o=cn(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 ",pe(e,s.bleedMm,i=>t({bleedMm:i})),` ${o} under the colours above it.`):null,s.mode==="sticker"?n("span.sentence",null,"The contour is ",pe(e,s.stickerMarginMm,i=>t({stickerMarginMm:i})),` ${o} outside the artwork.`):null,s.mode==="stencil"?n("span.sentence",null,"Bridges are ",pe(e,s.bridgeMm,i=>i>0&&t({bridgeMm:i})),` ${o} wide; the sheet reaches `,pe(e,s.stencilMarginMm,i=>t({stencilMarginMm:i})),` ${o} past the artwork.`):null,n("span.sentence",null,"Parts narrower than ",pe(e,s.minFeatureMm,i=>i>0&&t({minFeatureMm:i})),` ${o} are too thin to weed.`),Pe("Remove them",s.removeThin,()=>t({removeThin:!s.removeThin}),"Drops the slivers and necks narrower than that from the sheets."),a?Pe("Registration marks",s.registration,()=>t({registration:!s.registration})):null,n("span.sentence",null,"Weeding border ",pe(e,s.weedBorderMm,i=>t({weedBorderMm:i})),` ${o} outside (0 for none).`),Pe("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."),Is(e,t),s.mode==="sticker"||s.mode==="stencil"?null:Pe("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 ",pe(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 ",pe(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 ",pe(e,s.toleranceMm,i=>i>0&&t({toleranceMm:i})),` ${o} of the drawing.`))}function ai(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,...[[dt(e,2),String(t.layers.length)],["pieces",ne(o)],["path segments",ne(s)],["whole job",Xe(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"}},W(i.level==="info"?"info":"alert",14)),n("span.faint",null,ja(e,i.message))))):n("div",{style:{display:"flex",gap:"8px",alignItems:"center",fontSize:"12.5px"}},W("check",14),n("span.dim",null,"Nothing here will trouble the cutter.")))}function ii(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)),ln(o.hex,14),n("span.num",{style:{flex:"1"}},o.name),n("span.dim.num",null,Xe(e,o.materialMm)),n("span.muted.num",{style:{width:"54px",textAlign:"right",fontSize:"11px"}},`${o.parts} pc`))))}function ri(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 Lo(i,`${o}-sheets.zip`);if(!r)return;const l=s.layers.length>1,c=`${s.layers.length} ${dt(e,s.layers.length)}${l?" and one combined file":""}`;q(r.folder?`Saved ${c} to ${r.folder}`:`Downloaded ${c}${i.length>1?" as one .zip":""}`,{kind:"good",action:kt(r.first)})}},`Download ${s&&s.layers.length>1?`${s.layers.length} ${dt(e,2)}`:`the ${dt(e,1)}`}`),n("button.btn",{disabled:!s,title:"Copies the shown sheet, or every sheet in one file",onclick:async()=>{s&&(await vt(t.shown>=0?s.layers[t.shown].svg:s.combinedSvg),q("Copied. Paste it into Design Space, Silhouette Studio or LightBurn."))}},"Copy")),n("div.note",null,n("span",null,s?`Real size: ${Fs(e,s.sizeMm[0])} wide`:"Real sizes"),n("span",null,"Nothing leaves this computer")))}const rt={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"},_s=e=>fetch(new URL(e,document.baseURI)).then(t=>{if(!t.ok)throw new Error(`${e}: HTTP ${t.status}`);return t.json()});let Wn=null;function li(){return Wn??=_s("../showcase.json"),Wn}function ci(e){return _s(`../showcase/${e}.json`)}function ui(e,t){const s=n("div.screenbody",null,n("div.sc-loading.faint",null,"Loading the showcase…"));return li().then(o=>N(s,di(o)),o=>N(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"),et()),s)}function di(e){let t=e.sets[0].cases[0],s="vtracer-1.0-simplify",o="fill",a=0;const i=pi(),r=n("div.sc-chips"),l=n("p.sc-caption"),c=n("div.sc-list"),d=new Map,p=e.gallery_engines.filter(v=>v!=="inkvec"),h=(v,M,A)=>{const L=n("div.seg"),x=()=>{for(const C of L.querySelectorAll("button"))C.setAttribute("aria-pressed",String(C.dataset.k===M()))};for(const[C,_]of v)L.append(n("button",{"data-k":C,onclick:()=>{A(C),x()}},_));return x(),L},f=async()=>{const v=t,M=++a;for(const[_,G]of d)G.setAttribute("aria-pressed",String(_===v.key));const A=v.what.includes("brand")||v.key.startsWith("brand");N(l,n("b",null,v.label),` · ${v.what}`,A?n("span.faint",{style:{fontSize:"11px",marginLeft:"6px"},title:"Nominative benchmark demonstration. All trademarks belong to their respective owners."},"(nominative benchmark)"):null);const L=v.m.inkvec,x=v.m[s];N(r,n("span.sc-legend.faint",null,n("i.sc-dot.a"),"anchor",n("i.sc-dot.hd"),"control point"),Ye(`Inkvec dE00 ${L.de1024.toFixed(3)}`,!0),Ye(`${rt[s]} dE00 ${x.de1024.toFixed(3)}`),Ye(`Inkvec ${L.paths} paths · ${L.coordinates} coordinates · ${Un(L.bytes)}`,!0),Ye(`${rt[s]} ${x.paths} paths · ${x.coordinates} coordinates · ${Un(x.bytes)}`),Ye(`The artist's file: ${v.artist.paths} paths · ${v.artist.coordinates} coordinates`));const C=await ci(v.key);M===a&&(i.set(C.input,C.svg.inkvec,C.svg[s],"Inkvec",rt[s]??s),i.layer(o))},u=[];new ResizeObserver(()=>{const v=i.el.getBoundingClientRect().height;v>200&&(c.style.height=`${v}px`)}).observe(i.el);const g=(v,M)=>{t=v,f();const A=d.get(v.key);if(!A)return;const L=c.getBoundingClientRect(),x=A.getBoundingClientRect(),C=34;x.topL.bottom&&(c.scrollTop+=x.bottom-L.bottom+6),M&&A.focus({preventScroll:!0})};c.addEventListener("keydown",v=>{const M=u.findIndex(x=>x.key===t.key),L={ArrowDown:3,ArrowUp:-3,ArrowRight:1,ArrowLeft:-1,Home:-M,End:u.length-1-M}[v.key];L!==void 0&&(v.preventDefault(),g(u[Math.min(u.length-1,Math.max(0,M+L))],!0))});for(const v of e.sets)c.append(n("div.sc-sethead",null,v.label,n("span",null,String(v.cases.length)))),c.append(n("div.sc-tiles",null,...v.cases.map(M=>{const A=n("button.sc-case",{onclick:()=>g(M,!1),title:`${M.label} · ${M.what}`},n("span.sc-thumb",null,n("img",{src:M.thumb,alt:"",loading:"lazy"})),n("span.sc-name",null,M.label));return d.set(M.key,A),u.push(M),A})));const m=n("div.sc-tools",null,n("span.eyebrow",null,"Compare with"),h(p.map(v=>[v,rt[v]??v]),()=>s,v=>{s=v,f()}),n("span.eyebrow",null,"Show"),h([["fill","Fill"],["wire","Wireframe"],["anchor","Anchors"],["handle","Handles"]],()=>o,v=>{o=v,i.layer(o)}),n("span.eyebrow",null,"Zoom"),...[1,4,12].map(v=>n("button.btn.compact.ghost",{onclick:()=>i.zoom(v)},`${v}×`)));f();const k=e.engines.find(v=>v.id==="inkvec"),T=e.best_de_wins.inkvec??0,w=e.brands.best_de_wins.inkvec??0,$=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,m,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 ${T} of ${e.cases}; in the other sets, on ${w} of ${e.brands.cases} brand logos and ${$} of ${e.corpus.cases} more icons and emoji.`),k?n("div.sc-bignums",null,lt("median colour difference",k.de_median.toFixed(3),"dE00; under 1 is invisible"),lt("geometry vs the artist",`${k.geometry_mean.toFixed(2)}×`,`mean; median ${k.geometry_median.toFixed(2)}×`),lt("DINOv3 similarity",k.dino.toFixed(4),"1 is identical"),lt("seconds per case",k.seconds_median.toFixed(2),"median, native command line")):null,Nt(`${e.cases} benchmark cases, re-run at ${e.size} px`,e.engines),Nt(`${e.brands.cases} brand logos`,e.brands.engines),Nt(`${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:v=>{v.preventDefault(),xt("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 Nt(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 Un=e=>`${(e/1024).toFixed(1)} KB`,Ye=(e,t=!1)=>n(`span.sc-chip${t?".win":""}`,null,e),lt=(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 pi(){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){N(e[0].cam,n("img.sc-raster",{src:r,alt:"",draggable:"false"})),o=[Yn(l),Yn(c)],N(e[1].cam,o[0].root),N(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 jn="http://www.w3.org/2000/svg";function Vn(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 Yn(e){const t=Vn(e),s=Vn(e),o=n("div.sc-layer");if(!t||!s)return{root:o,anchors:0,set:()=>{}};s.classList.add("sc-wire");const a=hi(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 hi(e){const t=document.createElementNS(jn,"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]=Zn(h),g=fi(h.getAttribute("d")??"",f,u);i+=g.anchors.length;for(const[m,k]of g.anchors)s+=`M${r(m)} ${r(k)}h0.001`;for(const[[m,k],[T,w]]of g.handles)o+=`M${r(m)} ${r(k)}L${r(T)} ${r(w)}`,a+=`M${r(T)} ${r(w)}h0.001`}for(const h of e.querySelectorAll("circle, ellipse")){const[f,u]=Zn(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(jn,"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 Zn(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 fi(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 m=()=>parseFloat(o[r++]),k=(w,$)=>a.push([w+t,$+s]),T=(w,$,v,M)=>i.push([[w+t,$+s],[v+t,M+s]]);for(;r{const a=e.state;if(!a.screen){N(s),s.style.display="none";return}s.style.display="",N(s,a.screen==="settings"?mi(e,t):a.screen==="showcase"?ui(e,t.close):yi(e,t))};return e.on(["screen","prefs","caps"],o),o(),s}function mi(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"),et()),n("div.screenbody",null,n("div.settingsgrid",null,ct("General",[null,fe("Theme","The light theme matches the dark one token for token.",Ft([["system","System"],["dark","Dark"],["light","Light"]],s?.theme??"system",a=>t.applyPrefs({theme:a}))),fe("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.",Ft([["ask","Ask"],["auto","Auto only"],["custom","Custom wizard"]],s?.onOpen??"ask",a=>t.applyPrefs({onOpen:a}))),fe("Language","English only in this release. Nothing in the app is machine-translated, which is why there is one.",Ft([["en","English"]],"en",()=>{}))]),ct("Performance",[fe("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")),fe("Draft resolution","Used while you move a control.",It(s?.draftPx??512,128,2048,a=>t.applyPrefs({draftPx:a}),"px")),fe("Draft time limit","A draft that overruns is abandoned, not shown late.",It(s?.draftSeconds??.4,.05,10,a=>t.applyPrefs({draftSeconds:a}),"s",2)),fe("Settle before the full trace","How long the controls stay still before the full-resolution trace is queued.",It(s?.settleMs??800,100,5e3,a=>t.applyPrefs({settleMs:a}),"ms"))]),ct("Denoiser",bi(e,o)),null,ct("Advanced",[null,null,fe("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:()=>No("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 R.resetPrefs();e.set({prefs:a,settings:a.trace}),q("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, if you ask for it, the denoiser's weights; your preferences are kept in this browser's storage.")))))}function bi(e,t){return t?t.supported?[fe("Status","Optional. Improves traces from photographs and screenshots.",n("span",{style:{fontSize:"12.5px",color:t.installed?"var(--good)":"var(--muted)"}},t.installed?`Installed · ${ae(t.bytes??0)}`:"Not installed")),fe(t.installed?"Remove":"Download",t.installed?t.path??"Kept in this browser's storage.":"Downloaded once into this browser's storage, 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 R.denoiserRemove(),o=e.state.caps;o&&e.set({caps:{...o,denoiser:s}}),q("Denoiser removed.")}},"Remove"):n("button.btn.compact",{onclick:()=>un(e)},"Download")),fe("Verified against",t.repo,n("span.mono.muted",{style:{fontSize:"11px"}},`${t.sha256.slice(0,16)}…`))]:[fe("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"))]:[fe("Status","",n("span.muted",null,"—"))]}function un(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=()=>{N(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 80 MB, plus ONNX Runtime Web) and kept in this browser. 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&&R.denoiserCancel(),le()}},i?"Cancel download":"Not now"),i?null:n("button.btn.primary",{onclick:()=>{i=!0,r(),R.denoiserDownload()}},"Download")))};(async()=>{const l=await Me.denoiserProgress(({got:d,total:p})=>{const h=p?d/p*100:0;s.firstElementChild.style.width=`${h.toFixed(1)}%`,o.textContent=p?`${ae(d)} of ${ae(p)} · you can keep tracing`:`${ae(d)} · you can keep tracing`}),c=await Me.denoiserDone(({ok:d,message:p,status:h})=>{const f=e.state.caps;f&&e.set({caps:{...f,denoiser:h}}),l(),c(),le(),d?q("Denoiser installed and verified.",{kind:"good"}):p!=="cancelled"&&q(p??"The download did not finish.",{kind:"bad"})})})(),We(a),r()}function yi(e,t){const s=e.state.caps,o=n("pre",null,"Loading the notices…");R.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(),xt(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"),et()),n("div.screenbody",null,n("div.about",null,n("div.aboutleft",null,n("span.mark",null,os(72)),n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"}},n("span.serif",{style:{fontSize:"40px",lineHeight:"1"}},wt),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(),rn()}},"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:()=>wi(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 wi(e){We(St("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:le},"Close")]))}function ct(e,t){return n("div.settinggroup",null,n("div.head.eyebrow",null,e),...t)}function fe(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 Ft(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 It(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 vi={M:2,L:2,T:2,H:1,V:1,C:6,S:4,Q:4,A:7,Z:0};function*xi(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=m=>d?h+c[m]:c[m],g=m=>d?f+c[m]:c[m];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 m={x:u(0),y:g(1)},k={x:u(2),y:g(3)};s=u(4),o=g(5),t.push({x:s,y:o,in:m,out:k}),r=k;break}case"S":{const m=r?{x:2*h-r.x,y:2*f-r.y}:{x:h,y:f},k={x:u(0),y:g(1)};s=u(2),o=g(3),t.push({x:s,y:o,in:m,out:k}),r=k;break}case"Q":{const m={x:u(0),y:g(1)};s=u(2),o=g(3),t.push({x:s,y:o,in:m,out:m}),r=m;break}case"T":{const m=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:m,out:m}),r=m;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 Si="path, circle, ellipse, rect, line, polyline, polygon";function $i(e){const t=[];for(const s of e.querySelectorAll(Si)){const o=Ci(s);o&&t.push(o)}return t}function re(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 Mi(e){const t=/rotate\(\s*(-?[\d.]+)/.exec(e.getAttribute("transform")??"");return t?Number(t[1]):0}function Ci(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=re(e,"cx"),o=re(e,"cy"),a=e.nodeName.toLowerCase()==="circle",i=a?re(e,"r"):re(e,"rx"),r=a?re(e,"r"):re(e,"ry");if(!(i>0)||!(r>0))return null;const l=Mi(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=re(e,"x"),o=re(e,"y"),a=re(e,"width"),i=re(e,"height");if(!(a>0)||!(i>0))return null;const r=Math.min(re(e,"rx",re(e,"ry")),a/2),l=Math.min(re(e,"ry",re(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(re(e,"x1"))},${t(re(e,"y1"))}L${t(re(e,"x2"))},${t(re(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 Ti(e){const t=Math.min(1,Math.max(0,(e-1)/11));return{r:1.2+t*1.2,opacity:.4+t*.6}}const Xn=.25,Kn=64;function Ai(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=ye("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,m,k,T,w=0,$=Number.NaN,v,M=null,A={x:0,y:0,w:1,h:1};const L={certainty:ye("g.certainty"),wireframe:ye("g.wireframe"),handles:ye("g.handles")},x={certainty:!1,wireframe:!1,handles:!1};for(const b of Object.values(L))b.style.display="none";l.style.display="none",i.append(L.certainty,L.wireframe,L.handles);let C=null,_=null;function G(){const b=e.state,z=b.svg,E=b.source!==k,F=z!==m;if((E||F)&&(u=(z?Jn(z):null)??{x:0,y:0,w:b.source?.width??1,h:b.source?.height??1},$=Number.NaN),E&&(k=b.source,N(t,b.source?n("img",{src:b.source.preview,alt:"",draggable:"false"}):null)),F){if(m=z,g=null,z){const B=new DOMParser().parseFromString(z,"image/svg+xml").documentElement;B&&B.nodeName==="svg"&&!B.querySelector("parsererror")&&(B.setAttribute("width",String(u.w)),B.setAttribute("height",String(u.h)),g=document.importNode(B,!0))}N(a,g,i),i.setAttribute("viewBox",`${u.x} ${u.y} ${u.w} ${u.h}`),C=null,_=null;for(const B of["wireframe","handles"])N(L[B]),x[B]=!1;Q(!0)}(b.result!==T||F&&!z)&&xe(),E||F?(de(),Se()):b.show.certainty&&de()}function V(){const b=e.state.compare;if(b===v)return;v=b,M&&URL.revokeObjectURL(M),M=null,b?(A=Jn(b.svg)??u,M=URL.createObjectURL(new Blob([b.svg],{type:"image/svg+xml"})),N(s,n("img",{src:M,alt:"",draggable:"false",style:{imageRendering:"auto"}}))):N(s),s.style.display=b?"":"none",t.style.display=b?"none":"",o.textContent=b?b.label:"Source",c.textContent=b?"Yours":"Vector · SVG",r.classList.toggle("comparing",!!b);const z=`translate(${e.state.pan.x}px, ${e.state.pan.y}px)`;(b?s:t).style.transform=z,se()}function se(){const b=s.querySelector("img");if(!b)return;const z=e.state.zoom;b.style.position="absolute",b.style.left=`${(A.x-u.x)*z}px`,b.style.top=`${(A.y-u.y)*z}px`,b.style.width=`${A.w*z}px`,b.style.height=`${A.h*z}px`,b.style.maxWidth="none"}function xe(){T=void 0,w++,N(L.certainty),x.certainty=!1}function ee(){return C??=g?$i(g):[],C}function J(){if(_)return _;const b=[],z=[],E=[];for(const F of ee()){const B=new Set;for(const X of ki(F)){const Z=Math.round(X.x*1e3)*1e8+Math.round(X.y*1e3);B.has(Z)||(B.add(Z),b.push({x:X.x,y:X.y}));for(const he of[X.in,X.out])he&&(z.push(`M${X.x} ${X.y}L${he.x} ${he.y}`),E.push(`M${he.x} ${he.y}l.001 .001`))}}return _={lines:z.join(""),knobs:E.join(""),dots:b},_}function Y(b){if(x[b]=!0,!g)return;const z=L[b];if(b==="wireframe"){const E=zi(ee());E&&z.append(ye("path",{d:E}))}else{const{lines:E,knobs:F}=J();E&&z.append(ye("path.lines",{d:E}),ye("path.knobs",{d:F}))}}let H="",te=!1;function Q(b=!1){b&&(H=""),!te&&(te=!0,requestAnimationFrame(()=>{te=!1,oe()}))}function oe(){const b=e.state,z=!!g&&b.show.anchors,E=Math.round(d.clientWidth),F=Math.round(d.clientHeight);if(z&&(!E||!F))return;const B=window.devicePixelRatio||1,X=z?`${b.zoom} ${b.pan.x} ${b.pan.y} ${E} ${F} ${B}`:"off";if(X===H)return;H=X;const Z=l.getContext("2d");if(!Z)return;if(!z){l.width=0,l.height=0;return}const he=Math.max(1,Math.round(E*B)),pn=Math.max(1,Math.round(F*B));(l.width!==he||l.height!==pn)&&(l.width=he,l.height=pn),l.style.width=`${E}px`,l.style.height=`${F}px`,Z.setTransform(B,0,0,B,0,0),Z.clearRect(0,0,E,F);const{dots:hn}=J();if(!hn.length)return;const fn=getComputedStyle(d),Js=fn.getPropertyValue("--stage").trim()||"#808080",Xs=fn.getPropertyValue("--anchor").trim()||"#ff5c00",{r:gn,opacity:Ks}=Ti(b.zoom);l.style.opacity=String(Ks);const Qs=b.pan.x-u.x*b.zoom,eo=b.pan.y-u.y*b.zoom,Ue=gn*1.15,to=gn*.85,tt=[],Tt=[];for(const je of hn){const Ve=Qs+je.x*b.zoom,be=eo+je.y*b.zoom;Ve<-Ue||be<-Ue||Ve>E+Ue||be>F+Ue||(tt.push(Ve),Tt.push(be))}for(const[je,Ve]of[[Ue,Js],[to,Xs]]){Z.beginPath();for(let be=0;be{E===w&&e.set({bandsMissing:!0,show:{...e.state.show,certainty:!1}})};R.traceBands(b.resultGeneration).then(B=>{E===w&&(B?ke(B):F())},F)}function ke(b){N(L.certainty);for(const{cls:z,d:E}of Fi(Li(b)))L.certainty.append(ye(`path.${z}`,{d:E}))}function de(){const{show:b}=e.state,z=(E,F)=>{F&&!x[E]&&(E==="certainty"?ue():Y(E)),L[E].style.display=F?"":"none"};z("wireframe",b.wireframe),z("handles",b.handles),z("certainty",b.certainty),l.style.display=b.anchors?"":"none",Q(),g&&(g.style.opacity=b.fill?"1":"0.12")}function Se(){const b=e.state,z=`translate(${b.pan.x}px, ${b.pan.y}px)`;for(const E of v?[s,a]:[t,a])E.style.transform=z;if(b.zoom!==$){$=b.zoom;const E=u.w*b.zoom,F=u.h*b.zoom,B=t.querySelector("img");B&&(B.style.width=`${E}px`,B.style.height=`${F}px`),g&&(g.setAttribute("width",String(E)),g.setAttribute("height",String(F))),i.setAttribute("width",String(E)),i.setAttribute("height",String(F)),se()}b.show.anchors&&Q(),Ne()}function Ne(){const b=e.state;if(h.classList.toggle("stacked",b.view!=="side"),p.style.display=b.view==="wipe"?"":"none",b.view==="side")r.classList.remove("hidden"),d.classList.remove("hidden"),r.style.clipPath="",d.style.clipPath="";else if(b.view==="wipe"){r.classList.remove("hidden"),d.classList.remove("hidden");const B=`${(b.wipe*100).toFixed(2)}%`;p.style.left=B,r.style.clipPath=`inset(0 ${(100-b.wipe*100).toFixed(2)}% 0 0)`,d.style.clipPath=`inset(0 0 0 ${B})`}else r.style.clipPath="",d.style.clipPath="",r.classList.toggle("hidden",!b.flicked),d.classList.toggle("hidden",b.flicked);const z=b.detail&&b.zoom>=6;let E=d.querySelector(".pixelgrid");z&&!E&&(E=n("div.pixelgrid"),d.append(E)),E&&(E.style.display=z?"":"none",E.style.backgroundSize=`${b.zoom}px ${b.zoom}px`,E.style.backgroundPosition=`${b.pan.x}px ${b.pan.y}px`);const F=t.querySelector("img");F&&(F.style.imageRendering=b.zoom>=4?"pixelated":"auto")}function Ce(){const b=d.getBoundingClientRect();return{w:b.width||1,h:b.height||1}}function ce(){const{w:b,h:z}=Ce(),E=Math.min(b/u.w,z/u.h)*.88;e.set({zoom:E,pan:{x:(b-u.w*E)/2,y:(z-u.h*E)/2},fitted:!0})}function Fe(b,z,E){const{w:F,h:B}=Ce();e.set({zoom:E,pan:{x:F/2-b*E,y:B/2-z*E},fitted:!1})}function S(b){const{w:z,h:E}=Ce();D(z/2,E/2,Math.min(Kn,Math.max(Xn,b))/e.state.zoom)}function D(b,z,E){const F=e.state,B=Math.min(Kn,Math.max(Xn,F.zoom*E)),X=B/F.zoom;e.set({zoom:B,pan:{x:b-(b-F.pan.x)*X,y:z-(z-F.pan.y)*X},fitted:!1})}for(const b of[r,d])b.addEventListener("wheel",z=>{z.preventDefault();const E=b.getBoundingClientRect();D(z.clientX-E.left,z.clientY-E.top,Math.exp(-z.deltaY*.0016))},{passive:!1}),b.addEventListener("dblclick",ce),b.addEventListener("pointerdown",z=>{if(z.button!==0)return;b.setPointerCapture(z.pointerId);let E=z.clientX,F=z.clientY;const B=Z=>{const he=e.state;e.set({pan:{x:he.pan.x+(Z.clientX-E),y:he.pan.y+(Z.clientY-F)},fitted:!1}),E=Z.clientX,F=Z.clientY},X=()=>{b.removeEventListener("pointermove",B),b.removeEventListener("pointerup",X),b.removeEventListener("pointercancel",X)};b.addEventListener("pointermove",B),b.addEventListener("pointerup",X),b.addEventListener("pointercancel",X)});p.addEventListener("pointerdown",b=>{b.stopPropagation(),p.setPointerCapture(b.pointerId);const z=F=>{const B=h.getBoundingClientRect();e.set({wipe:Math.min(.98,Math.max(.02,(F.clientX-B.left)/B.width))})},E=()=>{p.removeEventListener("pointermove",z),p.removeEventListener("pointerup",E)};p.addEventListener("pointermove",z),p.addEventListener("pointerup",E)});const P=n("style",{"data-owner":"viewer-isolate"});document.head.append(P);function U(){const b=e.state.hoverFill;d.classList.toggle("isolating",!!b?.length),P.textContent=b?.length?Ii(b):""}e.on(["svg","source","result"],G),e.on(["compare"],V),e.on(["zoom","pan","view","wipe","flicked","detail"],Se),e.on(["show"],de),e.on(["hoverFill"],U),new ResizeObserver(()=>{!e.state.svg&&!e.state.source||(Ne(),e.state.show.anchors&&Q(),e.state.fitted&&ce())}).observe(h),new MutationObserver(()=>{e.state.show.anchors&&Q(!0)}).observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]});const I=()=>{window.matchMedia(`(resolution: ${window.devicePixelRatio||1}dppx)`).addEventListener("change",()=>{e.state.show.anchors&&Q(),I()},{once:!0})};return I(),{el:f,redraw:G,transform:Se,fit:ce,goTo:Fe,zoomTo:S}}function zi(e){let t="";for(const s of e){const o=s.trim();o&&(t+=o[0]==="M"?o:`M0 0${o}`)}return t}function Li(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=Ei(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 Pi=[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 Ei(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*Pi[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 Di=1,ut=16,Ni=256;function Fi(e,t=Ni){const s=new Map,o=new Int32Array(e.length),a=[],i={sure:[],soft:[],unsure:[]},r=[],l=Di/2;for(let d=0;dh&&C.y0-lf&&(C.cls===p.cls?r[o[x]]=d+1:o[x]>$&&($=o[x]))}}let v=-1;for(const M of i[p.cls])if(M>$&&r[M]!==d+1){v=M;break}v<0&&(v=a.length,a.push(p.cls),i[p.cls].push(v)),o[d]=v;for(let M=T;M<=w;M++)for(let A=m;A<=k;A++){const L=A*65536+M,x=s.get(L);x?x.push(d):s.set(L,[d])}}const c=new Map;for(let d=0;dd[0]-p[0]).map(d=>d[1])}const _t=".pane.vector > .art > svg:not(.overlay)";function Ii(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[`${_t} * { fill-opacity: 0.12 !important; stroke-opacity: 0.12 !important; }`,`${_t} :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; }`,`${_t} :is(${s("stroke")}) { stroke-opacity: 1 !important; }`].join(` -`)}function _i(e,t,s){const o=Ai(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,m=[1,4,12].map(w=>!u.fitted&&Math.abs(u.zoom-w)<.01),k=JSON.stringify([g,u.view,u.show,u.fitted,m,u.detail,!!u.svg,u.bandsMissing]);if(k===c)return;c=k;const T=(w,$)=>n("button",{"aria-pressed":String($==="fit"?u.fitted:!u.fitted&&Math.abs(u.zoom-$)<.01),disabled:!g,onclick:()=>$==="fit"?o.fit():o.zoomTo($)},w);N(a,n("div.seg",null,...["side","wipe","ab"].map(w=>n("button",{"aria-pressed":String(u.view===w),disabled:!g,onclick:()=>e.set({view:w})},w==="side"?"Side by side":w==="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(([w,$])=>n("button.toggle",{"aria-pressed":String(u.show[w]),disabled:!u.svg||w==="certainty"&&u.bandsMissing,title:w==="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[w]=!u.show[w],e.touch("show")}},$))),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 w=!u.detail;e.set({detail:w}),w&&u.zoom<12&&o.zoomTo(12)}},"Detail"),n("div.seg.num",null,T("Fit","fit"),T("1×",1),T("4×",4),T("12×",12))))},p=n("div",{style:{display:"contents"}});i.append(o.el,p);const h=()=>{const u=e.state;if(!u.source){o.el.style.display="none",N(p,Hi(e,t,s()));return}o.el.style.display="",N(p,Ri(u.stageState,u,t),u.svg?Bi(e):null,u.detail&&u.svg?Oi(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 m="Ready",k="",T="var(--dim)";if(u.tracing){m=u.liveStages[u.liveStages.length-1]?.name??"starting";const $=u.liveStages.reduce((v,M)=>v+M.ms,0)/1e3;k=`· ${Zt(u,u.tracingTier)??"—"} px · ${Te($)} elapsed · Esc to cancel`,T="var(--state-stale)"}else if(g&&u.result){const w=u.result.tier==="draft";m=w?"draft shown":`traced in ${Te(g.seconds)}`,k=w?`· ${g.tracedPx} px · full trace queued`:`· ${g.tracedPx} px · final · ${ne(g.segments)} segments`,T=w?"var(--state-draft)":"var(--state-final)"}else u.source&&(m="Opened",k=`· ${u.source.width} × ${u.source.height} · ${u.source.container}`);N(r,n("span.lead",{style:{color:T}},m),k?n("span",null,k):null,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,n("span.privacy",null,"Your image never leaves this computer."))};return e.on(["source","view","show","zoom","fitted","detail","svg","bandsMissing"],d),e.on(["source","svg","stageState","result","detail","worstCorner","prefs"],h),e.on(["tracing","liveStages","report","result","source","update"],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 Bi(e){const t=e.state,s=t.tracing?"stale":t.result?.tier==="draft"?"draft":"final",o=t.tracing?`re-tracing · ${Zt(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 Oi(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,qi(e)))}function Ri(e,t,s){const o=(a,i,r,l,c)=>n("div.stagestate",null,n("span",{style:{color:i,display:"flex"}},W(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 Gi(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 Hi(e,t,s){const o=cs(e.state.caps?.platform);return n("div.firstrun",null,Gi(e,t),n("div.drop",null,n("span.glyph",null,W("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:us,target:"_blank",rel:"noopener noreferrer"},"Inkvec Studio for the desktop"),"."))}function Bs(e,t){const s=e.state.worstCorner;s&&(e.set({detail:!0}),t.goTo(s.x,s.y,12))}function qi(e){const t=e.state.worstCorner;return t?`Worst disagreement: ${ie(t.de00)} dE00, at ${t.x} × ${t.y} in the traced raster.`:"The trace and the source agree everywhere we can measure."}const Os={precision:.1,speckleFloor:2,traceSize:2048,timeLimit:0,maxColours:64,colourMerging:.035,flatFills:!1,blackAndWhite:!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 Qn(e,t,s){e[t]=s}const y=new vo(yo(Os,{tolerancePx:.1,judgePx:1024,cornerDegrees:30,documentCleanup:!0}));let Rs=[],qt=0,Gs=0;const ht=new Map,Hs=new Eo;function Wi(){const e={...y.state.settings};return delete e.colourGroups,y.state.colourGroups.length&&(e.colourGroups=y.state.colourGroups),e}async function De(e){if(y.state.source)try{const t=y.state.colourGroups,s=await R.startTrace(Wi(),e);ht.set(s,t);for(const o of ht.keys())ovoid De("final"),y.state.prefs?.settleMs??800))}function qs(){return new Promise(e=>{if(!y.state.source){e();return}const t=y.on(["result","stageState"],()=>{y.state.tracing||(t(),e())});window.clearTimeout(qt),De("final")})}let Bt=0;function Ui(e,t=y.state.generation){const s=++Bt;if(e.state!=="traced"||!y.state.snaps.length){Ot(e,t);return}Ws(e).then(o=>{s===Bt&&Ot(e,t,o)},o=>{s===Bt&&(q(`The snapped colours could not be applied to this trace: ${o}`,{kind:"bad"}),Ot(e,t))})}async function Ws(e){for(;;){const t=y.state.snaps;if(!t.length)return{svg:e.svg,inks:e.palette};const s=e.report.tracedPx,o=await R.snapInks(e.svg,t,s,s);if(y.state.snaps===t)return o}}function Ot(e,t,s=null){if(e.state==="traced"){const o=y.state.result?.tier==="draft",a=y.state.result?.tier==="final"?y.state.report:y.state.previous;y.set({result:e,resultGeneration:t,resultGroups:ht.get(t)??y.state.resultGroups,bandsMissing:!1,previous:e.tier==="final"?a:y.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:!1,justSwapped:o&&e.tier==="final"}),y.state.justSwapped&&window.setTimeout(()=>y.set({justSwapped:!1}),600),e.tier==="final"&&y.set({groupSuggestions:ms(y.state)});const i=y.state.auto;i&&t===i.generation&&e.tier==="final"&&!i.report&&(y.set({auto:{...i,svg:e.svg,report:e.report,palette:e.palette,losses:e.losses}}),R.sourceFacts(i.settings.traceSize).then(r=>{y.state.auto?.generation===t&&y.set({facts:r})},()=>{})),e.oversized&&e.tier==="final"&&q(`Traced at ${e.tracedPx} px; the SVG is still ${e.sourcePx[0]} × ${e.sourcePx[1]} and scales without limit. This is normal.`);return}switch(y.set({tracing:!1}),e.state){case"flat":y.set({stageState:{kind:"flat"},svg:null,report:null,palette:[],losses:[]});break;case"undecodable":y.set({stageState:{kind:"undecodable",message:e.message}});break;case"outOfMemory":y.set({stageState:{kind:"outOfMemory",neededGb:e.neededGb,suggestPx:e.suggestPx}});break;case"failed":y.set({stageState:{kind:"failed",message:e.message}});break}}async function Ge(e,t=!0){He.close(),y.set({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(),y.set({zoom:1,pan:{x:0,y:0},stageState:{kind:"drawing"}}),await De("final"),Hs.reset();const s=y.state;s.tracing&&y.set({auto:{generation:s.generation,settings:{...s.settings},preset:s.preset,svg:null,report:null,palette:[],losses:[]}}),t&&ji()}catch(s){y.set({stageState:{kind:"undecodable",message:String(s)}})}}function ji(){const e=y.state.prefs?.onOpen??"ask";e==="custom"?He.open():e==="ask"&&y.set({chooser:!0})}async function Us(e){await Ge(async()=>{const t=await R.openPath(e);y.set({source:t});const s=await R.loadPrefs();y.set({prefs:s})})}async function ft(e){if(e.path)return Us(e.path);const t=e.file;t&&await Ge(async()=>{const s=await R.openBytes(new Uint8Array(await t.arrayBuffer()),e.name);y.set({source:s})})}async function dn(){const[e]=await Jt([{name:"Images and SVG",extensions:["png","jpg","jpeg","webp","gif","bmp","tif","tiff","svg"]}]);e&&await ft(e)}async function Vi(e,t){y.set({tab:"vectorize"}),bt(),await Ge(async()=>{const s=await R.openBytes([...new TextEncoder().encode(e)],t);y.set({source:s})},!1)}window.addEventListener("inkvec:retrace",e=>{const{svg:t,name:s}=e.detail;Vi(t,s)});const Wt=n("header.appbar"),Ze=n("div.body"),Yi=gi(y,{applyPrefs:e=>void qe(e),close:()=>y.set({screen:null})}),Mt=_i(y,{openFile:()=>void dn(),openSample:e=>void Ge(async()=>{const t=await R.openSample(e);y.set({source:t})}),traceNow:()=>void De("final"),cancel:()=>{R.cancelTrace(),y.set({tracing:!1,stageState:y.state.svg?{kind:"cancelled"}:{kind:"empty"}})},retryAt:e=>{y.state.settings.traceSize=e,y.touch("settings"),De("final")},jumpToWorst:()=>Bs(y,Mt.viewer),openDenoiser:()=>un(y),showUpdate:()=>{const e=y.state.update;e&&xt(e.url)},markSeen:()=>void qe({seenFirstRun:!0})},()=>Rs);function js(e){const t=y.state.caps?.presets.find(o=>o.id===e);if(t)return{settings:t.settings,wantsDenoiser:t.wantsDenoiser};const s=y.state.prefs?.saved.find(o=>o.id===e);return s?{settings:s.settings,wantsDenoiser:!1}:null}function es(){const e=y.state.preset;return e&&js(e)?.settings||Os}function Vs(e){y.set({settings:{...e.settings},preset:e.preset,colourGroups:[...e.colourGroups]}),_e()}const Ys={setPreset:e=>{const t=js(e);t&&(y.set({preset:e,settings:{...t.settings}}),t.wantsDenoiser&&y.state.caps?.denoiser.supported&&!y.state.caps.denoiser.installed&&y.set({stageState:{kind:"denoiserMissing"}}),_e())},savePreset:e=>{const t=y.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:{...y.state.settings}}];qe({saved:o}).then(()=>{const a=y.state.prefs?.saved.find(i=>i.id===s);a?(y.set({preset:s}),q(`Saved as "${a.name}".`,{kind:"good"})):q("The preset tray is full. Forget one and try again.",{kind:"bad"})})},deletePreset:e=>{const t=y.state.prefs;t&&(qe({saved:t.saved.filter(s=>s.id!==e)}),y.state.preset===e&&y.set({preset:null}))},baseSettings:es,changeSetting:(e,t)=>{Qn(y.state.settings,e,t),y.touch("settings");const s=y.state.caps?.denoiser;e==="cleanUpDamage"&&t!=="off"&&s?.supported&&!s.installed&&y.set({stageState:{kind:"denoiserMissing"}}),_e()},resetGroup:e=>{const t=es();for(const s of y.state.caps?.controls??[])s.group===e&&Qn(y.state.settings,s.key,t[s.key]);y.touch("settings"),_e()},traceNow:()=>void De("final"),cancel:()=>{R.cancelTrace(),y.set({tracing:!1,stageState:y.state.svg?{kind:"cancelled"}:{kind:"empty"}})},snap:e=>void Zi(e),setColourGroups:e=>{y.set({colourGroups:e,paletteSelection:[]}),_e()},openExport:()=>Ps(y,Ct,qs),copySvg:async()=>{y.state.svg&&(await vt(y.state.svg),q("SVG copied. Paste straight into Figma or Illustrator."))},saveCard:()=>Ea(y),openDenoiser:()=>un(y),jumpToWorst:()=>Bs(y,Mt.viewer),backToAuto:()=>{const e=y.state.auto;e&&Vs({settings:e.settings,preset:e.preset,colourGroups:y.state.colourGroups})},hideAutoNote:()=>y.set({autoNoteHidden:!0}),openWizard:()=>He.open()},Ct=ra(y,Ys),Zs={...Ys,restore:Vs,setOnOpen:e=>void qe({onOpen:e}),markSeen:()=>void qe({seenFirstRun:!0})},He=Pa(y,Ct,Zs,Hs);Mt.mount(Aa(y,Zs,()=>He.open()));async function Zi(e){const t=y.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())];y.set({snaps:i});try{const r=await Ws(s);if(y.state.result!==s)return;y.set({svg:r.svg,palette:r.inks})}catch(r){y.state.snaps===i&&y.set({snaps:o}),q(String(r),{kind:"bad"})}}let Ut=null,jt=null,ts=null;function bt(){const e=y.state;e.tab==="vectorize"?N(Ze,Mt.el,Ct):e.tab==="minify"?(Ut??=Ga(y),N(Ze,Ut)):e.tab==="fabricate"?(jt??=Ua(y),N(Ze,jt)):(ts??=_a(y),N(Ze,ts))}function ns(){const e=y.state;N(Wt,n("span.brand",null,os(18),wt),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:()=>y.set({tab:t})},s))),n("div",{style:{marginLeft:"auto",display:"flex",alignItems:"center",gap:"2px"}},n("button.btn.ghost.compact",{onclick:()=>void dn()},"Open…"),null,n("button.btn.ghost.compact",{onclick:()=>y.set({screen:"settings"})},"Settings"),n("button.btn.ghost.compact",{onclick:()=>y.set({screen:"about"})},"About"),n("button.btn.ghost.compact",{title:"Before and after on real logos, and how Inkvec compares",onclick:()=>y.set({screen:"showcase"})},"Showcase"),n("button.btn.ghost.compact",{title:"The user guide (F1)",onclick:()=>rn(Es(y.state))},"Help"),n("div.sep"),et())),Wt.setAttribute("data-tauri-drag-region","")}async function qe(e){const t=y.state.prefs;if(!t)return;const s={...t,...e},o=await R.savePrefs(s);y.set({prefs:o}),Vt(o.theme)}function Vt(e){const t=e==="dark"||e==="system"&&window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.dataset.theme=t?"dark":"light"}function Ji(e){const t=e.metaKey||e.ctrlKey,s=e.target instanceof HTMLInputElement||e.target instanceof HTMLTextAreaElement;if(t&&e.key.toLowerCase()==="o"){e.preventDefault(),dn();return}if(t&&e.key.toLowerCase()==="e"&&y.state.svg){e.preventDefault(),Ps(y,Ct,qs);return}if(t&&e.key===","){e.preventDefault(),y.set({screen:"settings"});return}if(!s){if(t&&/^[1-8]$/.test(e.key)){const o=y.state.caps?.presets[Number(e.key)-1];o&&(e.preventDefault(),y.set({preset:o.id,settings:{...o.settings}}),_e());return}e.key==="Escape"&&(y.state.screen?y.set({screen:null}):He.isOpen()?He.close(!0):y.state.chooser?y.set({chooser:!1}):y.state.tracing&&(R.cancelTrace(),y.set({tracing:!1,stageState:y.state.svg?{kind:"cancelled"}:{kind:"empty"}})))}}async function Xi(){{lo(y,e=>void Ki(e.map(ds)));return}}async function Ki(e){if(!e.length)return;const t=e.filter(s=>s.name.toLowerCase().endsWith(".svg"));if(t.length===1&&e.length===1&&y.state.tab==="vectorize"){await ft(t[0]);return}if(t.length){const s=y.state.tab==="fabricate"?"fabricate":"minify";y.set({tab:s}),bt(),await(s==="fabricate"?jt:Ut)?.acceptDropped?.(t[0]);return}if(e.length>1){q(`${e.length} files dropped. ${wt} opens one image at a time; the desktop app has Batch.`),await ft(e[0]);return}y.set({tab:"vectorize"}),await ft(e[0])}async function Qi(){const e=document.getElementById("app");if(!e)return;e.append(Wt,Ze,Yi),uo(e),mo(),ns(),bt(),y.on(["tab","source","prefs","caps"],ns),y.on(["tab"],bt),window.addEventListener("keydown",Ji),Ia(y),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>Vt(y.state.prefs?.theme??"system")),document.addEventListener("click",a=>{const i=a.target?.closest?.("a[href]"),r=i?.getAttribute("href")??"";i&&/^(https?:|mailto:)/i.test(r)&&(a.preventDefault(),xt(r).catch(()=>q("Could not open the link in your browser.",{kind:"bad"})))}),await Me.traceStage(({generation:a,name:i,ms:r})=>{if(a!==Gs)return;const l=[...y.state.liveStages],c=l.find(d=>d.name===i);c?c.ms+=r:l.push({name:i,ms:r}),y.set({liveStages:l})}),await Me.traceDone(({generation:a,outcome:i})=>{a===y.state.generation&&Ui(i,a)}),await Me.openPath(a=>void ss(a)),await Me.batchRow(a=>{const i=y.state.batch,r=i.rows.findIndex(l=>l.id===a.id);r>=0&&(i.rows[r]=a),y.touch("batch")}),await Me.batchTotals(a=>{y.state.batch.totals=a,y.touch("batch")}),await Me.batchFinished(a=>{const i=y.state.batch;i.rows=a,i.running=!1,i.paused=!1,y.touch("batch");const r=a.filter(l=>l.state==="failed").length;q(r?`Batch finished · ${r} failed. Sort failures first to see them.`:`Batch finished · ${a.filter(l=>l.state==="done").length} written.`,{kind:r?"bad":"good"})});const[t,s]=await Promise.all([R.capabilities(),R.loadPrefs()]);y.set({caps:t,prefs:s,settings:s.trace}),Vt(s.theme),Rt(`Engine ${t.engineVersion} · ${t.buildTarget}`,.55),Rs=await R.listSamples(),y.touch("source"),Rt("Preparing the workspace…",.85),await Xi(),Rt("Ready",1),R.appReady().catch(()=>{});const o=await R.launchPath().catch(()=>null);o&&ss(o),po().then(er),s.checkUpdates&&R.checkUpdate().then(a=>y.set({update:a})).catch(()=>{})}async function er(e){e&&(y.set({tab:"vectorize",screen:null}),"sample"in e?await Ge(async()=>y.set({source:await R.openSample(e.sample)})):await Ge(async()=>y.set({source:await R.openBytes(e.bytes,e.name)})))}async function ss(e){y.set({tab:"vectorize",screen:null}),await Us(e)}function Rt(e,t){R.startupProgress(e,t).catch(()=>{})}Qi().catch(e=>{R.appReady().catch(()=>{});const t=document.getElementById("app");t&&N(t,n("div.firstrun",null,n("span.serif",{style:{fontSize:"24px"}},`${wt} could not start`),n("span.faint",{style:{maxWidth:"50ch",textAlign:"center"}},String(e))))});