plotline 0.1.0.dev0__cp311-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plotline/__init__.py +30 -0
- plotline/_core.pyd +0 -0
- plotline/_core.pyi +58 -0
- plotline/cli/__init__.py +0 -0
- plotline/cli/main.py +294 -0
- plotline/mcp_server.py +313 -0
- plotline/py.typed +0 -0
- plotline/registry.py +16 -0
- plotline/session.py +400 -0
- plotline/types.py +130 -0
- plotline/web/THIRD-PARTY-LICENSES.txt +1492 -0
- plotline/web/assets/index-B3NFwj0a.css +1 -0
- plotline/web/assets/index-BcHuSLsL.js +1 -0
- plotline/web/assets/plotline_wasm_bg-BxSp_00A.wasm +0 -0
- plotline/web/assets/worker-DB9SFtUV.js +1 -0
- plotline/web/index.html +15 -0
- plotline/web/lucide-preact-LICENSE.txt +43 -0
- plotline/web/preact-LICENSE.txt +21 -0
- plotline-0.1.0.dev0.dist-info/METADATA +159 -0
- plotline-0.1.0.dev0.dist-info/RECORD +24 -0
- plotline-0.1.0.dev0.dist-info/WHEEL +4 -0
- plotline-0.1.0.dev0.dist-info/entry_points.txt +2 -0
- plotline-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
- plotline-0.1.0.dev0.dist-info/sboms/plotline-py.cyclonedx.json +2984 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color-scheme:light dark;--bg: light-dark(#f3f4f7, #0d0f13);--surface: light-dark(#ffffff, #161920);--field: light-dark(#f5f6f9, #1e222b);--hover: light-dark(#eceef3, #262b36);--border: light-dark(#e2e5eb, #2a2f3a);--text: light-dark(#171a20, #e7e9ef);--muted: light-dark(#5f6878, #8f97a8);--accent: light-dark(#4f46e5, #8b8ff9);--on-accent: light-dark(#ffffff, #0d0f13);--accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);--edge: color-mix(in srgb, var(--text) 30%, transparent);--ok: light-dark(#15803d, #4ade80);--danger: light-dark(#b42318, #f97066);--backdrop: light-dark(#e4e6eb, #08090c);--shadow: 0 1px 2px rgb(0 0 0 / .05), 0 2px 8px rgb(0 0 0 / .04);--radius: 12px;accent-color:var(--accent)}:root[data-theme=light]{color-scheme:light}:root[data-theme=dark]{color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;height:100%}body{font:13px/1.45 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased}*{scrollbar-width:thin;scrollbar-color:var(--border) transparent}.app{display:grid;grid-template-columns:clamp(230px,22vw,300px) minmax(0,1fr) clamp(230px,22vw,300px);height:100vh}.app>aside{display:flex;flex-direction:column;gap:12px;overflow-y:auto;padding:12px}.app>aside>label{margin:0 4px}.stages{display:flex;flex-direction:column;gap:12px}.app>main{display:flex;flex-direction:column;gap:12px;min-width:0;min-height:0;padding:12px 0}.brand{display:flex;align-items:center;gap:8px;margin:2px 4px;font-size:18px;font-weight:700;letter-spacing:-.02em}.brand svg{width:22px;height:22px;color:var(--accent)}fieldset,details.stage,details.panel{margin:0;min-width:0;padding:12px 14px 14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}details.stage:not([open]),details.panel:not([open]){padding-bottom:12px}legend,details.stage>summary,details.panel>summary{margin:0;padding:0;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}legend{float:left;width:100%;margin-bottom:8px}legend+*{clear:both}details.stage[open]>summary,details.panel[open]>summary{margin-bottom:8px}.toolbar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:6px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}.toolbar label{margin:0}.toolbar .group{display:flex;gap:6px;align-items:center}.toolbar .group+.group{padding-left:6px;border-left:1px solid var(--border)}.toolbar .group.end{margin-left:auto;padding-left:0;border-left:none}.toolbar .zoom{min-width:3.5em;color:var(--muted);font-variant-numeric:tabular-nums}.preview{position:relative;flex:1;min-height:0;overflow:hidden;background:var(--backdrop);border:1px solid var(--border);border-radius:var(--radius)}.preview canvas:focus-visible{outline-offset:-2px}.preview .hint{position:absolute;top:10px;left:50%;max-width:calc(100% - 20px);margin:0;padding:6px 12px;transform:translate(-50%);font-size:12px;color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadow);pointer-events:none}.skip{position:absolute;top:8px;left:8px;z-index:1;padding:6px 12px;color:var(--on-accent);background:var(--accent);border-radius:8px;transform:translateY(-200%)}.skip:focus{transform:none}.toolbar:focus{outline:none}footer{padding:8px 14px;font-size:12px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}.status{display:flex;flex-wrap:wrap;align-items:center;gap:2px 14px}.status ol{display:flex;flex-wrap:wrap;gap:2px 14px;margin:0;padding:0;list-style:none}.status li{display:flex;align-items:center;gap:6px;color:var(--muted)}.status li:before{content:"";width:7px;height:7px;flex:none;border-radius:50%;background:var(--border)}.status li[data-state=done]{color:var(--text)}.status li[data-state=done]:before{background:var(--ok)}.status li[data-state=running]{color:var(--text)}.status li[data-state=running]:before{background:var(--accent);animation:pulse 1s ease-in-out infinite alternate}@keyframes pulse{to{opacity:.3}}.stats{margin:4px 0 0;font-variant-numeric:tabular-nums}label{display:flex;align-items:center;gap:8px;margin:6px 0;color:var(--text)}label>input:not([type=checkbox],[type=range]),label>select{margin-left:auto}label>input[type=text]{flex:1;min-width:0}label>select{min-width:0;max-width:65%}label:has(>textarea),label:has(>.wide){flex-wrap:wrap}label>textarea,label>.wide{flex-basis:100%;max-width:none;margin-left:0}label:has(>.row){flex-direction:column;align-items:stretch;gap:4px}.row{display:flex;justify-content:space-between;align-items:center;gap:4px}.row>span{display:flex;align-items:center;gap:4px;white-space:nowrap}.row input[type=number]{width:5.5em}input[type=range]{display:block;width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=color],[type=file]),select,textarea{height:28px;padding:0 8px;font:inherit;color:inherit;background:var(--field);border:1px solid var(--border);border-radius:7px;transition:border-color .15s,box-shadow .15s}textarea{height:auto;padding:6px 8px;font:12px/1.4 ui-monospace,Cascadia Code,Consolas,monospace;resize:vertical}input[type=number]{width:6.5em;font-variant-numeric:tabular-nums}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}input[type=checkbox],input[type=range]{margin:0}input[type=checkbox]{width:15px;height:15px;flex:none}input[type=color]{width:36px;height:26px;margin-left:auto;padding:2px;background:var(--field);border:1px solid var(--border);border-radius:7px;cursor:pointer}input[type=color]::-webkit-color-swatch-wrapper{padding:0}input[type=color]::-webkit-color-swatch{border:none;border-radius:4px}input[type=color]::-moz-color-swatch{border:none;border-radius:4px}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}button,.button,.chip{display:inline-flex;align-items:center;gap:6px;height:30px;margin:0;padding:0 12px;font:inherit;font-weight:500;color:var(--text);background:var(--field);border:1px solid var(--border);border-radius:8px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s,border-color .15s,color .15s}button:hover:not(:disabled),.button:hover,.chip:hover{background:var(--hover)}button:active:not(:disabled){transform:translateY(1px)}button:disabled{opacity:.45;cursor:not-allowed}input:disabled{opacity:.55;cursor:not-allowed}button svg,.button svg,.chip svg{width:15px;height:15px;flex:none}button.primary{color:var(--on-accent);background:var(--accent);border-color:transparent}button.primary:hover:not(:disabled){background:color-mix(in srgb,var(--accent) 85%,var(--text))}button.icon{width:30px;padding:0;justify-content:center}.button:has(:focus-visible),.chip:has(:focus-visible){outline:2px solid var(--accent);outline-offset:2px}.chip:has(:checked){color:var(--accent);background:var(--accent-soft);border-color:color-mix(in srgb,var(--accent) 45%,transparent)}fieldset>button,fieldset>.button{margin:4px 6px 0 0}.control{margin-bottom:4px}.help{margin:-2px 0 8px;color:var(--muted);font-size:12px}details details{margin-top:6px}details details>summary{color:var(--muted);font-size:12px;font-weight:500;cursor:pointer}summary{cursor:pointer}summary:hover{color:var(--text)}.error{margin:4px 0;color:var(--danger)}.warnings{margin:8px 0 0;padding-left:1.2em;color:var(--muted)}.suggestions{margin:0;padding:0;list-style:none}.suggestions li+li{margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}.suggestions p{margin:0 0 6px}.suggestions button{margin:0}.grid{display:grid;grid-template-columns:1fr 1fr;gap:6px 10px}.grid label{flex-direction:column;align-items:stretch;gap:2px;margin:0;color:var(--muted);font-size:12px}.grid label>span{display:flex;align-items:center;gap:4px;color:var(--text)}.grid select,.grid input[type=number]{width:100%;max-width:none;min-width:0;margin-left:0}.muted{color:var(--muted)}.swatch{width:14px;height:14px;flex:none;border:1px solid var(--edge);border-radius:4px}.name{flex:1;min-width:0;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}.pen{display:flex;align-items:center;gap:8px;padding:4px 0;font-variant-numeric:tabular-nums}progress{width:7em;height:6px}dialog.modal{width:min(720px,100vw - 32px);height:min(70vh,560px);padding:0;color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}dialog.modal[open]{display:flex;flex-direction:column}dialog.modal::backdrop{background:#0006}dialog.modal header{display:flex;align-items:center;justify-content:space-between;padding:8px 8px 8px 16px;border-bottom:1px solid var(--border)}dialog.modal h2{display:flex;align-items:center;gap:8px;margin:0;font-size:15px}dialog.modal h2 svg{width:18px;height:18px;color:var(--accent)}dialog.modal .body{display:grid;grid-template-columns:140px minmax(0,1fr);grid-template-rows:minmax(0,1fr);flex:1;min-height:0}dialog.modal nav{display:flex;flex-direction:column;gap:2px;padding:8px;border-right:1px solid var(--border)}dialog.modal nav button{justify-content:flex-start;background:none;border-color:transparent}dialog.modal nav button[aria-current]{color:var(--accent);background:var(--accent-soft)}dialog.modal .fields{overflow-y:auto;padding:8px 16px 16px}dialog.modal h3{margin:16px 0 4px;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}dialog.modal.wide{width:min(860px,100vw - 32px);height:80vh}dialog.modal.wide .body{grid-template-columns:220px minmax(0,1fr)}dialog.modal nav .help{margin:8px 4px 0}ol.pens{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}ol.pens button{width:100%;gap:8px;font-variant-numeric:tabular-nums}dialog.modal .actions{display:flex;gap:4px;margin-top:8px}.inks{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:6px 0}.inks>span{margin-right:auto}.inks input[type=color]{margin-left:4px}button.ink{width:22px;height:22px;padding:0;border-radius:50%;border:1px solid var(--edge)}button.ink:hover:not(:disabled){filter:brightness(1.1)}button.ink[aria-pressed=true]{outline:2px solid var(--accent);outline-offset:2px}.filter{display:flex;gap:6px;margin-bottom:8px}.filter input{flex:1;min-width:0}.model{display:flex;flex-direction:column;gap:4px;padding:6px 0;border-top:1px solid var(--border)}.sizes{display:flex;flex-wrap:wrap;gap:4px}.sizes button{height:26px;padding:0 8px;font-variant-numeric:tabular-nums}.sizes button .muted{font-size:11px;font-weight:400}.sizes button[aria-pressed=true]{color:var(--accent);background:var(--accent-soft);border-color:color-mix(in srgb,var(--accent) 45%,transparent)}@media(max-width:600px){dialog.modal .body,dialog.modal.wide .body{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}dialog.modal nav{flex-direction:row;flex-wrap:wrap;border-right:none;border-bottom:1px solid var(--border)}}dialog.tour{position:fixed;z-index:2;width:min(320px,100vw - 16px);margin:0;padding:12px 14px;color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 4px 24px #0000002e}dialog.tour header{display:flex;align-items:center;justify-content:space-between;gap:8px}dialog.tour h2{margin:0;font-size:15px}dialog.tour p{margin:6px 0 12px}dialog.tour .actions{display:flex;align-items:center;gap:6px}dialog.tour .count{margin-right:auto;color:var(--muted);font-size:12px}[data-tour-active]{outline:2px solid var(--accent);outline-offset:2px}dl{display:grid;grid-template-columns:auto 1fr;gap:2px 12px;margin:0}dd{margin:0}.loading{display:grid;place-items:center;height:100vh;margin:0;color:var(--muted)}@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var ke,$,ut,B,qe,ht,_t,Te,de,oe,pt,Fe,Ee,Ne,mt,fe={},ge=[],sn=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,re=Array.isArray;function U(t,e){for(var n in e)t[n]=e[n];return t}function Oe(t){t&&t.parentNode&&t.parentNode.removeChild(t)}function ye(t,e,n){var r,a,i,c={};for(i in e)i=="key"?r=e[i]:i=="ref"?a=e[i]:c[i]=e[i];if(arguments.length>2&&(c.children=arguments.length>3?ke.call(arguments,2):n),typeof t=="function"&&t.defaultProps!=null)for(i in t.defaultProps)c[i]===void 0&&(c[i]=t.defaultProps[i]);return ue(t,c,r,a,null)}function ue(t,e,n,r,a){var i={type:t,props:e,key:n,ref:r,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:a??++ut,__i:-1,__u:0};return a==null&&$.vnode!=null&&$.vnode(i),i}function L(t){return t.children}function he(t,e){this.props=t,this.context=e}function Z(t,e){if(e==null)return t.__?Z(t.__,t.__i+1):null;for(var n;e<t.__k.length;e++)if((n=t.__k[e])!=null&&n.__e!=null)return n.__e;return typeof t.type=="function"?Z(t):null}function cn(t){if(t.__P&&t.__d){var e=t.__v,n=e.__e,r=[],a=[],i=U({},e);i.__v=e.__v+1,$.vnode&&$.vnode(i),We(t.__P,i,e,t.__n,t.__P.namespaceURI,32&e.__u?[n]:null,r,n??Z(e),!!(32&e.__u),a),i.__v=e.__v,i.__.__k[i.__i]=i,wt(r,i,a),e.__e=e.__=null,i.__e!=n&&ft(i)}}function ft(t){if((t=t.__)!=null&&t.__c!=null)return t.__e=t.__c.base=null,t.__k.some(function(e){if(e!=null&&e.__e!=null)return t.__e=t.__c.base=e.__e}),ft(t)}function Ie(t){(!t.__d&&(t.__d=!0)&&B.push(t)&&!ve.__r++||qe!=$.debounceRendering)&&((qe=$.debounceRendering)||ht)(ve)}function ve(){try{for(var t,e=1;B.length;)B.length>e&&B.sort(_t),t=B.shift(),e=B.length,cn(t)}finally{B.length=ve.__r=0}}function gt(t,e,n,r,a,i,c,d,u,s,p){var v,l,m,y,k,w,f=r&&r.__k||ge,_=e.length;for(u=dn(n,e,f,u,_),v=0;v<_;v++)(m=n.__k[v])!=null&&(l=m.__i!=-1&&f[m.__i]||fe,m.__i=v,w=We(t,m,l,a,i,c,d,u,s,p),y=m.__e,m.ref&&l.ref!=m.ref&&(l.ref&&Ue(l.ref,null,m),p.push(m.ref,m.__c||y,m)),k==null&&y!=null&&(k=y),4&m.__u?(u=yt(m,u,t),l.__e&&(l.__e=null)):typeof m.type=="function"&&w!==void 0?u=w:y&&(u=y.nextSibling),m.__u&=-7);return n.__e=k,u}function dn(t,e,n,r,a){var i,c,d,u,s,p=n.length,v=p,l=0;for(t.__k=new Array(a),i=0;i<a;i++)(c=e[i])!=null&&typeof c!="boolean"&&typeof c!="function"?(typeof c=="string"||typeof c=="number"||typeof c=="bigint"||c.constructor==String?c=t.__k[i]=ue(null,c,null,null,null):re(c)?c=t.__k[i]=ue(L,{children:c},null,null,null):c.constructor===void 0&&c.__b>0?c=t.__k[i]=ue(c.type,c.props,c.key,c.ref?c.ref:null,c.__v):t.__k[i]=c,u=i+l,c.__=t,c.__b=t.__b+1,d=null,(s=c.__i=un(c,n,u,v))!=-1&&(v--,(d=n[s])&&(d.__u|=2)),d==null||d.__v==null?(s==-1&&(a>p?l--:a<p&&l++),typeof c.type!="function"&&(c.__u|=4)):s!=u&&(s==u-1?l--:s==u+1?l++:(s>u?l--:l++,c.__u|=4))):t.__k[i]=null;if(v)for(i=0;i<p;i++)(d=n[i])!=null&&(2&d.__u)==0&&(d.__e==r&&(r=Z(d)),St(d,d));return r}function yt(t,e,n){var r,a;if(typeof t.type=="function"){for(r=t.__k,a=0;r&&a<r.length;a++)r[a]&&(r[a].__=t,e=yt(r[a],e,n));return e}t.__e!=e&&(e&&t.type&&!e.parentNode&&(e=Z(t)),e=n.insertBefore(t.__e,e||null));do e=e&&e.nextSibling;while(e!=null&&e.nodeType==8);return e}function vt(t,e){return e=e||[],t==null||typeof t=="boolean"||(re(t)?t.some(function(n){vt(n,e)}):e.push(t)),e}function un(t,e,n,r){var a,i,c,d=t.key,u=t.type,s=e[n],p=s!=null&&(2&s.__u)==0;if(s===null&&d==null||p&&d==s.key&&u==s.type)return n;if(r>(p?1:0)){for(a=n-1,i=n+1;a>=0||i<e.length;)if((s=e[c=a>=0?a--:i++])!=null&&(2&s.__u)==0&&d==s.key&&u==s.type)return c}return-1}function Ge(t,e,n){e[0]=="-"?t.setProperty(e,n??""):t[e]=n==null?"":typeof n!="number"||sn.test(e)?n:n+"px"}function ae(t,e,n,r,a){var i,c;e:if(e=="style")if(typeof n=="string")t.style.cssText=n;else{if(typeof r=="string"&&(t.style.cssText=r=""),r)for(e in r)n&&e in n||Ge(t.style,e,"");if(n)for(e in n)r&&n[e]==r[e]||Ge(t.style,e,n[e])}else if(e[0]=="o"&&e[1]=="n")i=e!=(e=e.replace(pt,"$1")),c=e.toLowerCase(),e=c in t||e=="onFocusOut"||e=="onFocusIn"?c.slice(2):e.slice(2),t.l||(t.l={}),t.l[e+i]=n,n?r?n[oe]=r[oe]:(n[oe]=Fe,t.addEventListener(e,i?Ne:Ee,i)):t.removeEventListener(e,i?Ne:Ee,i);else{if(a=="http://www.w3.org/2000/svg")e=e.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(e!="width"&&e!="height"&&e!="href"&&e!="list"&&e!="form"&&e!="tabIndex"&&e!="download"&&e!="rowSpan"&&e!="colSpan"&&e!="role"&&e!="popover"&&e in t)try{t[e]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&e[4]!="-"?t.removeAttribute(e):t.setAttribute(e,e=="popover"&&n==1?"":n))}}function Je(t){return function(e){if(this.l){var n=this.l[e.type+t];if(e[de]==null)e[de]=Fe++;else if(e[de]<n[oe])return;return n($.event?$.event(e):e)}}}function We(t,e,n,r,a,i,c,d,u,s){var p,v,l,m,y,k,w,f,_,S,g,b,x,P,I,V,H=e.type;if(e.constructor!==void 0)return null;128&n.__u&&(u=!!(32&n.__u),i=[d=e.__e=n.__e]),(p=$.__b)&&p(e);e:if(typeof H=="function"){v=c.length;try{if(_=e.props,S=H.prototype&&H.prototype.render,g=(p=H.contextType)&&r[p.__c],b=p?g?g.props.value:p.__:r,n.__c?f=(l=e.__c=n.__c).__=l.__E:(S?e.__c=l=new H(_,b):(e.__c=l=new he(_,b),l.constructor=H,l.render=_n),g&&g.sub(l),l.state||(l.state={}),l.__n=r,m=l.__d=!0,l.__h=[],l._sb=[]),S&&l.__s==null&&(l.__s=l.state),S&&H.getDerivedStateFromProps!=null&&(l.__s==l.state&&(l.__s=U({},l.__s)),U(l.__s,H.getDerivedStateFromProps(_,l.__s))),y=l.props,k=l.state,l.__v=e,m)S&&H.getDerivedStateFromProps==null&&l.componentWillMount!=null&&l.componentWillMount(),S&&l.componentDidMount!=null&&l.__h.push(l.componentDidMount);else{if(S&&H.getDerivedStateFromProps==null&&_!==y&&l.componentWillReceiveProps!=null&&l.componentWillReceiveProps(_,b),e.__v==n.__v||!l.__e&&l.shouldComponentUpdate!=null&&l.shouldComponentUpdate(_,l.__s,b)===!1){e.__v!=n.__v&&(l.props=_,l.state=l.__s,l.__d=!1),e.__e=n.__e,e.__k=n.__k,e.__k.some(function(F){F&&(F.__=e)}),ge.push.apply(l.__h,l._sb),l._sb=[],l.__h.length&&c.push(l),d=Z(n);break e}l.componentWillUpdate!=null&&l.componentWillUpdate(_,l.__s,b),S&&l.componentDidUpdate!=null&&l.__h.push(function(){l.componentDidUpdate(y,k,w)})}if(l.context=b,l.props=_,l.__P=t,l.__e=!1,x=$.__r,P=0,S)l.state=l.__s,l.__d=!1,x&&x(e),p=l.render(l.props,l.state,l.context),ge.push.apply(l.__h,l._sb),l._sb=[];else do l.__d=!1,x&&x(e),p=l.render(l.props,l.state,l.context),l.state=l.__s;while(l.__d&&++P<25);l.state=l.__s,l.getChildContext!=null&&(r=U(U({},r),l.getChildContext())),S&&!m&&l.getSnapshotBeforeUpdate!=null&&(w=l.getSnapshotBeforeUpdate(y,k)),I=p!=null&&p.type===L&&p.key==null?kt(p.props.children):p,d=gt(t,re(I)?I:[I],e,n,r,a,i,c,d,u,s),l.base=e.__e,e.__u&=-161,l.__h.length&&c.push(l),f&&(l.__E=l.__=null)}catch(F){if(c.length=v,e.__v=null,u||i!=null){if(F.then){for(e.__u|=u?160:128;d&&d.nodeType==8&&d.nextSibling;)d=d.nextSibling;i!=null&&(i[i.indexOf(d)]=null),e.__e=d}else if(i!=null)for(V=i.length;V--;)Oe(i[V])}else e.__e=n.__e;e.__k==null&&(e.__k=n.__k||[]),F.then||bt(e),$.__e(F,e,n)}}else i==null&&e.__v==n.__v?(e.__k=n.__k,e.__e=n.__e):d=e.__e=hn(n.__e,e,n,r,a,i,c,u,s);return(p=$.diffed)&&p(e),128&e.__u?void 0:d}function bt(t){t&&(t.__c&&(t.__c.__e=!0),t.__k&&t.__k.some(bt))}function wt(t,e,n){for(var r=0;r<n.length;r++)Ue(n[r],n[++r],n[++r]);$.__c&&$.__c(e,t),t.some(function(a){try{t=a.__h,a.__h=[],t.some(function(i){i.call(a)})}catch(i){$.__e(i,a.__v)}})}function kt(t){return typeof t!="object"||t==null||t.__b>0?t:re(t)?t.map(kt):t.constructor!==void 0?null:U({},t)}function hn(t,e,n,r,a,i,c,d,u){var s,p,v,l,m,y,k,w=n.props||fe,f=e.props,_=e.type;if(_=="svg"?a="http://www.w3.org/2000/svg":_=="math"?a="http://www.w3.org/1998/Math/MathML":a||(a="http://www.w3.org/1999/xhtml"),i!=null){for(s=0;s<i.length;s++)if((m=i[s])&&"setAttribute"in m==!!_&&(_?m.localName==_:m.nodeType==3)){t=m,i[s]=null;break}}if(t==null){if(_==null)return document.createTextNode(f);t=document.createElementNS(a,_,f.is&&f),d&&($.__m&&$.__m(e,i),d=!1),i=null}if(_==null)w===f||d&&t.data==f||(t.data=f);else{if(i=_=="textarea"&&f.defaultValue!=null?null:i&&ke.call(t.childNodes),!d&&i!=null)for(w={},s=0;s<t.attributes.length;s++)w[(m=t.attributes[s]).name]=m.value;for(s in w)m=w[s],s=="dangerouslySetInnerHTML"?v=m:s=="children"||s in f||s=="value"&&"defaultValue"in f||s=="checked"&&"defaultChecked"in f||ae(t,s,null,m,a);for(s in f)m=f[s],s=="children"?l=m:s=="dangerouslySetInnerHTML"?p=m:s=="value"?y=m:s=="checked"?k=m:d&&typeof m!="function"||w[s]===m||ae(t,s,m,w[s],a);if(p)d||v&&(p.__html==v.__html||p.__html==t.innerHTML)||(t.innerHTML=p.__html),e.__k=[];else if(v&&(t.innerHTML=""),gt(e.type=="template"?t.content:t,re(l)?l:[l],e,n,r,_=="foreignObject"?"http://www.w3.org/1999/xhtml":a,i,c,i?i[0]:n.__k&&Z(n,0),d,u),i!=null)for(s=i.length;s--;)Oe(i[s]);d&&_!="textarea"||(s="value",_=="progress"&&y==null?t.removeAttribute("value"):y!=null&&(y!==t[s]||_=="progress"&&!y||_=="option"&&y!=w[s])&&ae(t,s,y,w[s],a),s="checked",k!=null&&k!=t[s]&&ae(t,s,k,w[s],a))}return t}function Ue(t,e,n){try{if(typeof t=="function"){var r=typeof t.__u=="function";r&&t.__u(),r&&e==null||(t.__u=t(e))}else t.current=e}catch(a){$.__e(a,n)}}function St(t,e,n){var r,a;if($.unmount&&$.unmount(t),(r=t.ref)&&(r.current&&r.current!=t.__e||Ue(r,null,e)),(r=t.__c)!=null){if(r.componentWillUnmount)try{r.componentWillUnmount()}catch(i){$.__e(i,e)}r.base=r.__P=r.__n=null}if(r=t.__k)for(a=0;a<r.length;a++)r[a]&&St(r[a],e,n||typeof t.type!="function");n||Oe(t.__e),t.__c=t.__=t.__e=void 0}function _n(t,e,n){return this.constructor(t,n)}function pn(t,e,n){var r,a,i,c;e==document&&(e=document.documentElement),$.__&&$.__(t,e),a=(r=!1)?null:e.__k,i=[],c=[],We(e,t=e.__k=ye(L,null,[t]),a||fe,fe,e.namespaceURI,a?null:e.firstChild?ke.call(e.childNodes):null,i,a?a.__e:e.firstChild,r,c),wt(i,t,c),t.props.children=null}function mn(t){function e(n){var r,a;return this.getChildContext||(r=new Set,(a={})[e.__c]=this,this.getChildContext=function(){return a},this.componentWillUnmount=function(){r=null},this.shouldComponentUpdate=function(i){this.props.value!=i.value&&r.forEach(function(c){c.__e=!0,Ie(c)})},this.sub=function(i){r.add(i);var c=i.componentWillUnmount;i.componentWillUnmount=function(){r&&r.delete(i),c&&c.call(i)}}),n.children}return e.__c="__cC"+mt++,e.__=t,e.Provider=e.__l=(e.Consumer=function(n,r){return n.children(r)}).contextType=e,e}ke=ge.slice,$={__e:function(t,e,n,r){for(var a,i,c;e=e.__;)if((a=e.__c)&&!a.__)try{if((i=a.constructor)&&i.getDerivedStateFromError!=null&&(a.setState(i.getDerivedStateFromError(t)),c=a.__d),a.componentDidCatch!=null&&(a.componentDidCatch(t,r||{}),c=a.__d),c)return a.__E=a}catch(d){t=d}throw t}},ut=0,he.prototype.setState=function(t,e){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=U({},this.state),typeof t=="function"&&(t=t(U({},n),this.props)),t&&U(n,t),t!=null&&this.__v&&(e&&this._sb.push(e),Ie(this))},he.prototype.forceUpdate=function(t){this.__v&&(this.__e=!0,t&&this.__h.push(t),Ie(this))},he.prototype.render=L,B=[],ht=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,_t=function(t,e){return t.__v.__b-e.__v.__b},ve.__r=0,Te=Math.random().toString(8),de="__d"+Te,oe="__a"+Te,pt=/(PointerCapture)$|Capture$/i,Fe=0,Ee=Je(!1),Ne=Je(!0),mt=0;var fn=0;function o(t,e,n,r,a,i){e||(e={});var c,d,u=e;if("ref"in u)for(d in u={},e)d=="ref"?c=e[d]:u[d]=e[d];var s={type:t,props:u,key:n,ref:c,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--fn,__i:-1,__u:0,__source:a,__self:i};if(typeof t=="function"&&(c=t.defaultProps))for(d in c)u[d]===void 0&&(u[d]=c[d]);return $.vnode&&$.vnode(s),s}const be=(...t)=>t.filter((e,n,r)=>!!e&&e.trim()!==""&&r.indexOf(e)===n).join(" ").trim();function gn(t,e,n=[]){throw new Error("[lucide]: iconNode is required when icon name is used")}const yn=t=>{let e="",n=!1;for(const r of t){if(r==="-"||r==="_"||r<=" "){n=e.length>0;continue}e.length===0?e+=r.toLowerCase():e+=n?r.toUpperCase():r,n=!1}return e};const vn=t=>{const e=yn(t);return e.charAt(0).toUpperCase()+e.slice(1)};const G={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};function Me(t){return t!=null}function bn(t,e={}){const n=e.attributeNames??{},r=l=>n[l]??l,a=t.size??t.width??G.width,i=t.size??t.height??G.height,c=t.aliases?.filter(l=>typeof l=="string"&&l.trim()!=="").map(l=>`lucide-${l}`)??[],d=[...t.name?[`lucide-${t.name}`]:[],...c],u=e.className?.split(" ").filter(Boolean)??[],s=e.includeDefaultClasses===!1?be(...u):be("lucide",...d,...u),p=e.absoluteStrokeWidth?Number(e.strokeWidth??G["stroke-width"])*Number(t.size??t.width??G.width)/Number(e.size??e.width??G.width):e.strokeWidth??G["stroke-width"];return["svg",{...Object.entries(G).reduce((l,[m,y])=>(l[r(m)]=y,l),{}),..."color"in e&&e.color&&{[r("stroke")]:e.color},..."size"in e&&Me(e.size)&&{[r("width")]:e.size,[r("height")]:e.size},..."width"in e&&Me(e.width)&&{[r("width")]:e.width},..."height"in e&&Me(e.height)&&{[r("height")]:e.height},[r("stroke-width")]:p,...s&&{[r("class")]:s},[r("viewBox")]:`0 0 ${a} ${i}`,...e.hasA11yProp===!1?{[r("aria-hidden")]:"true"}:{},..."attributes"in e&&e.attributes},t.node.map(l=>{const[m,y,k]=l,w=e.nonScalingStroke?{[r("vector-effect")]:"non-scaling-stroke",...y}:y;return k?[m,w,k]:[m,w]})]}const wn=t=>{for(const e in t)if(e.startsWith("aria-")||e==="role"||e==="title")return!0;return!1};var Y,z,Pe,Ze,we=0,xt=[],E=$,Ye=E.__b,Xe=E.__r,Ke=E.diffed,Qe=E.__c,et=E.unmount,tt=E.__;function ie(t,e){E.__h&&E.__h(z,t,we||e),we=0;var n=z.__H||(z.__H={__:[],__h:[]});return t>=n.__.length&&n.__.push({}),n.__[t]}function T(t){return we=1,kn(Ct,t)}function kn(t,e,n){var r=ie(Y++,2);if(r.t=t,!r.__c&&(r.__=[Ct(void 0,e),function(d){var u=r.__N?r.__N[0]:r.__[0],s=r.t(u,d);u!==s&&(r.__N=[s,r.__[1]],r.__c.setState({}))}],r.__c=z,!z.__f)){var a=function(d,u,s){if(!r.__c.__H)return!0;var p=!1,v=r.__c.props!==d;if(r.__c.__H.__.some(function(m){if(m.__N){p=!0;var y=m.__[0];m.__=m.__N,m.__N=void 0,y!==m.__[0]&&(v=!0)}}),i){var l=i.call(this,d,u,s);return p?l||v:l}return!p||v};z.__f=!0;var i=z.shouldComponentUpdate,c=z.componentWillUpdate;z.componentWillUpdate=function(d,u,s){if(this.__e){var p=i;i=void 0,a(d,u,s),i=p}c&&c.call(this,d,u,s)},z.shouldComponentUpdate=a}return r.__N||r.__}function O(t,e){var n=ie(Y++,3);!E.__s&&Re(n.__H,e)&&(n.__=t,n.u=e,z.__H.__h.push(n))}function Sn(t,e){var n=ie(Y++,4);!E.__s&&Re(n.__H,e)&&(n.__=t,n.u=e,z.__h.push(n))}function R(t){return we=5,xn(function(){return{current:t}},[])}function xn(t,e){var n=ie(Y++,7);return Re(n.__H,e)&&(n.__=t(),n.__H=e,n.__h=t),n.__}function Cn(t){var e=z.context[t.__c],n=ie(Y++,9);return n.c=t,e?(n.__==null&&(n.__=!0,e.sub(z)),e.props.value):t.__}function Tn(){for(var t;t=xt.shift();){var e=t.__H;if(t.__P&&e)try{e.__h.some(_e),e.__h.some(He),e.__h=[]}catch(n){e.__h=[],E.__e(n,t.__v)}}}E.__b=function(t){z=null,Ye&&Ye(t)},E.__=function(t,e){t&&e.__k&&e.__k.__m&&(t.__m=e.__k.__m),tt&&tt(t,e)},E.__r=function(t){Xe&&Xe(t),Y=0;var e=(z=t.__c).__H;e&&(Pe===z?(e.__h=[],z.__h=[],e.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(e.__h.some(_e),e.__h.some(He),e.__h=[],Y=0)),Pe=z},E.diffed=function(t){Ke&&Ke(t);var e=t.__c;e&&e.__H&&(e.__H.__h.length&&(xt.push(e)!==1&&Ze===E.requestAnimationFrame||((Ze=E.requestAnimationFrame)||Mn)(Tn)),e.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0)})),Pe=z=null},E.__c=function(t,e){e.some(function(n){try{n.__h.some(_e),n.__h=n.__h.filter(function(r){return!r.__||He(r)})}catch(r){e.some(function(a){a.__h&&(a.__h=[])}),e=[],E.__e(r,n.__v)}}),Qe&&Qe(t,e)},E.unmount=function(t){et&&et(t);var e,n=t.__c;n&&n.__H&&(n.__H.__.some(function(r){try{_e(r)}catch(a){e=a}}),n.__H=void 0,e&&E.__e(e,n.__v))};var nt=typeof requestAnimationFrame=="function";function Mn(t){var e,n=function(){clearTimeout(r),nt&&cancelAnimationFrame(e),setTimeout(t)},r=setTimeout(n,35);nt&&(e=requestAnimationFrame(n))}function _e(t){var e=z,n=t.__c;typeof n=="function"&&(t.__c=void 0,n()),z=e}function He(t){var e=z;t.__c=t.__(),z=e}function Re(t,e){return!t||t.length!==e.length||e.some(function(n,r){return n!==t[r]})}function Ct(t,e){return typeof e=="function"?e(t):e}const Pn=mn({size:24,color:"currentColor",strokeWidth:2,absoluteStrokeWidth:!1,nonScalingStroke:!1,class:""}),$n=()=>Cn(Pn);const An=({color:t,size:e,width:n,height:r,strokeWidth:a,absoluteStrokeWidth:i,nonScalingStroke:c,children:d,iconNode:u=[],icon:s={node:u,aliases:[],size:24},class:p="",...v})=>{const{size:l=24,strokeWidth:m=2,absoluteStrokeWidth:y=!1,nonScalingStroke:k=!1,color:w="currentColor",class:f=""}=$n()??{},[_,S,g=[]]=bn(s,{color:t??w,width:n??e??l,height:r??e??l,strokeWidth:a??m,absoluteStrokeWidth:i??y,nonScalingStroke:c??k,className:be(f,p),hasA11yProp:!!d||wn(v),attributes:v});return ye(_,{...S},[...g.map(([b,x])=>ye(b,x)),...vt(d)])};function D(t,e,n=[]){const r=typeof t=="string"?gn(t,e,n):t,a=({class:i="",className:c="",children:d,...u})=>ye(An,{...u,icon:r,class:be(i,c)},d);return r.name&&(a.displayName=vn(r.name)),a}const Tt={name:"arrow-down",size:24,node:[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]};Tt.node;const zn=D(Tt);const Mt={name:"arrow-up",size:24,node:[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]};Mt.node;const Dn=D(Mt);const Pt={name:"check",size:24,node:[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]};Pt.node;const En=D(Pt);const $t={name:"circle-question-mark",size:24,node:[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],aliases:["help-circle","circle-help"]};$t.node;const Nn=D($t);const At={name:"copy",size:24,node:[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]};At.node;const In=D(At);const zt={name:"download",size:24,node:[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]]};zt.node;const pe=D(zt);const Dt={name:"folder-open",size:24,node:[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]};Dt.node;const Hn=D(Dt);const Et={name:"image-up",size:24,node:[["path",{d:"M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21",key:"9csbqa"}],["path",{d:"m14 19.5 3-3 3 3",key:"9vmjn0"}],["path",{d:"M17 22v-5.5",key:"1aa6fl"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}]]};Et.node;const Ln=D(Et);const Nt={name:"image",size:24,node:[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",key:"1xmnt7"}]]};Nt.node;const Fn=D(Nt);const It={name:"maximize",size:24,node:[["path",{d:"M8 3H5a2 2 0 0 0-2 2v3",key:"1dcmit"}],["path",{d:"M21 8V5a2 2 0 0 0-2-2h-3",key:"1e4gt3"}],["path",{d:"M3 16v3a2 2 0 0 0 2 2h3",key:"wsl5sc"}],["path",{d:"M16 21h3a2 2 0 0 0 2-2v-3",key:"18trek"}]]};It.node;const On=D(It);const Ht={name:"monitor",size:24,node:[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2",key:"48i651"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21",key:"1svkeh"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21",key:"vw1qmm"}]]};Ht.node;const Wn=D(Ht);const Lt={name:"moon",size:24,node:[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]]};Lt.node;const Un=D(Lt);const Ft={name:"pen-line",size:24,node:[["path",{d:"M13 21h8",key:"1jsn5i"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}]],aliases:["edit-3"]};Ft.node;const Ot=D(Ft);const Wt={name:"play",size:24,node:[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]]};Wt.node;const Rn=D(Wt);const Ut={name:"plus",size:24,node:[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]};Ut.node;const jn=D(Ut);const Rt={name:"route",size:24,node:[["circle",{cx:"6",cy:"19",r:"3",key:"1kj8tv"}],["path",{d:"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15",key:"1d8sl"}],["circle",{cx:"18",cy:"5",r:"3",key:"gq8acd"}]]};Rt.node;const Bn=D(Rt);const jt={name:"save",size:24,node:[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]};jt.node;const Vn=D(jt);const Bt={name:"settings",size:24,node:[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]};Bt.node;const Vt=D(Bt);const qt={name:"sparkles",size:24,node:[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]],aliases:["stars"]};qt.node;const qn=D(qt);const Gt={name:"square",size:24,node:[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]};Gt.node;const Gn=D(Gt);const Jt={name:"sun",size:24,node:[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]};Jt.node;const Jn=D(Jt);const Zt={name:"x",size:24,node:[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]};Zt.node;const je=D(Zt);class Zn{constructor(e){this.worker=e,e.onmessage=n=>{const r=n.data;if("type"in r)return this.onEvent(r);this.pending.get(r.id)?.(r),this.pending.delete(r.id)},e.onerror=n=>{this.failed=n.message||"the engine stopped";for(const[r,a]of this.pending)a({id:r,ok:!1,error:this.failed});this.pending.clear()}}worker;onEvent=()=>{};nextId=0;failed=null;pending=new Map;call(e,n=[],r=[]){if(this.failed!==null)return Promise.reject(new Error(this.failed));const a=++this.nextId;return new Promise((i,c)=>{this.pending.set(a,d=>d.ok?i({state:d.state,value:d.value}):c(new Error(d.error))),this.worker.postMessage({id:a,method:e,args:n},r)})}}const Yt="image/png,image/jpeg,image/webp",Yn=new Set(Yt.split(","));function ot(t){return Array.from(t?.files??[]).find(e=>Yn.has(e.type))}function Xn(t,e,n){const r=n.toLowerCase().split(/\s+/).filter(Boolean),a=new Map;for(const i of t){const c=`${i.kind} ${i.name}`.toLowerCase();e&&i.kind!==e||!r.every(d=>c.includes(d))||a.set(i.model,[...a.get(i.model)??[],i])}return[...a]}const $e=[["A5",148,210],["A4",210,297],["A3",297,420],["Letter",215.9,279.4],["Tabloid",279.4,431.8]],se=(t,e)=>Math.abs(t-e)<=.05;function Kn({canvas:t,error:e,onChange:n}){const{width_mm:r,height_mm:a}=t,i=$e.find(([,s,p])=>se(s,r)&&se(p,a)||se(s,a)&&se(p,r)),c=s=>n({...t,...s}),d=s=>{const[,p,v]=$e.find(l=>l[0]===s);c(r>a?{width_mm:v,height_mm:p}:{width_mm:p,height_mm:v})},u=(s,p)=>o("label",{children:[p,o("span",{children:[o("input",{type:"number",min:"0",step:"any",value:t[s],onChange:v=>c({[s]:v.currentTarget.valueAsNumber})})," mm"]})]});return o("fieldset",{"data-tour":"page",children:[o("legend",{children:"Page"}),o("div",{class:"grid",children:[o("label",{children:["Size",o("select",{value:i?.[0]??"custom",onChange:s=>d(s.currentTarget.value),children:[$e.map(([s])=>o("option",{value:s,children:s})),!i&&o("option",{value:"custom",children:"custom"})]})]}),o("label",{children:["Orientation",o("select",{value:r>a?"landscape":"portrait",onChange:()=>c({width_mm:a,height_mm:r}),children:[o("option",{value:"portrait",children:"portrait"}),o("option",{value:"landscape",children:"landscape"})]})]}),u("width_mm","Width"),u("height_mm","Height"),u("margin_mm","Margin"),o("label",{children:["Fit",o("select",{value:t.fit,onChange:s=>c({fit:s.currentTarget.value}),children:[o("option",{value:"fit",children:"fit"}),o("option",{value:"fill",children:"fill"})]})]})]}),e&&o("p",{class:"error",children:e})]})}function Qn({suggestions:t,error:e,onApply:n}){return o("fieldset",{children:[o("legend",{children:"Suggestions"}),o("ul",{class:"suggestions",children:t.map(r=>{const a=r.patch;return o("li",{children:[o("p",{children:r.reason}),a&&o("button",{type:"button","aria-label":`Apply: ${r.reason}`,onClick:()=>n(a),children:[o(En,{})," Apply"]})]},r.kind)})}),e&&o("p",{class:"error",children:e})]})}function eo({pens:t,hidden:e,error:n,mode:r,onEdit:a,onToggle:i,children:c}){return o("fieldset",{"data-tour":"pens",children:[o("legend",{children:"Pens"}),t.map((d,u)=>o("div",{class:"pen",children:[o("span",{class:"swatch",style:{background:d.color}}),o("span",{class:"name",title:d.name,children:d.name}),o("span",{class:"muted",children:[d.width_mm," mm"]}),o("input",{type:"checkbox","aria-label":`Show ${d.name} in the preview`,title:"Show in the preview",checked:!e.has(u),onChange:()=>i(u)})]})),n&&o("p",{class:"error",children:n}),c,(r??"one_pen")==="one_pen"&&t.length>1&&o("p",{class:"help",children:"Pen use is set to one pen, so only the first pen draws."}),e.size>0&&o("p",{class:"help",children:"A hidden pen is only hidden in the preview: it still exports."}),o("button",{type:"button",onClick:a,children:[o(Ot,{})," Edit pens"]})]})}function Xt({opens:t,title:e,icon:n,wide:r,children:a}){const i=R(null);O(()=>{t>0&&!i.current.open&&i.current.showModal()},[t]);const c=`${e.toLowerCase()}-title`;return o("dialog",{ref:i,class:r?"modal wide":"modal","aria-labelledby":c,children:[o("header",{children:[o("h2",{id:c,children:[n," ",e]}),o("button",{type:"button",class:"icon",title:"Close","aria-label":"Close",onClick:()=>i.current.close(),children:o(je,{})})]}),o("div",{class:"body",children:a})]})}function to({opens:t,pens:e,bank:n,inks:r,error:a,onChange:i}){const[c,d]=T(0),[u,s]=T(""),[p,v]=T(""),l=Math.min(c,e.length-1),m=e[l],y=g=>i(e.map((b,x)=>x===l?{...b,...g}:b)),k=(g,b)=>{i(g),d(b)},w=g=>{const b=[...e];[b[l],b[l+g]]=[b[l+g],b[l]],k(b,l+g)},f=[...new Set(n.map(g=>g.kind))],_=Xn(n,u,p),S=r.find(g=>g.color.toLowerCase()===m.color.toLowerCase());return o(Xt,{opens:t,title:"Pens",icon:o(Ot,{}),wide:!0,children:[o("nav",{children:[o("ol",{class:"pens",children:e.map((g,b)=>o("li",{children:o("button",{type:"button","aria-current":b===l?"true":void 0,onClick:()=>d(b),children:[o("span",{class:"swatch",style:{background:g.color}}),o("span",{class:"name",children:g.name}),o("span",{class:"muted",children:g.width_mm})]})}))}),o("div",{class:"actions",children:[o("button",{type:"button",class:"icon",title:"Add pen","aria-label":"Add pen",onClick:()=>k([...e,{name:`Pen ${e.length+1}`,width_mm:.3,color:"#000000"}],e.length),children:o(jn,{})}),o("button",{type:"button",class:"icon",title:"Duplicate pen","aria-label":"Duplicate pen",onClick:()=>k([...e.slice(0,l+1),{...m},...e.slice(l+1)],l+1),children:o(In,{})}),o("button",{type:"button",class:"icon",title:"Move up: draws earlier","aria-label":"Move up",disabled:l===0,onClick:()=>w(-1),children:o(Dn,{})}),o("button",{type:"button",class:"icon",title:"Move down: draws later","aria-label":"Move down",disabled:l===e.length-1,onClick:()=>w(1),children:o(zn,{})}),o("button",{type:"button",class:"icon",title:"Remove pen","aria-label":"Remove pen",disabled:e.length===1,onClick:()=>k(e.filter((g,b)=>b!==l),Math.max(0,l-1)),children:o(je,{})})]}),o("p",{class:"help",children:"Pens plot in this order, and with Pen use set to one pen the first draws alone. List light inks first."})]}),o("div",{class:"fields",children:[a&&o("p",{class:"error",children:a}),o("h3",{children:["Pen ",l+1]}),o("label",{children:["Name ",o("input",{type:"text",value:m.name,onChange:g=>y({name:g.currentTarget.value})})]}),me("Line width","mm",m.width_mm,g=>y({width_mm:g})),o("div",{class:"inks",role:"group","aria-label":"Ink colour",children:[o("span",{children:"Colour"}),r.map(g=>o("button",{type:"button",class:"ink",title:g.name,"aria-label":g.name,"aria-pressed":g===S,style:{background:g.color},onClick:()=>y({color:g.color})})),o("input",{type:"color",title:"Any colour","aria-label":"Any colour",value:m.color,onChange:g=>y({color:g.currentTarget.value})})]}),o("h3",{children:"Pen bank"}),o("p",{class:"help",children:"Choosing a size gives this pen its name and line width, as the maker publishes it. The colour stays."}),o("div",{class:"filter",children:[o("select",{"aria-label":"Kind",value:u,onChange:g=>s(g.currentTarget.value),children:[o("option",{value:"",children:"every kind"}),f.map(g=>o("option",{value:g,children:g}))]}),o("input",{type:"search",placeholder:"Search, such as micron 005","aria-label":"Search the bank",value:p,onInput:g=>v(g.currentTarget.value)})]}),_.length===0&&o("p",{class:"help",children:"No pen in the bank matches."}),_.map(([g,b])=>o("div",{class:"model",children:[o("span",{children:[g,o("span",{class:"muted",children:[" · ",b[0].kind]})]}),o("div",{class:"sizes",children:b.map(x=>{const P=x.name.slice(g.length+1);return o("button",{type:"button","aria-pressed":x.name===m.name&&x.width_mm===m.width_mm,title:`${x.name}, ${x.width_mm} mm line`,onClick:()=>y({name:x.name,width_mm:x.width_mm}),children:[P,Number(P)!==x.width_mm&&o("span",{class:"muted",children:x.width_mm})]})})})]}))]})]})}function no({stats:t}){const e=Math.round(t?.estimated_seconds??0);return t?t.path_count===0?o("p",{class:"stats",children:"No marks at these settings. Try a lower threshold, or the black, white and gamma settings under Image."}):o("p",{class:"stats",children:[t.path_count," paths · pen down ",t.pen_down_mm.toFixed(1)," mm · pen up ",t.pen_up_mm.toFixed(1)," mm · about ",Math.floor(e/60)," min ",e%60," s to plot"]}):o("p",{class:"stats",children:"No drawing yet"})}function oo({stages:t,progress:e,run:n,stopping:r}){const a=c=>(e[c]?.progress??0)>=1,i=n.state==="running"?t.find(c=>!a(c.name)):void 0;return o("div",{class:"status",children:[o("span",{class:n.error?"error":void 0,children:[n.id>0?`Run ${n.id}: `:"",r?"stopping…":n.state,n.error?`: ${n.error}`:""]}),o("ol",{children:t.map(c=>{const d=e[c.name];return a(c.name)?o("li",{"data-state":"done",title:d.label,children:[c.label,": done"]}):c===i?o("li",{"data-state":"running",children:[c.label,": ",d?.label??"running"," ",o("progress",{value:d?.progress??0,max:1})]}):o("li",{children:[c.label,": waiting"]})})})]})}function ro({warnings:t,error:e,onSave:n,onLoad:r}){return o("fieldset",{"data-tour":"presets",children:[o("legend",{children:"Presets"}),o("button",{type:"button",onClick:n,children:[o(Vn,{})," Save preset"]}),o("label",{class:"button",children:[o(Hn,{})," Load preset",o("input",{class:"visually-hidden",type:"file",accept:"application/json,.json",onChange:a=>{const i=a.currentTarget.files?.[0];a.currentTarget.value="",i&&r(i)}})]}),e&&o("p",{class:"error",children:e}),t.length>0&&o("ul",{class:"warnings",children:t.map(a=>o("li",{children:a}))})]})}function io({drawing:t,layers:e,machine:n,onExport:r,onGcode:a}){return o("fieldset",{"data-tour":"export",children:[o("legend",{children:"Export"}),o("button",{type:"button",disabled:!t,onClick:()=>r("svg"),children:[o(pe,{})," SVG"]}),o("button",{type:"button",disabled:!t,onClick:()=>r("json"),children:[o(pe,{})," JSON"]}),n?e.map((i,c)=>o("div",{children:o("button",{type:"button",disabled:!t,onClick:()=>a(c),children:[o(pe,{})," G-code: ",i.name]})})):o("p",{class:"help",children:"For G-code, set up a machine in Settings."})]})}const lo=[["z_down_mm","Pen down Z","mm","The Z the pen draws at."],["z_up_mm","Pen up Z","mm","The Z it travels at between paths."],["z_safe_mm","Safe Z","mm","The Z each file lifts to before its first move and before its end lines."],["z_feed","Z feed","mm/min","How fast the pen moves up and down."],["settle_ms","Settle","ms","The pause after each Z move."]],ao=[["draw_feed","Draw"],["travel_feed","Travel"]],so=[["width","Width"],["height","Height"],["x","Offset X"],["y","Offset Y"]],co=[["draw_mm_s","Draw","mm/s",.1],["travel_mm_s","Travel","mm/s",.1],["pen_lift_s","Pen lift","s",0]],uo=["Machine","Pen lift","G-code","Plot time"],ho={name:"My machine",area:{x:0,y:0,width:200,height:200},z_down_mm:0,z_up_mm:5,z_safe_mm:10,draw_feed:1500,travel_feed:3e3,z_feed:600,settle_ms:0,start_gcode:"",end_gcode:""};function me(t,e,n,r){return o("label",{children:[t," ",o("input",{type:"number",step:"any",value:n,onChange:a=>{const i=a.currentTarget.valueAsNumber;Number.isFinite(i)?r(i):a.currentTarget.value=String(n)}})," ",e]})}function _o({opens:t,machine:e,error:n,speeds:r,onPick:a,onEdit:i,onSpeeds:c}){const[d,u]=T("Machine"),s=e===null?"none":"current",p=o("p",{children:"Choose a machine first"}),v={Machine:o(L,{children:[o("label",{children:["Machine"," ",o("select",{value:s,onChange:l=>{const m=l.currentTarget.value;m==="none"?a(null):m==="new"&&a(ho)},children:[o("option",{value:"none",children:"None: export SVG for your plotter's software"}),e&&o("option",{value:"current",children:e.name}),o("option",{value:"new",children:"New machine"})]})]}),e&&o(L,{children:[o("label",{children:["Name ",o("input",{type:"text",value:e.name,onChange:l=>i({...e,name:l.currentTarget.value})})]}),o("h3",{children:"Drawable area"}),so.map(([l,m])=>me(m,"mm",e.area[l],y=>i({...e,area:{...e.area,[l]:y}}))),o("p",{class:"help",children:"Offset is the machine position of the area's corner with the smallest X and Y. The page is centred in the area."}),o("h3",{children:"Feeds"}),ao.map(([l,m])=>me(m,"mm/min",e[l],y=>i({...e,[l]:y})))]})]}),"Pen lift":e?lo.map(([l,m,y,k])=>o(L,{children:[me(m,y,e[l],w=>i({...e,[l]:w})),o("p",{class:"help",children:k})]})):p,"G-code":e?o(L,{children:[o("label",{children:["Start ",o("textarea",{rows:6,value:e.start_gcode,onChange:l=>i({...e,start_gcode:l.currentTarget.value})})]}),o("label",{children:["End ",o("textarea",{rows:6,value:e.end_gcode,onChange:l=>i({...e,end_gcode:l.currentTarget.value})})]}),o("p",{class:"help",children:"plotline writes G21 and G90 before the start lines, and lifts to safe Z before the end lines. Home the machine before mounting the pen: the files have no G28."})]}):p,"Plot time":o(L,{children:[co.map(([l,m,y,k])=>o("label",{children:[m," ",o("input",{type:"number",min:k,step:"any",disabled:e!==null,value:e?Number(r[l].toFixed(3)):r[l],onChange:w=>{const f=w.currentTarget.valueAsNumber;Number.isFinite(f)&&(k===0?f>=0:f>0)?c({...r,[l]:f}):w.currentTarget.value=String(r[l])}})," ",y]})),e&&o("p",{class:"help",children:"From the machine's feeds and pen lift."})]})}[d];return o(Xt,{opens:t,title:"Settings",icon:o(Vt,{}),children:[o("nav",{children:uo.map(l=>o("button",{type:"button","aria-current":l===d?"true":void 0,onClick:()=>u(l),children:l}))}),o("div",{class:"fields",children:[n&&o("p",{class:"error",children:n}),v]})]})}function Kt(t){return Math.max(0,t.length-1)}function po(t,e,n){for(let r=0;r<Kt(e);r++)n(e[r],e[r+1])}function mo(t,e,n,r,a=20){const i=Math.max(1e-6,Math.min((n-2*a)/t,(r-2*a)/e));return{scale:i,x:(n-t*i)/2,y:(r-e*i)/2}}function fo(t,e,n,r){return{scale:t.scale*e,x:n-(n-t.x)*e,y:r-(r-t.y)*e}}const go=t=>Math.round(t*25.4*100/96),Ae=2e5;function rt(t,e,n,r){if(!(r<=n)){t.moveTo(e[2*n],e[2*n+1]),r-n===1&&t.lineTo(e[2*n]+1e-4,e[2*n+1]);for(let a=n+1;a<r;a++)t.lineTo(e[2*a],e[2*a+1])}}function yo(t,e,n,r,a,i,c){const{result:d,pens:u}=e,s=e.running?e.frames:[],p=s.some(Boolean),v=!p&&d?d.canvas:e.canvas,l=t.canvas;l.width!==Math.round(r*i)&&(l.width=Math.round(r*i)),l.height!==Math.round(a*i)&&(l.height=Math.round(a*i)),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,l.width,l.height),t.setTransform(i*n.scale,0,0,i*n.scale,i*n.x,i*n.y),t.lineCap="round",t.lineJoin="round";const m=(_,S)=>{t.strokeStyle=_.color,t.lineWidth=_.width_mm,t.stroke(S)},y=(_,S,g)=>{t.strokeStyle=_.color,t.lineWidth=_.width_mm;for(let b=0;b<S;b+=Ae){const x=new Path2D;for(let P=b;P<Math.min(S,b+Ae);P++)g(x,P);t.stroke(x)}};t.save(),t.shadowColor="rgba(0, 0, 0, 0.35)",t.shadowBlur=8*i,t.shadowOffsetY=2*i,t.fillStyle="#fff",t.fillRect(0,0,v.width_mm,v.height_mm),t.restore(),t.strokeStyle="#ccc",t.lineWidth=1/n.scale;const k=v.margin_mm;if(t.strokeRect(k,k,v.width_mm-2*k,v.height_mm-2*k),e.showSource&&c&&d?.tone){const _=d.tone;t.globalAlpha=.3,t.drawImage(c,_.x,_.y,_.w,_.h),t.globalAlpha=1}const w=_=>u[_]??u[0],f=p?s.flatMap((_,S)=>_?.coords&&_.offsets&&!e.hidden.has(S)?[{pen:w(S),coords:_.coords,offsets:_.offsets}]:[]):(d?.layers??[]).filter((_,S)=>!e.hidden.has(S));t.save(),t.globalCompositeOperation="multiply",s.forEach((_,S)=>{const g=_?.points;g&&!e.hidden.has(S)&&y(w(S),g.length/2,(b,x)=>rt(b,g,x,x+1))});for(const _ of f)y(_.pen,Kt(_.offsets),(S,g)=>rt(S,_.coords,_.offsets[g],_.offsets[g+1]));if(t.restore(),e.showTravel&&!p){const _={color:"red",width_mm:1/n.scale};let S=new Path2D,g=0,b=null;for(const x of f)po(x.coords,x.offsets,(P,I)=>{I<=P||(b&&(S.moveTo(b[0],b[1]),S.lineTo(x.coords[2*P],x.coords[2*P+1]),++g%Ae===0&&(m(_,S),S=new Path2D)),b=[x.coords[2*I-2],x.coords[2*I-1]])});m(_,S)}e.hasImage||(t.fillStyle="#888",t.font="6px sans-serif",t.textAlign="center",t.textBaseline="middle",t.fillText("Open, drop or paste an image to start",v.width_mm/2,v.height_mm/2))}function vo(t){const{canvas:e,result:n,view:r,onView:a,fits:i}=t,c=R(null),d=R(null),u=R(null),[s,p]=T({w:0,h:0}),[v,l]=T(null),m=v?.result===n?v.bitmap:null,y=(f,_)=>a(()=>mo(e.width_mm,e.height_mm,f,_));O(()=>{const f=c.current,_=new ResizeObserver(()=>{p({w:f.clientWidth,h:f.clientHeight}),y(f.clientWidth,f.clientHeight)});return _.observe(f),()=>_.disconnect()},[e.width_mm,e.height_mm,i]),O(()=>{if(!n?.tone)return;const{ink:f,width_px:_,height_px:S}=n.tone,g=new Uint8ClampedArray(f.length*4);for(let P=0;P<f.length;P++)g[4*P]=g[4*P+1]=g[4*P+2]=f[P],g[4*P+3]=255;let b=!1,x;return createImageBitmap(new ImageData(g,_,S)).then(P=>{if(b)return P.close();x=P,l({result:n,bitmap:P})}),()=>{b=!0,x?.close()}},[n]),O(()=>{const f=requestAnimationFrame(()=>{const _=d.current.getContext("2d");yo(_,t,r,s.w,s.h,window.devicePixelRatio||1,m)});return()=>cancelAnimationFrame(f)},[t.canvas,t.frames,t.result,t.running,t.showSource,t.showTravel,t.hidden,t.pens,t.hasImage,r,s,m]);const k=(f,_,S)=>a(g=>fo(g,Math.min(200,Math.max(.05,g.scale*f))/g.scale,_,S)),w={ArrowLeft:[40,0],ArrowRight:[-40,0],ArrowUp:[0,40],ArrowDown:[0,-40]};return o("div",{ref:c,style:{position:"relative",height:"100%"},children:o("canvas",{ref:d,tabIndex:0,"aria-label":"Preview of the drawing: arrow keys pan, plus and minus zoom, 0 fits the page",style:{position:"absolute",inset:0,width:"100%",height:"100%",touchAction:"none",cursor:"grab"},onKeyDown:f=>{if(f.key==="+"||f.key==="=")k(1.25,s.w/2,s.h/2);else if(f.key==="-")k(.8,s.w/2,s.h/2);else if(f.key==="0")y(s.w,s.h);else if(w[f.key])a(_=>({..._,x:_.x+w[f.key][0],y:_.y+w[f.key][1]}));else return;f.preventDefault()},onWheel:f=>{f.preventDefault();const _=f.currentTarget.getBoundingClientRect(),S=f.deltaMode===1?33:f.deltaMode===2?_.height:1;k(Math.exp(-f.deltaY*S*.002),f.clientX-_.left,f.clientY-_.top)},onPointerDown:f=>{f.currentTarget.setPointerCapture(f.pointerId),u.current={x:f.clientX,y:f.clientY}},onPointerMove:f=>{const _=u.current;if(!_)return;const S=f.clientX-_.x,g=f.clientY-_.y;u.current={x:f.clientX,y:f.clientY},a(b=>({...b,x:b.x+S,y:b.y+g}))},onPointerUp:()=>u.current=null,onPointerCancel:()=>u.current=null})})}const bo=/^#\/(\$defs|definitions)\/(.+)$/,J=t=>typeof t=="string"?t:null,te=t=>typeof t=="number"?t:null;function wo(t,e){const n=Array.isArray(t.allOf)&&t.allOf.length===1?t.allOf[0].$ref:void 0,r=bo.exec(J(t.$ref)??J(n)??"");return r?e[r[1]]?.[r[2]]??null:null}function it(t){const e=r=>String(r).replaceAll("_"," ");if(Array.isArray(t.enum))return t.enum.map(r=>({value:String(r),label:e(r),title:null}));const n=t.oneOf;return Array.isArray(n)&&n.every(r=>"const"in r)?n.map(r=>({value:String(r.const),label:e(r.const),title:J(r.description)})):null}function ko(t,e,n){const r={key:t,label:J(e["x-label"])??t.replaceAll("_"," "),help:J(e.description)??"",advanced:e["x-advanced"]===!0},a=J(e["x-unit"]),i=e.default,c=wo(e,n),d=it(e)??(c&&it(c));if(d)return{...r,kind:"select",options:d,default:i==null?null:String(i)};if(e.type==="boolean")return{...r,kind:"checkbox",default:i===!0};if(e.type==="integer"||e.type==="number"){const u=e.type==="integer",s=te(e.minimum),p=te(e.maximum);if(s!==null&&p!==null){const v=te(e["x-step"])??(u?1:(p-s)/100);return{...r,kind:"slider",min:s,max:p,step:v,integer:u,unit:a,default:te(i)??s}}return{...r,kind:"number",integer:u,unit:a,default:te(i)}}return{...r,kind:"text",default:J(i)}}function So(t){const e=t.properties??{};return Object.entries(e).map(([n,r])=>ko(n,r,t))}const lt=[["order",null],["tone","Image"]],at="split";function xo(t){const e=t.filter(r=>r.name!==at&&!lt.some(([a])=>a===r.name)),n=lt.flatMap(([r,a])=>t.filter(i=>i.name===r).map(i=>({...i,label:a??i.label})));return{own:e,shared:n,pens:t.filter(r=>r.name===at)}}function Co({c:t,value:e,help:n,set:r}){const a=d=>r(d.currentTarget.valueAsNumber),i=t.help||void 0;let c;return t.kind==="checkbox"?c=o("label",{title:i,children:[o("input",{type:"checkbox",checked:e===!0,onChange:d=>r(d.currentTarget.checked)})," ",t.label]}):t.kind==="slider"?c=o("label",{title:i,children:[o("span",{class:"row",children:[t.label,o("span",{children:[o("input",{type:"number","aria-label":t.label,min:t.min,max:t.max,step:t.step,value:e,onChange:a})," ",t.unit]})]}),o("input",{type:"range","aria-label":t.label,min:t.min,max:t.max,step:t.step,value:e,onInput:a})]}):t.kind==="number"?c=o("label",{title:i,children:[t.label," ",o("input",{type:"number",step:t.integer?1:"any",value:e,onChange:a})," ",t.unit]}):t.kind==="select"?c=o("label",{title:i,children:[t.label," ",o("select",{value:String(e),onChange:d=>r(d.currentTarget.value),children:t.options.map(d=>o("option",{value:d.value,title:d.title??void 0,children:d.label}))})]}):c=o("label",{title:i,children:[t.label," ",o("input",{type:"text",value:String(e??""),onChange:d=>r(d.currentTarget.value)})]}),o("div",{class:"control",children:[c,n&&t.help&&o("p",{class:"help",children:t.help})]})}function st({stages:t,values:e,errors:n,help:r,part:a="style",onChange:i}){const{own:c,shared:d,pens:u}=xo(t),s=(p,v)=>{const l=So(p.schema);if(l.length===0&&!n[p.name])return null;const m=w=>o(Co,{c:w,value:e[p.name]?.[w.key]??w.default,help:r,set:f=>i(p.name,w.key,f)}),y=l.filter(w=>w.advanced),k=o(L,{children:[n[p.name]&&o("p",{class:"error",children:n[p.name]}),l.filter(w=>!w.advanced).map(m),y.length>0&&o("details",{children:[o("summary",{children:"Advanced"}),y.map(m)]})]});return a==="pens"?o("div",{children:k},p.name):o("details",{class:"stage",open:v,children:[o("summary",{children:p.label}),k]},p.name)};return a==="pens"?o(L,{children:u.map(p=>s(p,!0))}):o(L,{children:[c.map(p=>s(p,!0)),d.map(p=>s(p,!1))]})}const ce=[{title:"Welcome to plotline",text:"plotline turns an image into lines a pen plotter can draw. This tour shows where everything is. Tour in the toolbar opens it again."},{target:"image",title:"Open an image",text:"Open a PNG, JPEG or WebP, or drop or paste one anywhere on the page. Sample image opens a still life to try things on."},{target:"style",title:"Pick a style",text:"Each style turns the image's tone into a different kind of mark. The line under the menu says what the chosen one does."},{target:"settings",title:"Shape the drawing",text:"The style's own settings come first. Path order and Image, with the levels, gamma and blur, are collapsed below. A change reruns only the stages after it."},{target:"run",title:"Run it",text:"Run draws the image with these settings. Stop ends a run after its current step."},{target:"status",title:"Follow the run",text:"Each stage shows its progress here. Under it are the path count, the pen-down and pen-up lengths and an estimate of the plot time."},{target:"preview",title:"Look closely",text:"The preview draws every mark at the pen's real width and colour. Drag to pan and scroll to zoom. With the preview focused, the arrow keys pan, plus and minus zoom and 0 fits the page."},{target:"view",title:"Check the drawing",text:"Source shows the image under the drawing. Travel shows the pen-up moves between paths. Fit shows the whole page again."},{target:"page",title:"Set the page",text:"The paper size, orientation and margin. Fit keeps the whole image on the page; fill covers the drawing area and crops the rest."},{target:"pens",title:"Choose pens",text:"Edit pens opens the pens dialog: pick a maker's pen from the bank at its published line width, or set a width yourself, and choose an ink colour. The tick hides a pen in the preview. Pen use shares the drawing between the pens: light to dark in list order, or each pen by its colour; pens plot in list order, so list light inks first."},{target:"export",title:"Export",text:"SVG writes the drawing in millimetres, one layer per pen that draws, for your plotter's own software. JSON writes the paths for other programs."},{target:"presets",title:"Keep a look",text:"A preset is a small file with the style, page, pens and settings, so you can come back to a look or share it. The page also remembers your last setup."},{target:"machine",title:"Plot from G-code",text:"Settings holds a pen-fitted 3D printer: its drawable area, pen heights and feeds. Once one is set, Export writes a G-code file per pen."},{target:"help",title:"Help on every setting",text:"Hover over a setting's name to read its help, or turn on Show help to see it under every control."}];function To(t,e,n,r,a){const c=u=>Math.max(8,Math.min(u,r-e-8)),d=u=>Math.max(8,Math.min(u,a-n-8));if(t){if(t.bottom+12+n<=a)return{left:c(t.left),top:t.bottom+12};if(t.top-12-n>=0)return{left:c(t.left),top:t.top-12-n};if(t.right+12+e<=r)return{left:t.right+12,top:d(t.top)};if(t.left-12-e>=0)return{left:t.left-12-e,top:d(t.top)}}return{left:(r-e)/2,top:(a-n)/2}}const ct="plotline.tour";function Mo(){try{return localStorage.getItem(ct)?!1:(localStorage.setItem(ct,"seen"),!0)}catch{return!1}}function Po({opens:t}){const e=R(null),[n,r]=T(!1),[a,i]=T(0),c=ce[a];O(()=>{t>0&&(i(0),r(!0))},[t]),Sn(()=>{if(!n)return;const u=e.current,s=c.target?document.querySelector(`[data-tour="${c.target}"]`):null;s?.setAttribute("data-tour-active",""),s?.scrollIntoView({block:"nearest"});const p=()=>{const l=s?.getBoundingClientRect(),{left:m,top:y}=To(l&&l.width>0?l:null,u.offsetWidth,u.offsetHeight,innerWidth,innerHeight);u.style.left=`${m}px`,u.style.top=`${y}px`};p(),u.querySelector(".primary")?.focus();const v=l=>l.key==="Escape"&&r(!1);return addEventListener("resize",p),document.addEventListener("keydown",v),()=>{s?.removeAttribute("data-tour-active"),removeEventListener("resize",p),document.removeEventListener("keydown",v)}},[n,a]);const d=a===ce.length-1;return o("dialog",{ref:e,class:"tour",open:n,"aria-labelledby":"tour-title",children:[o("header",{children:[o("h2",{id:"tour-title",children:c.title}),o("button",{type:"button",class:"icon",title:"Close the tour","aria-label":"Close the tour",onClick:()=>r(!1),children:o(je,{})})]}),o("p",{children:c.text}),o("div",{class:"actions",children:[o("span",{class:"count",children:[a+1," of ",ce.length]}),o("button",{type:"button",disabled:a===0,onClick:()=>i(u=>Math.max(0,u-1)),children:"Back"}),o("button",{type:"button",class:"primary",onClick:()=>d?r(!1):i(u=>Math.min(ce.length-1,u+1)),children:d?"Done":"Next"})]})]})}const $o=()=>o("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","aria-hidden":"true",children:[o("path",{d:"M5 17C5 10 10 5 17 5"}),o("circle",{cx:"5",cy:"19",r:"2"}),o("circle",{cx:"19",cy:"5",r:"2"})]}),ze=t=>t instanceof Error?t.message:String(t);function De(t,e,n){const r=URL.createObjectURL(new Blob([e],{type:n})),a=document.createElement("a");a.href=r,a.download=t,a.click(),setTimeout(()=>URL.revokeObjectURL(r))}const Le="plotline.preset";function dt(t){try{t===null?localStorage.removeItem(Le):localStorage.setItem(Le,t)}catch{}}function Ao(){try{return JSON.parse(localStorage.getItem(Le)??"null")}catch{return null}}const Qt="plotline.theme",ne=[["system",Wn,"System theme"],["light",Jn,"Light theme"],["dark",Un,"Dark theme"]];function zo(){try{return localStorage.getItem(Qt)??"system"}catch{return"system"}}function Do(){const[t,e]=T(()=>Math.max(0,ne.findIndex(([i])=>i===document.documentElement.dataset.theme))),[,n,r]=ne[t];return o("button",{type:"button",class:"icon",title:r,"aria-label":r,onClick:()=>{const i=(t+1)%ne.length;document.documentElement.dataset.theme=ne[i][0];try{localStorage.setItem(Qt,ne[i][0])}catch{}e(i)},children:o(n,{})})}class Eo{constructor(e){this.value=e}value;subs=new Set;set(e){this.value=e;for(const n of this.subs)n(e)}}function en(t){const[e,n]=T(t.value);return O(()=>(t.subs.add(n),n(t.value),()=>{t.subs.delete(n)}),[]),e}const tn="plotline.help";function No(){try{return localStorage.getItem(tn)==="1"}catch{return!1}}function Io(t){try{localStorage.setItem(tn,t?"1":"0")}catch{}}function Ho({status:t,machine:e,onExport:n,onGcode:r}){const{stats:a,layers:i}=en(t);return o(io,{drawing:a!==null,layers:i,machine:e,onExport:n,onGcode:r})}function Lo({engine:t,status:e,stages:n,error:r,onOpen:a,onSample:i,onRun:c,onStop:d,onSvg:u,onSettings:s,onTour:p,...v}){const{run:l,stats:m}=en(e),[y,k]=T([]),[w,f]=T(null),[_,S]=T({}),[g,b]=T(!1),[x,P]=T(!1),[I,V]=T({scale:1,x:0,y:0}),[H,F]=T(0),[Se,xe]=T(0),j=l.state==="running"&&Se===l.id,le=m!==null&&v.pens[0].width_mm*I.scale*(window.devicePixelRatio||1)<1,K=R(0);return O(()=>{t.onEvent=M=>{if(M.type==="run"){K.current=M.id,k([]),S({});return}M.id<K.current||(M.type==="frame"?((M.points||M.coords)&&k(A=>{const X=[...A];return X[M.pen]=M,X}),S(A=>({...A,[M.stage]:{label:M.label,progress:M.progress}}))):M.type==="result"?(f(M),e.set({...e.value,layers:M.layers.map(A=>A.pen)})):e.set({run:{id:M.id,state:M.state,error:M.error},stats:M.stats,layers:e.value.layers}))}},[]),o(L,{children:[o("div",{class:"toolbar",id:"toolbar",tabIndex:-1,children:[o("span",{class:"group","data-tour":"image",children:[o("label",{class:"button",children:[o(Ln,{})," Open image",o("input",{class:"visually-hidden",type:"file",accept:Yt,onChange:M=>{const A=M.currentTarget.files?.[0];M.currentTarget.value="",a(A)}})]}),o("button",{type:"button",onClick:i,children:[o(qn,{})," Sample image"]})]}),o("span",{class:"group","data-tour":"run",children:[o("button",{type:"button",class:"primary",disabled:!v.hasImage,onClick:c,children:[o(Rn,{})," Run"]}),o("button",{type:"button",disabled:l.state!=="running"||j,onClick:()=>{xe(l.id),d()},children:[o(Gn,{})," Stop"]})]}),o("span",{class:"group","data-tour":"view",children:[o("label",{class:"chip",children:[o("input",{class:"visually-hidden",type:"checkbox",checked:g,onChange:M=>b(M.currentTarget.checked)}),o(Fn,{})," Source"]}),o("label",{class:"chip",children:[o("input",{class:"visually-hidden",type:"checkbox",checked:x,onChange:M=>P(M.currentTarget.checked)}),o(Bn,{})," Travel"]})]}),o("span",{class:"group",children:[o("button",{type:"button",onClick:()=>F(M=>M+1),children:[o(On,{})," Fit"]}),o("span",{class:"zoom",title:"The preview's size against the paper's",children:[go(I.scale),"%"]})]}),o("span",{class:"group end",children:[o("button",{type:"button",disabled:m===null,onClick:u,children:[o(pe,{})," Export SVG"]}),o("button",{type:"button","data-tour":"machine",onClick:s,children:[o(Vt,{})," Settings"]}),o("button",{type:"button",onClick:p,children:[o(Nn,{})," Tour"]}),o(Do,{})]})]}),r&&o("p",{class:"error",children:r}),o("div",{class:"preview","data-tour":"preview",children:[o(vo,{...v,frames:y,result:w,running:l.state==="running",showSource:g,showTravel:x,view:I,onView:V,fits:H}),le&&o("p",{class:"hint",children:"The pen is under a pixel wide at this zoom, so its marks barely show: zoom in to see them."})]}),o("footer",{"data-tour":"status",children:[o(oo,{stages:n,progress:_,run:l,stopping:j}),o(no,{stats:m})]})]})}function Fo(){const[t,e]=T(null),[n]=T(()=>{const h=new Worker(new URL(""+new URL("worker-DB9SFtUV.js",import.meta.url).href,import.meta.url),{type:"module"});return h.addEventListener("error",C=>e(C.message||"the engine stopped")),new Zn(h)}),[r,a]=T(null),[i,c]=T([]),[d,u]=T([]),[s,p]=T(null),[v]=T(()=>new Eo({run:{id:0,state:"idle",error:null},stats:null,layers:[]})),[l,m]=T([]),[y,k]=T({}),[w,f]=T([]),[_,S]=T({}),[g,b]=T(new Set),[x,P]=T(No),[I,V]=T(0),[H,F]=T(0),[Se,xe]=T(()=>Mo()?1:0),j=R({}),le=R({}),K=R(),M=h=>{p(h),v.set({run:h.run,stats:h.stats,layers:h.layers}),clearTimeout(K.current),K.current=setTimeout(()=>n.call("preset").then(C=>dt(JSON.stringify(C.value)),()=>{}),500)};async function A(h,C=[],N="",W){try{const ee=await n.call(h,C,W);return M(ee.state),k(q=>({...q,[N]:""})),ee.value}catch(ee){k(q=>({...q,[N]:ze(ee)})),n.call("preset").then(q=>M(q.state),()=>{})}}O(()=>{n.call("listPens").then(C=>c(C.value),()=>{}),n.call("listInks").then(C=>u(C.value),()=>{}),n.call("listStyles").then(async({state:C,value:N})=>{a(N);const W=Ao();if(W)try{C=(await n.call("loadPreset",[W])).state}catch{dt(null)}M(C)},C=>e(ze(C)));const h=C=>Ce(ot(C.clipboardData));return document.addEventListener("paste",h),()=>document.removeEventListener("paste",h)},[]);const X=s?.style;O(()=>{X&&A("describe",[X]).then(h=>h&&m(h.stages))},[X]);const nn=JSON.stringify(s?.pens);O(()=>b(new Set),[nn]);function Be(h,C,N){S(W=>({...W,[h]:{...W[h],[C]:N}})),j.current[h]={...j.current[h],[C]:N},clearTimeout(le.current[h]),le.current[h]=setTimeout(async()=>{const W=j.current[h];delete j.current[h],await A("update",[h,W],h),j.current[h]||S(({[h]:ee,...q})=>q)},150)}async function Ce(h){if(h)try{const C=new Uint8Array(await h.arrayBuffer());await A("setImageBytes",[C],"image",[C.buffer])}catch(C){k(N=>({...N,image:ze(C)}))}}async function on(){const h=await A("preset");h&&De("preset.json",JSON.stringify(h,null,2),"application/json")}async function rn(h){let C;try{C=JSON.parse(await h.text())}catch{k(N=>({...N,preset:`${h.name} is not a preset: it is not valid JSON.`}));return}f(await A("loadPreset",[C],"preset")??[])}async function Ve(h){const C=await A(h);C!==void 0&&De(`drawing.${h}`,C,h==="svg"?"image/svg+xml":"application/json")}async function ln(h){const C=await A("gcode",[h]),N=(v.value.layers[h]?.name??String(h+1)).replace(/[^\w.-]+/g,"_");C!==void 0&&De(`plot_${h+1}_${N}.gcode`,C,"text/plain")}async function an(h){await A("setMachine",[h],"machine"),h&&s&&await A("setCanvas",[{...s.canvas,width_mm:h.area.width,height_mm:h.area.height}],"machine")}if(t)return o("p",{class:"loading",children:t});if(!r||!s)return o("p",{class:"loading",children:"Loading the engine"});const Q={...s.settings};for(const h in _)Q[h]={...Q[h],..._[h]};return o("div",{class:"app",onDragOver:h=>h.preventDefault(),onDrop:h=>{h.preventDefault(),Ce(ot(h.dataTransfer))},children:[o("a",{class:"skip",href:"#toolbar",children:"Skip to the toolbar"}),o("aside",{children:[o("h1",{class:"brand",children:[o($o,{})," plotline"]}),o("label",{"data-tour":"help",children:[o("input",{type:"checkbox",checked:x,onChange:h=>{P(h.currentTarget.checked),Io(h.currentTarget.checked)}})," Show help"]}),o("fieldset",{"data-tour":"style",children:[o("legend",{children:"Style"}),o("label",{children:["Style"," ",o("select",{value:s.style,onChange:h=>A("setStyle",[h.currentTarget.value]),children:r.map(h=>o("option",{value:h.name,children:h.label}))})]}),o("p",{class:"help",children:r.find(h=>h.name===s.style)?.description})]}),o("div",{class:"stages","data-tour":"settings",children:o(st,{stages:l,values:Q,errors:y,help:x,onChange:Be})})]}),o("main",{children:o(Lo,{engine:n,status:v,stages:l,canvas:s.canvas,hidden:g,pens:s.pens,hasImage:s.image,onOpen:Ce,error:y[""]||y.image&&`Could not open the image (${y.image}).${s.image?" The previous image is still open.":""}`,onSample:()=>A("sample",[],"image"),onRun:()=>A("run"),onStop:()=>A("cancel"),onSvg:()=>Ve("svg"),onSettings:()=>{k(h=>({...h,machine:""})),V(h=>h+1)},onTour:()=>xe(h=>h+1)})}),o("aside",{children:[s.suggestions.length>0&&o(Qn,{suggestions:s.suggestions,error:y.suggestions,onApply:h=>A("apply",[h],"suggestions")}),o(Kn,{canvas:s.canvas,error:y.page,onChange:h=>A("setCanvas",[h],"page")}),o(eo,{pens:s.pens,hidden:g,error:y.pens,mode:Q.split?.mode,onEdit:()=>{k(h=>({...h,pens:""})),F(h=>h+1)},onToggle:h=>b(C=>{const N=new Set(C);return N.delete(h)||N.add(h),N}),children:o(st,{part:"pens",stages:l,values:Q,errors:y,help:x,onChange:Be})}),o(Ho,{status:v,machine:s.machine!==null,onExport:Ve,onGcode:ln}),o(ro,{warnings:w,error:y.preset,onSave:on,onLoad:rn})]}),o(_o,{opens:I,machine:s.machine,error:y.machine,speeds:s.speeds,onPick:an,onEdit:h=>A("setMachine",[h],"machine"),onSpeeds:h=>A("setSpeeds",[h])}),o(to,{opens:H,pens:s.pens,bank:i,inks:d,error:y.pens,onChange:h=>A("setPens",[h],"pens")}),o(Po,{opens:Se})]})}document.documentElement.dataset.theme=zo();pn(o(Fo,{}),document.getElementById("app"));
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class F{static __wrap(e){const t=Object.create(F.prototype);return t.__wbg_ptr=e,T.register(t,t.__wbg_ptr,t),t}__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,T.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_drawinghandle_free(e,0)}canvas(){let e,t;try{const r=s.drawinghandle_canvas(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}layerCoords(e){const t=s.drawinghandle_layerCoords(this.__wbg_ptr,e);if(t[2])throw f(t[1]);return f(t[0])}layerCount(){return s.drawinghandle_layerCount(this.__wbg_ptr)>>>0}layerOffsets(e){const t=s.drawinghandle_layerOffsets(this.__wbg_ptr,e);if(t[2])throw f(t[1]);return f(t[0])}layerPen(e){let t,r;try{const o=s.drawinghandle_layerPen(this.__wbg_ptr,e);var i=o[0],_=o[1];if(o[3])throw i=0,_=0,f(o[2]);return t=i,r=_,l(i,_)}finally{s.__wbindgen_free(t,r,1)}}toneInk(){return s.drawinghandle_toneInk(this.__wbg_ptr)}toneRect(){const e=s.drawinghandle_toneRect(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}}Symbol.dispose&&(F.prototype[Symbol.dispose]=F.prototype.free);class A{static __wrap(e){const t=Object.create(A.prototype);return t.__wbg_ptr=e,z.register(t,t.__wbg_ptr,t),t}__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,z.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_framehandle_free(e,0)}coords(){return s.framehandle_coords(this.__wbg_ptr)}label(){let e,t;try{const r=s.framehandle_label(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}offsets(){return s.framehandle_offsets(this.__wbg_ptr)}pen(){return s.framehandle_pen(this.__wbg_ptr)>>>0}points(){return s.framehandle_points(this.__wbg_ptr)}progress(){return s.framehandle_progress(this.__wbg_ptr)}stage(){let e,t;try{const r=s.framehandle_stage(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(A.prototype[Symbol.dispose]=A.prototype.free);class R{static __wrap(e){const t=Object.create(R.prototype);return t.__wbg_ptr=e,U.register(t,t.__wbg_ptr,t),t}__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,U.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_run_free(e,0)}cancel(){s.run_cancel(this.__wbg_ptr)}error(){const e=s.run_error(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}nextFrame(){const e=s.run_nextFrame(this.__wbg_ptr);return e===0?void 0:A.__wrap(e)}state(){let e,t;try{const r=s.run_state(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(R.prototype[Symbol.dispose]=R.prototype.free);class P{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,B.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_session_free(e,0)}analysis(){const e=s.session_analysis(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}drawing(){const e=s.session_drawing(this.__wbg_ptr);return e===0?void 0:F.__wrap(e)}error(){const e=s.session_error(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}gcode(e){let t,r;try{const o=s.session_gcode(this.__wbg_ptr,e);var i=o[0],_=o[1];if(o[3])throw i=0,_=0,f(o[2]);return t=i,r=_,l(i,_)}finally{s.__wbindgen_free(t,r,1)}}json(){const e=s.session_json(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}loadPreset(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_loadPreset(this.__wbg_ptr,t,r);if(i[2])throw f(i[1]);return f(i[0])}machine(){const e=s.session_machine(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}constructor(){const e=s.session_new();return this.__wbg_ptr=e,B.register(this,this.__wbg_ptr,this),this}preset(){let e,t;try{const r=s.session_preset(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}previewPng(e,t,r){const i=s.session_previewPng(this.__wbg_ptr,e,t,r);if(i[3])throw f(i[2]);let _;return i[0]!==0&&(_=$(i[0],i[1]).slice(),s.__wbindgen_free(i[0],i[1]*1,1)),_}setCanvas(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_setCanvas(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}setImageBytes(e){const t=oe(e,s.__wbindgen_malloc),r=g,i=s.session_setImageBytes(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}setImage(e,t,r){const i=_e(r,s.__wbindgen_malloc),_=g,o=s.session_setImage(this.__wbg_ptr,e,t,i,_);if(o[1])throw f(o[0])}setMachine(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_setMachine(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}setPens(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_setPens(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}setSpeeds(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_setSpeeds(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}setStyle(e){const t=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=g,i=s.session_setStyle(this.__wbg_ptr,t,r);if(i[1])throw f(i[0])}settings(){let e,t;try{const r=s.session_settings(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}speeds(){let e,t;try{const r=s.session_speeds(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}start(){const e=s.session_start(this.__wbg_ptr);if(e[2])throw f(e[1]);return R.__wrap(e[0])}state(){let e,t;try{const r=s.session_state(this.__wbg_ptr);return e=r[0],t=r[1],l(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}stats(){const e=s.session_stats(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}svg(){const e=s.session_svg(this.__wbg_ptr);let t;return e[0]!==0&&(t=l(e[0],e[1]),s.__wbindgen_free(e[0],e[1]*1,1)),t}update(e,t){const r=w(e,s.__wbindgen_malloc,s.__wbindgen_realloc),i=g,_=w(t,s.__wbindgen_malloc,s.__wbindgen_realloc),o=g,a=s.session_update(this.__wbg_ptr,r,i,_,o);if(a[1])throw f(a[0])}}Symbol.dispose&&(P.prototype[Symbol.dispose]=P.prototype.free);function X(n){let e,t;try{const _=w(n,s.__wbindgen_malloc,s.__wbindgen_realloc),o=g,a=s.describe(_,o);var r=a[0],i=a[1];if(a[3])throw r=0,i=0,f(a[2]);return e=r,t=i,l(r,i)}finally{s.__wbindgen_free(e,t,1)}}function G(){let n,e;try{const t=s.listInks();return n=t[0],e=t[1],l(t[0],t[1])}finally{s.__wbindgen_free(n,e,1)}}function K(){let n,e;try{const t=s.listPens();return n=t[0],e=t[1],l(t[0],t[1])}finally{s.__wbindgen_free(n,e,1)}}function Q(){let n,e;try{const t=s.listStyles();return n=t[0],e=t[1],l(t[0],t[1])}finally{s.__wbindgen_free(n,e,1)}}function Z(n,e){const t=s.syntheticStillLife(n,e);var r=te(t[0],t[1]).slice();return s.__wbindgen_free(t[0],t[1]*4,4),r}function ee(){return{__proto__:null,"./plotline_wasm_bg.js":{__proto__:null,__wbg_Error_30c8987f7c2ed4e2:function(e,t){return Error(l(e,t))},__wbg___wbindgen_throw_41e9ee4f547fc59a:function(e,t){throw new Error(l(e,t))},__wbg_new_ee2291f50781bf1d:function(){return new Array},__wbg_new_from_slice_37a3c3a194266cb8:function(e,t){return new Uint32Array(ne(e,t))},__wbg_new_from_slice_9a868026ffa4208a:function(e,t){return new Uint8Array($(e,t))},__wbg_new_from_slice_f545fd22ddc142b8:function(e,t){return new Float64Array(re(e,t))},__wbg_push_2baf45db356cf468:function(e,t){return e.push(t)},__wbindgen_generic_0000000000000001:function(e,t){return l(e,t)},__wbindgen_init_externref_table:function(){const e=s.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}}}}const T=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_drawinghandle_free(n,1)),z=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_framehandle_free(n,1)),U=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_run_free(n,1)),B=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_session_free(n,1));function te(n,e){return n=n>>>0,q().subarray(n/4,n/4+e)}function re(n,e){return n=n>>>0,se().subarray(n/8,n/8+e)}function ne(n,e){return n=n>>>0,ie().subarray(n/4,n/4+e)}function $(n,e){return n=n>>>0,y().subarray(n/1,n/1+e)}let h=null;function q(){return(h===null||h.byteLength===0)&&(h=new Float32Array(s.memory.buffer)),h}let m=null;function se(){return(m===null||m.byteLength===0)&&(m=new Float64Array(s.memory.buffer)),m}function l(n,e){return le(n>>>0,e)}let S=null;function ie(){return(S===null||S.byteLength===0)&&(S=new Uint32Array(s.memory.buffer)),S}let v=null;function y(){return(v===null||v.byteLength===0)&&(v=new Uint8Array(s.memory.buffer)),v}function oe(n,e){const t=e(n.length*1,1)>>>0;return y().set(n,t/1),g=n.length,t}function _e(n,e){const t=e(n.length*4,4)>>>0;return q().set(n,t/4),g=n.length,t}function w(n,e,t){if(t===void 0){const a=O.encode(n),c=e(a.length,1)>>>0;return y().subarray(c,c+a.length).set(a),g=a.length,c}let r=n.length,i=e(r,1)>>>0;const _=y();let o=0;for(;o<r;o++){const a=n.charCodeAt(o);if(a>127)break;_[i+o]=a}if(o!==r){o!==0&&(n=n.slice(o)),i=t(i,r,r=o+n.length*3,1)>>>0;const a=y().subarray(i+o,i+r),c=O.encodeInto(n,a);o+=c.written,i=t(i,r,o,1)>>>0}return g=o,i}function f(n){const e=s.__wbindgen_externrefs.get(n);return s.__externref_table_dealloc(n),e}let M=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});M.decode();const ae=2146435072;let N=0;function le(n,e){return N+=e,N>=ae&&(M=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),M.decode(),N=e),M.decode(y().subarray(n,n+e))}const O=new TextEncoder;"encodeInto"in O||(O.encodeInto=function(n,e){const t=O.encode(n);return e.set(t),{read:n.length,written:t.length}});let g=0,s;function ce(n,e){return s=n.exports,h=null,m=null,S=null,v=null,s.__wbindgen_start(),s}async function fe(n,e){if(typeof Response=="function"&&n instanceof Response){if(!n.ok)throw new Error(`failed to fetch Wasm: ${n.status} ${n.statusText} fetching '${n.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(n,e)}catch(i){if(t(n.type)&&n.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",i);else throw i}const r=await n.arrayBuffer();return await WebAssembly.instantiate(r,e)}else{const r=await WebAssembly.instantiate(n,e);return r instanceof WebAssembly.Instance?{instance:r,module:n}:r}function t(r){switch(r){case"basic":case"cors":case"default":return!0}return!1}}async function ge(n){if(s!==void 0)return s;n!==void 0&&(Object.getPrototypeOf(n)===Object.prototype?{module_or_path:n}=n:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),n===void 0&&(n=new URL(""+new URL("plotline_wasm_bg-BxSp_00A.wasm",import.meta.url).href,import.meta.url));const e=ee();(typeof n=="string"||typeof Request=="function"&&n instanceof Request||typeof URL=="function"&&n instanceof URL)&&(n=fetch(n));const{instance:t,module:r}=await fe(await n,e);return ce(t)}const L=[];let J;function de(){if(!J){const e=new MessageChannel;e.port1.onmessage=()=>L.shift()?.(),e.port1.unref?.(),J=e.port2}const n=J;return new Promise(e=>{L.push(e),n.postMessage(null)})}const H=n=>n.flatMap(e=>e?[e.buffer]:[]);function we(n,e,t){const r=n.drawing();if(r)try{const i=[];for(let c=0;c<r.layerCount();c++)i.push({pen:JSON.parse(r.layerPen(c)),coords:r.layerCoords(c),offsets:r.layerOffsets(c)});const _=r.toneRect(),o=r.toneInk(),a={type:"result",id:e,canvas:JSON.parse(r.canvas()),layers:i,tone:_&&o?{...JSON.parse(_),ink:o}:null};t(a,H([...i.flatMap(c=>[c.coords,c.offsets]),o]))}finally{r.free()}}async function ue(n,e,t,r,i={}){const{frameMs:_=33,yieldMs:o=50,now:a=()=>performance.now(),yieldFn:c=de}=i;try{r({type:"run",id:t},[]);let u=-1/0,b=a();for(let d=e.nextFrame();d;d=e.nextFrame()){try{const j=a(),k=d.progress();if(k>=1||j-u>=_){const E={points:d.points(),coords:d.coords(),offsets:d.offsets()},V={type:"frame",id:t,stage:d.stage(),progress:k,label:d.label(),pen:d.pen(),...E};r(V,H(Object.values(E))),u=j}}finally{d.free()}a()-b>=o&&(await c(),b=a())}const p=e.state();p==="done"&&we(n,t,r);const C=n.stats();r({type:"end",id:t,state:p,error:e.error()??null,stats:C?JSON.parse(C):null},[])}finally{e.free()}}const pe=new Set(["setImageBytes","setImage","sample","setCanvas","setPens","setStyle","update","loadPreset","apply"]),D=n=>String(n?.message??n);class ye{constructor(e,t){this.session=e,this.post=t;const r=e;this.methods={listStyles:()=>JSON.parse(Q()),listPens:()=>JSON.parse(K()),listInks:()=>JSON.parse(G()),setMachine:i=>r.setMachine(JSON.stringify(i)),gcode:i=>r.gcode(i),describe:i=>JSON.parse(X(i)),setImageBytes:i=>{r.setImageBytes(i),this.image=!0},setImage:(i,_,o)=>{r.setImage(i,_,o),this.image=!0},sample:()=>this.methods.setImage(600,875,Z(600,875)),setCanvas:i=>r.setCanvas(JSON.stringify(i)),setPens:i=>r.setPens(JSON.stringify(i)),setStyle:i=>r.setStyle(i),update:(i,_)=>r.update(i,JSON.stringify(_)),apply:({style:i,settings:_,page:o})=>{i&&r.setStyle(i);for(const[a,c]of Object.entries(_??{}))r.update(a,JSON.stringify(c));o&&r.setCanvas(JSON.stringify({...JSON.parse(r.preset()).canvas,...o}))},setSpeeds:i=>r.setSpeeds(JSON.stringify(i)),run:()=>this.start(),cancel:()=>this.current?.cancel(),preset:()=>JSON.parse(r.preset()),loadPreset:i=>r.loadPreset(JSON.stringify(i)),svg:()=>r.svg(),json:()=>r.json()}}session;post;image=!1;counter=0;current=null;go=null;methods;handle({id:e,method:t,args:r}){try{if(!Object.hasOwn(this.methods,t))throw new Error(`unknown method: ${t}`);const _=this.methods[t](...r);pe.has(t)&&this.image&&this.start(),this.post({id:e,ok:!0,state:this.state(),value:_})}catch(_){this.post({id:e,ok:!1,error:D(_)})}const i=this.go;this.go=null,i?.()}start(){const e=this.session.start(),t=++this.counter;this.current=e,this.go=()=>{ue(this.session,e,t,this.post).catch(r=>this.post({type:"end",id:t,state:"error",error:D(r),stats:null},[])).finally(()=>{this.current===e&&(this.current=null)})}}state(){const e=this.session,{style:t,canvas:r,pens:i,machine:_,settings:o}=JSON.parse(e.preset()),a=e.stats(),c=[],u=e.drawing();if(u)try{for(let p=0;p<u.layerCount();p++)c.push(JSON.parse(u.layerPen(p)))}finally{u.free()}const b=e.analysis();return{image:this.image,style:t,canvas:r,pens:i,machine:_,settings:o,speeds:JSON.parse(e.speeds()),layers:c,suggestions:b?JSON.parse(b).suggestions:[],run:{id:this.counter,state:e.state(),error:e.error()??null},stats:a?JSON.parse(a):null}}}const x=[];let I,W;const Y=n=>self.postMessage({id:n.id,ok:!1,error:W});self.onmessage=n=>{I?I.handle(n.data):W!==void 0?Y(n.data):x.push(n.data)};ge().then(()=>{I=new ye(new P,(n,e)=>self.postMessage(n,{transfer:e??[]}));for(const n of x.splice(0))I.handle(n)}).catch(n=>{W=n instanceof Error?n.message:String(n);for(const e of x.splice(0))Y(e)});
|
plotline/web/index.html
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="color-scheme" content="light dark" />
|
|
7
|
+
<title>plotline</title>
|
|
8
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' color='%234f46e5'><style>@media (prefers-color-scheme: dark) { svg { color: %238b8ff9 } }</style><g fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round'><path d='M5 17C5 10 10 5 17 5'/><circle cx='5' cy='19' r='2'/><circle cx='19' cy='5' r='2'/></g></svg>" />
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-BcHuSLsL.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B3NFwj0a.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="app"></div>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
The following Lucide icons are derived from the Feather project:
|
|
20
|
+
|
|
21
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
22
|
+
|
|
23
|
+
The MIT License (MIT) (for the icons listed above)
|
|
24
|
+
|
|
25
|
+
Copyright (c) 2013-present Cole Bemis
|
|
26
|
+
|
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
28
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
29
|
+
in the Software without restriction, including without limitation the rights
|
|
30
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
32
|
+
furnished to do so, subject to the following conditions:
|
|
33
|
+
|
|
34
|
+
The above copyright notice and this permission notice shall be included in all
|
|
35
|
+
copies or substantial portions of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
40
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
43
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-present Jason Miller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: plotline
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Environment :: Web Environment
|
|
7
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
8
|
+
Classifier: Operating System :: MacOS
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Rust
|
|
13
|
+
Classifier: Topic :: Artistic Software
|
|
14
|
+
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
|
+
Requires-Dist: click>=8.2
|
|
17
|
+
Requires-Dist: mcp>=2.3,<3 ; extra == 'mcp'
|
|
18
|
+
Provides-Extra: mcp
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Summary: Turn images into drawings for pen plotters.
|
|
21
|
+
License-Expression: MIT
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
24
|
+
Project-URL: Homepage, https://jonathanrjoyner.github.io/plotline/
|
|
25
|
+
Project-URL: Issues, https://github.com/JonathanRJoyner/plotline/issues
|
|
26
|
+
Project-URL: Source, https://github.com/JonathanRJoyner/plotline
|
|
27
|
+
|
|
28
|
+
<p align="center">
|
|
29
|
+
<picture>
|
|
30
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/logo-dark.svg">
|
|
31
|
+
<img alt="plotline" src="docs/images/logo-light.svg" width="332">
|
|
32
|
+
</picture>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center"><strong>Turn images into drawings for pen plotters.</strong></p>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<a href="https://github.com/JonathanRJoyner/plotline/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/JonathanRJoyner/plotline/actions/workflows/ci.yml/badge.svg"></a>
|
|
39
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
40
|
+
<img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-blue">
|
|
41
|
+
<img alt="Status: early" src="https://img.shields.io/badge/status-early-orange">
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://jonathanrjoyner.github.io/plotline/"><strong>Open it in your browser</strong></a>
|
|
46
|
+
·
|
|
47
|
+
<a href="#command-line">Command line</a>
|
|
48
|
+
·
|
|
49
|
+
<a href="docs/BRIEF.md">Design brief</a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
plotline converts raster images into vector line art, such as stippling, single continuous lines, dashes and sketched strokes. It comes with a live preview that shows the algorithm at work, so you can see what a setting does before you commit ink to paper.
|
|
53
|
+
|
|
54
|
+
<table>
|
|
55
|
+
<tr>
|
|
56
|
+
<td align="center" valign="top" width="33%"><img src="docs/images/still-life.jpg" alt="A still life of three balls and a cube"><br><sub><b>The image</b></sub></td>
|
|
57
|
+
<td align="center" valign="top" width="33%"><img src="docs/images/grid.png" alt="The still life drawn with the grid style"><br><sub><b>grid</b>: dots on a regular grid</sub></td>
|
|
58
|
+
<td align="center" valign="top" width="33%"><img src="docs/images/hatch.png" alt="The still life drawn with the hatch style"><br><sub><b>hatch</b>: crossing parallel lines</sub></td>
|
|
59
|
+
</tr>
|
|
60
|
+
<tr>
|
|
61
|
+
<td align="center" valign="top"><img src="docs/images/waves.png" alt="The still life drawn with the waves style"><br><sub><b>waves</b>: rippling lines, one stroke each</sub></td>
|
|
62
|
+
<td align="center" valign="top"><img src="docs/images/spiral.png" alt="The still life drawn with the spiral style"><br><sub><b>spiral</b>: one rippling spiral</sub></td>
|
|
63
|
+
<td align="center" valign="top"><img src="docs/images/ink.png" alt="The plotline logo drawn with the ink style"><br><sub><b>ink</b>: line art, here the logo</sub></td>
|
|
64
|
+
</tr>
|
|
65
|
+
</table>
|
|
66
|
+
|
|
67
|
+
<p align="center"><sub>plotline drew every picture above with a 0.3 mm pen, from images it renders in code.</sub></p>
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- **Eighteen styles,** from dots on a grid to one continuous spiral. The table below describes each one.
|
|
72
|
+
- **Live preview.** The browser GUI shows the drawing being built. It runs the engine inside the page as WebAssembly, so your image is never uploaded.
|
|
73
|
+
- **Suggestions.** The engine measures a new image and suggests changes: line art, levels, invert, noise, page orientation and colour. You apply each one with a click. The tone stage's Levels can also be Auto, which sets black and white from the image.
|
|
74
|
+
- **Short pen travel.** Every run orders its paths to keep the pen's travel short, and the line styles lift the pen far less often than `grid`.
|
|
75
|
+
- **Plotter-ready output.** An SVG in millimetres holds one layer per pen that draws, each labelled with the name of its pen. A run can also write the paths as JSON, and a PNG preview that draws each mark at its real pen width. For a pen-fitted 3D printer, the GUI writes one G-code file per pen.
|
|
76
|
+
- **Several pens.** Pen use shares a drawing between your pens: light to dark in list order, or each pen by its colour, so cyan, magenta, yellow and black pens can draw a colour image. By default the first pen draws alone.
|
|
77
|
+
- **Real pens.** A bank of common fineliners, technical pens, gel pens, ballpoints and markers gives each pen its maker's line width, and a list of common inks gives it a colour.
|
|
78
|
+
- **Presets.** A small JSON file holds the style, page, pens, settings and machine of a run, so you can keep a look and share it.
|
|
79
|
+
- **One engine.** The engine is written in Rust and compiled twice: to a native extension for the Python package, and to WebAssembly for the browser. The Python package and the command line are thin layers over it.
|
|
80
|
+
|
|
81
|
+
| Style | What it draws |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `grid` | A dot on a regular grid wherever the image is dark enough. |
|
|
84
|
+
| `hatch` | Straight parallel lines over the dark areas, in three levels that cross over darker tones. |
|
|
85
|
+
| `waves` | Parallel lines that ripple more where the image is darker, one pen stroke per line. |
|
|
86
|
+
| `spiral` | One spiral from the centre out that ripples the same way. |
|
|
87
|
+
| `ink` | Line art, lettering and inked drawings. It outlines every ink shape and fills it with rings, and draws strokes thinner than the pen once down the middle, so the drawing keeps its own shapes and weights. |
|
|
88
|
+
| `stipple` | Points that spread evenly while staying dense in dark areas (weighted Voronoi relaxation), each drawn as a dot sized by the ink under it. |
|
|
89
|
+
| `line` | The same points joined into one unbroken line that does not cross itself. |
|
|
90
|
+
| `dashes` | A short stroke at each point, along the image's edges. |
|
|
91
|
+
| `tree` | The points joined by the shortest tree. |
|
|
92
|
+
| `mesh` | A triangle mesh over the points. |
|
|
93
|
+
| `cells` | The outlines of the points' Voronoi cells. |
|
|
94
|
+
| `sketch` | Straight strokes chained from dark to dark, like a quick pencil sketch. |
|
|
95
|
+
| `halftone` | Dots on a regular grid, each sized by the ink, like a printed halftone. |
|
|
96
|
+
| `dither` | Dots placed by error diffusion. |
|
|
97
|
+
| `circles` | Circles packed without overlap, larger and closer where the image is darker. |
|
|
98
|
+
| `contours` | Level lines of the image, like a topographic map. |
|
|
99
|
+
| `flow` | Evenly spaced lines that follow the image's edges, closer together where it is darker. |
|
|
100
|
+
| `hilbert` | A space-filling curve that folds tighter where the image is darker, broken where it leaves the image. |
|
|
101
|
+
|
|
102
|
+
`stipple`, `line`, `dashes`, `tree`, `mesh` and `cells` share one placement of points, so switching between them at their default settings reuses the points. A style switch resets the settings to the new style's defaults.
|
|
103
|
+
|
|
104
|
+
## In the browser
|
|
105
|
+
|
|
106
|
+
Open **[jonathanrjoyner.github.io/plotline](https://jonathanrjoyner.github.io/plotline/)**. There is nothing to install.
|
|
107
|
+
|
|
108
|
+
1. Drop or paste a PNG, JPEG or WebP image, or click Sample image.
|
|
109
|
+
2. Pick a style, change its settings and click Run to watch it draw.
|
|
110
|
+
3. Set up your pens in the Pens dialog, and choose how they share the drawing under Pen use. The page remembers your setup between visits.
|
|
111
|
+
4. Click Export SVG in the toolbar, ready for a pen plotter's own software.
|
|
112
|
+
|
|
113
|
+
For a new image the Suggestions panel, at the top of the right column, lists what would suit it, each with an Apply button. Nothing changes until you click.
|
|
114
|
+
|
|
115
|
+
For G-code, enter your machine in the Settings dialog: the drawable area, pen heights, feeds, and start and end G-code. The export panel then writes one G-code file per pen. The preset carries the machine, so save a preset to keep it. A short tour of the page opens on your first visit, and Tour in the toolbar opens it again.
|
|
116
|
+
|
|
117
|
+
## Command line
|
|
118
|
+
|
|
119
|
+
There is no release yet, so work from a clone of this repository. Building the Python extension needs a Rust toolchain: install one with [rustup](https://rustup.rs). With [uv](https://docs.astral.sh/uv/) installed as well, run this in the clone, where `uv sync` builds the extension and installs the package:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
uv sync
|
|
123
|
+
uv run plotline --help
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Then draw an image of your own:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
uv run plotline run grid your-photo.jpg --out drawing.svg --preview preview.png
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This writes `drawing.svg` and `preview.png`, and prints a one-line summary with the path count and an estimated plot time.
|
|
133
|
+
|
|
134
|
+
| To | Use |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| Set the page and margin, in mm | `--width 210 --height 297 --margin 15` |
|
|
137
|
+
| Choose the pen, by ink name or `#rrggbb` colour | `--pen 'Fine:0.3:blue'` |
|
|
138
|
+
| Share the drawing between pens given with `--pen` more than once; by default only the first draws | `--set split.mode=light_to_dark` or `by_colour` |
|
|
139
|
+
| Change a setting | `--set points.spacing_mm=3` |
|
|
140
|
+
| List a style's settings | `plotline describe grid` |
|
|
141
|
+
| Save the run's look as a preset | `--save-preset my-look.json` |
|
|
142
|
+
| Start a later run from a preset | `--preset my-look.json` |
|
|
143
|
+
| See an image's statistics and suggested changes as JSON | `plotline analyse your-photo.jpg` |
|
|
144
|
+
| Open the live preview locally | `plotline gui` |
|
|
145
|
+
|
|
146
|
+
`plotline gui` serves the browser GUI from your machine. Building it needs Node, wasm-pack and the `wasm32-unknown-unknown` Rust target: run `npm run wasm`, `npm ci` and `npm run build` in `web/`.
|
|
147
|
+
|
|
148
|
+
## With an AI assistant
|
|
149
|
+
|
|
150
|
+
`plotline mcp` serves plotline to an AI assistant over the Model Context Protocol, on stdio. From a clone, `uv sync` installs the MCP extra; once there is a release, `pip install "plotline[mcp]"` will. Give an MCP client the server as `{"command": "plotline", "args": ["mcp"]}`, where `command` is the `plotline` on the client's PATH or the full path to it (for a clone, the one in its `.venv`). The assistant can load an image, ask `analyse_image` what would suit it, run styles, look at previews, also zoomed in on a part of the page, and export the result.
|
|
151
|
+
|
|
152
|
+
## Roadmap
|
|
153
|
+
|
|
154
|
+
plotline is early software. The goals, architecture, decisions and everything still planned are in the [design brief](docs/BRIEF.md).
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
[MIT](LICENSE)
|
|
159
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
plotline/__init__.py,sha256=KLnm9RhY5oMi44d-VHs8Q3ILhhVTEJ8wTW5vmkLAEfQ,761
|
|
2
|
+
plotline/_core.pyd,sha256=j0D_O3D8eACkkgykxdpEjDdaQgaWPHtcPOAaCDri9Wc,3081216
|
|
3
|
+
plotline/_core.pyi,sha256=bBgJf24F8WA_soEcchyQK9491JBa2K7S5SbUyGFDeZI,2445
|
|
4
|
+
plotline/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
plotline/cli/main.py,sha256=ygG5XOYOfo8i5rxQ_qjCbVCzvGaM8xs2DqK-ZlUbBSw,11135
|
|
6
|
+
plotline/mcp_server.py,sha256=MdqhKLO2AySDAUSTx8Qb8f-g9Qh_bIP_xrlPIkBVuEQ,14601
|
|
7
|
+
plotline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
plotline/registry.py,sha256=ygcejWpOBhaWVMdj-aV6L-eH3WHFWl0frk3fVrsefHM,554
|
|
9
|
+
plotline/session.py,sha256=TrJDAYqdr9fk0stbEh43r9QYrz7qU_sR7nh6JyzjF6k,18117
|
|
10
|
+
plotline/types.py,sha256=I-mUtkCeoIAigWlDM3fAoDWuggMHGhmL1-moGDIFNOo,3535
|
|
11
|
+
plotline/web/THIRD-PARTY-LICENSES.txt,sha256=y-fmy7Tx2LBBf1CTorZGRhtuZGJSC0VWF1lN-KxXUMI,74548
|
|
12
|
+
plotline/web/assets/index-B3NFwj0a.css,sha256=36iAid7SPXhzK0aYblP_SKlJGi-ScodxdiS_6D7Efu0,11994
|
|
13
|
+
plotline/web/assets/index-BcHuSLsL.js,sha256=BaHTUb8S9-L3O8gAvPPJjQo4MPU1C6cqN8GKqL-YJZQ,57695
|
|
14
|
+
plotline/web/assets/plotline_wasm_bg-BxSp_00A.wasm,sha256=AeEqRbJYk1UIFQMFh-Zn1Z6UDR3PVbRUiNY8Zf7s7gM,1799429
|
|
15
|
+
plotline/web/assets/worker-DB9SFtUV.js,sha256=go6tHz7e2Phn7OXKZJcztnyTuhOFWGwj0MyUNJtWZaA,15032
|
|
16
|
+
plotline/web/index.html,sha256=Kl9CGvSWOah266tli0jLXHRRrwNu4Tl4e-DKpg1mlNo,835
|
|
17
|
+
plotline/web/lucide-preact-LICENSE.txt,sha256=tJUEe9k6mwaRNREHb1BNq6F9W76z4GUPO7U6QiAynFc,3208
|
|
18
|
+
plotline/web/preact-LICENSE.txt,sha256=H-aVhAnIwlenDFh6GLb39BKxebRWYweQ0wsuyajkt9Q,1087
|
|
19
|
+
plotline-0.1.0.dev0.dist-info/METADATA,sha256=WaHcpAYe_dUMFwgmAKCNDTyDXze39JLbAAS26IOHovU,10460
|
|
20
|
+
plotline-0.1.0.dev0.dist-info/WHEEL,sha256=I37eTNpE2wbBLxuGAyG7p6lFvJQiIjnLN05rBA0wN9g,96
|
|
21
|
+
plotline-0.1.0.dev0.dist-info/entry_points.txt,sha256=O4jXQ8F6MDRS-EhhNsMEzqpb5M47768KM11oFHfiUVw,50
|
|
22
|
+
plotline-0.1.0.dev0.dist-info/licenses/LICENSE,sha256=0aMe67H7sOzVnR1y6cNKmBJN0k_J-RFltZkLCmsnq5Y,1072
|
|
23
|
+
plotline-0.1.0.dev0.dist-info/sboms/plotline-py.cyclonedx.json,sha256=ICi3LUR1bq_Ic4Sf1a_qXpFv7Z3Ewabi0Ay84Hxlxzw,93349
|
|
24
|
+
plotline-0.1.0.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonathan Joyner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|