toyon 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +6 -6
  2. package/dist/bridge.js +1 -1
  3. package/dist/cli.js +107 -21
  4. package/dist/daemon.js +3829 -1257
  5. package/dist/shell/assets/{Editor-D5UJRxLO.js → Editor-CFH2QbSg.js} +189 -189
  6. package/dist/shell/assets/XTerm-C3aMkE_w.js +58 -0
  7. package/dist/shell/assets/{cssMode-DIt1Onbo.js → cssMode-dc6AdHsZ.js} +1 -1
  8. package/dist/shell/assets/{freemarker2-BlnMjxhl.js → freemarker2-CxtmNsLB.js} +1 -1
  9. package/dist/shell/assets/{grammar-xCgG7hS1.js → grammar-B-X0FPRk.js} +1 -1
  10. package/dist/shell/assets/{handlebars-CgcpCzmy.js → handlebars-DVzmQ0KM.js} +1 -1
  11. package/dist/shell/assets/{html-yqzA9zjA.js → html-CqqXL9ke.js} +1 -1
  12. package/dist/shell/assets/{htmlMode-AyUHvgCk.js → htmlMode-Bg-Em0rL.js} +1 -1
  13. package/dist/shell/assets/index-BskWflhx.css +1 -0
  14. package/dist/shell/assets/index-C8QP4sgP.js +255 -0
  15. package/dist/shell/assets/{javascript-CYZ-NuGG.js → javascript-W-C4G7BB.js} +1 -1
  16. package/dist/shell/assets/{jsonMode-BpybSxrR.js → jsonMode-De4ajMpV.js} +1 -1
  17. package/dist/shell/assets/{liquid-BHHjN7T9.js → liquid-BSUg_7LQ.js} +1 -1
  18. package/dist/shell/assets/{lspLanguageFeatures-CHyCsA1P.js → lspLanguageFeatures-BImXYajo.js} +1 -1
  19. package/dist/shell/assets/{mdx-CCgYQauJ.js → mdx-B9A7RorP.js} +1 -1
  20. package/dist/shell/assets/{python-C1JulLv8.js → python-BD9sq3DF.js} +1 -1
  21. package/dist/shell/assets/{razor-Dj4sWgNm.js → razor-DGQVRh5-.js} +1 -1
  22. package/dist/shell/assets/{tsMode-D1gKgsY6.js → tsMode-DudQlgN_.js} +1 -1
  23. package/dist/shell/assets/{xml-BHUNgL0o.js → xml-BI6-GyBn.js} +1 -1
  24. package/dist/shell/assets/{yaml-CetvwW8L.js → yaml-BEUvyYtP.js} +1 -1
  25. package/dist/shell/index.html +2 -2
  26. package/package.json +1 -1
  27. package/dist/shell/assets/XTerm-l6IDHxt8.js +0 -40
  28. package/dist/shell/assets/index-BGLn9_S8.js +0 -241
  29. package/dist/shell/assets/index-Cm07OHxH.css +0 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@ Each copy runs on your own machine, and the next one is pre-warmed. Works with C
6
6
 
7
7
  [How it works](#how-it-works) · [Install](#install) · [Under the hood](#under-the-hood) · [Trust](#trust) · [Uninstall](#uninstall)
8
8
 
9
- ![Toyon with four chats on a plant shop, each changing its own copy: a dark theme, one wide column, price badges and bigger photos. The rail walks through the copies and back to the dark one, which is ready to land](https://raw.githubusercontent.com/toyon-dev/toyon/main/docs/images/toyon.gif)
9
+ ![Toyon with four chats on a plant shop, each changing its own copy. The desk opens a code, a phone signs in with it and starts a copy of its own, both screens follow that chat, and both end on the last copy with its Wildflowers filter pressed, ready to land](https://raw.githubusercontent.com/toyon-dev/toyon/main/docs/images/toyon.gif)
10
10
 
11
11
  ## Why
12
12
 
@@ -24,7 +24,7 @@ In Toyon you point at a button in the page and ask for it to be bigger, watch it
24
24
 
25
25
  Each copy is a branch and a git worktree with your dependencies and `.env` files; one spare per project is kept warm.
26
26
 
27
- Point at anything on the page to talk about it or open the line that draws it, and the design pane maps it to your tokens and components.
27
+ Point at anything on the page to talk about it or open the line that draws it, and the design pane maps it to your tokens and components. What the agent is told about the page, and the keys, are in [docs/preview.md](https://github.com/toyon-dev/toyon/blob/main/docs/preview.md).
28
28
 
29
29
  One Toyon holds every project you open, and the list of chats shows which one needs you.
30
30
 
@@ -65,7 +65,7 @@ Each agent signs in from the chat. Claude Code and Codex install on first start;
65
65
  |---|---|---|
66
66
  | Claude Code or Codex in a terminal | The same agent under your login, with your permission rules, hooks, slash commands and MCP servers | Each chat gets its own copy of the project with the app running, and nothing lands until you press |
67
67
  | Cursor, Zed or VS Code | Your editor and your checkout; any file opens back in your editor | The running app is the centre of every chat, wired to its source |
68
- | Lovable, v0 or Bolt | The loop: a chat and the page it changes | Your own project on your machine, with your database, Docker and private packages; each chat is a branch in your repository |
68
+ | Lovable, v0 or Bolt | The loop: a chat and the page it changes, and pointing at an element to talk about it | Your own project on your machine, with your database, Docker and private packages; each chat is a branch in your repository |
69
69
  | Conductor or Superset | Many agents at once, each on its own copy | Built around the app instead of the agents; MIT, no account, no telemetry, lands on your machine without GitHub |
70
70
 
71
71
  ## Under the hood
@@ -79,16 +79,16 @@ A project with a page and an API behind it:
79
79
  "setup": ["npm install"],
80
80
  "run": {
81
81
  "web": "vite --port $PORT --strictPort",
82
- "api": "node --watch server.js"
82
+ "api": { "cmd": "node --watch server.js", "from": "trunk" }
83
83
  },
84
84
  "check": "npm test",
85
85
  "land": { "route": "pr" }
86
86
  }
87
87
  ```
88
88
 
89
- Each command gets its own `$PORT`, and the addresses of the others as `<NAME>_URL` (`API_URL` here), so the page can proxy to its API. The preview shows `web`.
89
+ Each command gets its own `$PORT`, and the addresses of the others as `<NAME>_URL` (`API_URL` here), so the page can proxy to its API. The preview shows `web`. A command marked `from: "trunk"` runs once, in the main checkout, and every copy's page reaches that one until the copy's own changes touch it; then the copy starts its own. A copy that only changes the page costs one Vite.
90
90
 
91
- Copies start when you open them, and stop their servers when nobody has looked at them for a while; the chat and the agent keep going. `toyon stop` stops the daemon and everything it runs. `toyon doctor` says what is running and why a page cannot connect. Every setting, the port rules and the sleep delay are in [docs/settings.md](https://github.com/toyon-dev/toyon/blob/main/docs/settings.md).
91
+ Copies start when you open them, and stop their servers when nobody has looked at them for a while; the chat and the agent keep going. Close the window and the daemon stops itself once nothing has needed it for half an hour; `toyon stop` stops it now, and everything it runs. `toyon doctor` says what is running and why a page cannot connect. Every setting, the port rules and the sleep delay are in [docs/settings.md](https://github.com/toyon-dev/toyon/blob/main/docs/settings.md).
92
92
 
93
93
  ## What it does not do
94
94
 
package/dist/bridge.js CHANGED
@@ -1 +1 @@
1
- var b=[{id:"quick-open",key:"p"},{id:"commands",key:"p",shift:!0,bareAlias:"F1"},{id:"search",key:"f",shift:!0},{id:"chats",key:"g"},{id:"changes",key:"b",ctrlAlias:{key:"g",shift:!0}},{id:"files",key:"e",shift:!0},{id:"panel-tab-prev",key:"ArrowLeft",alt:!0,textKeeps:!0},{id:"panel-tab-next",key:"ArrowRight",alt:!0,textKeeps:!0},{id:"composer",key:"l"},{id:"rail",key:"k",shift:!0},{id:"keys",key:","},{id:"terminal",key:"j",ctrlAlias:{key:"`"}},{id:"design",key:"d"},{id:"term-tab",key:"`",ctrl:!0,shift:!0},{id:"pick",key:"e"},{id:"inspect",key:"i"},{id:"zen",key:"."},{id:"new",key:"k",aliases:["n"]},{id:"worktree",key:"1-9"},{id:"wt-prev",key:"ArrowUp",alt:!0,ctrlAlias:{key:"Tab",shift:!0},cmdShiftAlias:["[","{"]},{id:"wt-next",key:"ArrowDown",alt:!0,ctrlAlias:{key:"Tab"},cmdShiftAlias:["]","}"]},{id:"wt-unseen-prev",key:"ArrowUp",alt:!0,shift:!0},{id:"wt-unseen-next",key:"ArrowDown",alt:!0,shift:!0},{id:"mark-unread",key:"u",shift:!0},{id:"project",key:"o",ctrlAlias:{key:"r",hostOnly:!0},cmdShiftAlias:["o"]},{id:"refs",key:"g",shift:!0},{id:"routes",key:"u"},{id:"reload",key:"r"},{id:"back",key:"ArrowLeft",textKeeps:!0},{id:"forward",key:"ArrowRight",textKeeps:!0},{id:"close",key:"w"}],I=new Set(["zen","close","back","forward"]);function W(e,{guest:t=!1}={}){if(e.altKey){if(e.metaKey||e.ctrlKey)return null;let r=b.find((i)=>i.alt&&i.key===e.key&&!!i.shift===e.shiftKey);return r&&r.id!=="worktree"?{id:r.id}:null}if(!e.metaKey&&!e.ctrlKey){if(e.shiftKey||t)return null;let r=b.find((i)=>i.bareAlias===e.key);return r&&r.id!=="worktree"?{id:r.id}:null}if(!!e.ctrlKey===e.metaKey)return null;let n=e.key.length===1?e.key.toLowerCase():e.key;if(e.metaKey&&!e.shiftKey&&n>="1"&&n<="9")return{id:"worktree",digit:Number(n)};for(let r of b){if(r.id==="worktree")continue;let i=r.ctrlAlias;if(i&&e.ctrlKey&&i.key===n&&!!i.shift===e.shiftKey&&!(t&&i.hostOnly))return{id:r.id};if(e.metaKey&&e.shiftKey&&r.cmdShiftAlias?.includes(n))return{id:r.id};if(r.alt)continue;if(e.ctrlKey&&!r.ctrl||!!r.shift!==e.shiftKey)continue;if(r.key===n||r.aliases?.includes(n))return{id:r.id}}return null}function j(e){let t=b.find((n)=>n.id===e);if(!t)throw Error(`unknown chord ${e}`);return t}function X(e){let t=e;if(!t?.tagName)return!1;if(t.isContentEditable||t.tagName==="TEXTAREA")return!0;return t.tagName==="INPUT"&&!["button","checkbox","radio","range","submit","reset"].includes(t.type??"")}var ee=Symbol.for("react.forward_ref"),te=Symbol.for("react.memo");function q(e){if(e._debugSource?.fileName)return{file:e._debugSource.fileName,line:e._debugSource.lineNumber};let t=e._debugStack?.stack;if(t){let n=t.match(/https?:\/\/[^/]+(\/src\/[^)\s?]+)(?:\?[^:)\s]*)?:(\d+):\d+/);if(n?.[1])return{file:n[1],line:Number(n[2])||void 0}}return null}function K(e){for(let t=e;t;t=t.return){let n=q(t);if(n)return n}return null}function T(e){if(typeof e==="function")return e.displayName||e.name||null;let t=e;if(t?.$$typeof===ee)return t.displayName||T(t.render);if(t?.$$typeof===te)return t.displayName||T(t.type);return null}var ne=(e)=>typeof e.type==="string"||e.tag===6,re=(e,t)=>!!e&&(e===t||!!e.stateNode&&e.stateNode===t.stateNode);function ie(e){let t=null,n=(r)=>{for(let i=r.child??null;i;i=i.sibling??null)if(ne(i)){if(t)return!1;t=i}else if(!n(i))return!1;return!0};return n(e)?t:null}function oe(e){for(let t=e;t;t=t.return){let n=q(t);if(n)return t._debugOwner===null?null:n}return null}function L(e){let t=K(e),n=null,r=null,i=!1;for(let o=e;o;o=o.return){let s=T(o.type);if(!s)continue;let l=!!e&&re(ie(o),e),a=oe(o);if(n&&!(l&&a))break;if(n=s,r=a,i=l,!l)break}if(r&&t&&r.file===t.file&&r.line===t.line)r=null;return{src:t,comp:n,call:r,wraps:i}}function _({src:e,call:t,wraps:n},r){let i=n?t:e,o=n?e:t;return(r?o??i:i??o)??null}if(window.__toyonBridge)throw Error("Toyon bridge already installed");window.__toyonBridge=!0;var w=window.__toyonShellOrigins??[],M=null,c=(e)=>{try{let t=M?[M]:w.length?w:["*"];for(let n of t)window.parent.postMessage({__toyon:!0,...e},n)}catch{}};c({type:"loaded",url:location.href,title:document.title});var E="#ff4929",G="#1d2021";window.addEventListener("vite:afterUpdate",()=>c({type:"hmr"}));window.addEventListener("error",(e)=>{c({type:"page-error",message:String(e.message),source:e.filename,line:e.lineno})});window.addEventListener("unhandledrejection",(e)=>{c({type:"page-error",message:`unhandled rejection: ${String(e.reason)}`})});var A=()=>c({type:"navigated",url:location.href,title:document.title}),le=history.pushState.bind(history);history.pushState=(...e)=>{le(...e),A()};var se=history.replaceState.bind(history);history.replaceState=(...e)=>{se(...e),A()};window.addEventListener("popstate",A);window.addEventListener("hashchange",A);var N=document.title;if(document.head)new MutationObserver(()=>{if(document.title===N)return;N=document.title,c({type:"title",title:N})}).observe(document.head,{childList:!0,subtree:!0,characterData:!0});var U=!1,P=null;window.addEventListener("keyup",(e)=>{if(window.__toyonShell||e.key!==P)return;P=null,c({type:"key",key:e.key,meta:!1,up:!0})},!0);window.addEventListener("keydown",(e)=>{if(window.__toyonShell)return;let t=W(e,{guest:!0});if(t&&j(t.id).textKeeps&&X(e.target))return;if(U){if(!t||!I.has(t.id))return}else if(e.key==="Escape"){c({type:"key",key:"Escape",meta:!1});return}else if(!t)return;e.preventDefault(),e.stopPropagation(),c({type:"key",key:e.key,meta:e.metaKey,ctrl:e.ctrlKey,shift:e.shiftKey,alt:e.altKey}),P=e.altKey?"Alt":e.ctrlKey?"Control":e.metaKey?"Meta":null},!0);var R=()=>c({type:"focus",gesture:navigator.userActivation?.isActive??!0});window.addEventListener("focusin",R,!0);window.addEventListener("focus",R);if(document.hasFocus()&&document.activeElement&&document.activeElement!==document.body)R();var Y=(e)=>!e.defaultPrevented&&!!e.dataTransfer?.types.includes("Files");window.addEventListener("dragover",(e)=>{if(!Y(e))return;if(e.preventDefault(),e.dataTransfer)e.dataTransfer.dropEffect="none";c({type:"drag-files"})});window.addEventListener("drop",(e)=>{if(!Y(e))return;e.preventDefault(),c({type:"drop-files"})});function D(e){for(let t of Object.keys(e))if(t.startsWith("__reactFiber$"))return e[t]??null;return null}var p=null;function ae(){if(p?.isConnected)return p;return p=document.createElement("div"),p.style.cssText="position:fixed;inset:0;pointer-events:none;z-index:2147483647;",document.documentElement.appendChild(p),p}function m(){if(p)p.innerHTML=""}var F=20;function x(e,t,n=!0){let r=document.createElement("div");if(r.style.cssText=`position:fixed;left:${e.left}px;top:${e.top}px;width:${e.width}px;height:${e.height}px;outline:2px solid ${E};outline-offset:-1px;${n?`background:${E}14;`:""}border-radius:2px;`,t){let i=e.top>=F,o=e.bottom+F<=window.innerHeight;if(t.style.top=i?`${-F}px`:o?`${e.height+4}px`:"2px",!i&&!o)t.style.left="2px";r.appendChild(t)}if(ae().appendChild(r),t){let i=t.getBoundingClientRect(),o=i.right-(window.innerWidth-2);if(o>0){let s=Number.parseFloat(t.style.left||"0");t.style.left=`${s-Math.min(o,Math.max(0,i.left-2))}px`}}}function g(e,t){let n=document.createElement("div");if(n.textContent=e,n.style.cssText=`position:absolute;left:0;background:${E};color:${G};font:11px -apple-system,sans-serif;padding:1px 6px;border-radius:3px;white-space:nowrap;`,t){let r=document.createElement("span");r.textContent=` ${t}`,r.style.opacity="0.55",n.appendChild(r)}return n}var v=!1,d=null,h=!1,f=!1,y=!1,O=(e)=>`${J(e.file)}${e.line?`:${e.line}`:""}`;function C(e){d=e,m();let t=L(D(e)),{src:n,comp:r,call:i}=t,o=_(t,f),s=o===n?i:n,l=h!==y,a=(...k)=>k.filter(Boolean).join(" ")||void 0,u=l?o?g(`open ${O(o)}`,a(s&&`⇧ ${O(s)}`,y&&"⌥ chat")):g(`find ${z(e)}`,a(y&&"⌥ chat")):g(r?`<${r} />${o?` · ${O(o)}`:""}`:z(e),a(!y&&"⌥ code"));x(e.getBoundingClientRect(),u,l)}function z(e){let t=e.tagName.toLowerCase(),n=e.classList[0];return(e.id?`${t}#${e.id}`:n?`${t}.${n}`:t).slice(0,48)}var ce=new Set(["name","placeholder","aria-label","title","alt","for","href","type","role","src"]);function ue(e){let t="";for(let r of Array.from(e.childNodes))if(r.nodeType===Node.TEXT_NODE)t+=r.textContent??"";let n=[];for(let r of Array.from(e.attributes)){if(n.length===12)break;if(r.value&&(ce.has(r.name)||r.name.startsWith("data-")))n.push([r.name.slice(0,40),r.value.slice(0,200)])}return{tag:e.tagName.toLowerCase().slice(0,40),id:e.id.slice(0,200),classes:Array.from(e.classList).slice(0,16).map((r)=>r.slice(0,120)),text:t.replace(/\s+/g," ").trim().slice(0,200),attrs:n}}function Z(e){let t=document.elementFromPoint(e.clientX,e.clientY);if(!t)return;let n=t!==d||e.altKey!==h||e.shiftKey!==f;if(h=e.altKey,f=e.shiftKey,n)C(t)}function V(e){e.preventDefault(),e.stopPropagation();let t=d??document.elementFromPoint(e.clientX,e.clientY),n=L(t?D(t):null),{src:r,comp:i,call:o}=n;f=e.shiftKey;let s=_(n,f),l=e.altKey!==y;if(B(),!t)return;c({type:"picked",verb:l?"code":"chat",site:s&&s===o?"call":"source",component:i,file:r?.file??null,line:r?.line??null,callFile:o?.file??null,callLine:o?.line??null,selector:fe(t),tag:t.tagName.toLowerCase(),classes:t.className?.toString?.().slice(0,200)??"",text:t.textContent?.trim().slice(0,120)??"",html:t.outerHTML.slice(0,600),route:location.pathname+location.search,element:ue(t)})}function S(e){if(e.key==="Escape"&&e.type==="keydown"){e.preventDefault(),e.stopPropagation(),B(),c({type:"pick-cancel"});return}if((e.key==="Alt"||e.key==="Shift")&&(h!==e.altKey||f!==e.shiftKey)){if(h=e.altKey,f=e.shiftKey,d)C(d)}}function de(e){if(y=e,v){if(d)C(d);return}v=!0,d=null,h=!1,f=!1,document.addEventListener("mousemove",Z,!0),document.addEventListener("click",V,!0),document.addEventListener("keydown",S,!0),document.addEventListener("keyup",S,!0),document.documentElement.style.cursor="crosshair"}function B(){v=!1,y=!1,d=null,h=!1,f=!1,m(),document.removeEventListener("mousemove",Z,!0),document.removeEventListener("click",V,!0),document.removeEventListener("keydown",S,!0),document.removeEventListener("keyup",S,!0),document.documentElement.style.cursor=""}function fe(e){let t=[],n=e;for(let r=0;n&&n!==document.documentElement&&r<6;r++){if(n.id){t.unshift(`#${CSS.escape(n.id)}`);break}let i=n.parentElement,o=i?Array.from(i.children).indexOf(n)+1:1;t.unshift(`${n.tagName.toLowerCase()}:nth-child(${o})`),n=i}return t.join(" > ")}function J(e){let t=e.lastIndexOf("/src/");return t>=0?e.slice(t+1):e.split("/").slice(-2).join("/")}function he(e,t){return e.endsWith(t)||t.endsWith(J(e))}function pe(e,t){let n=[],r=0;for(let s of Array.from(document.querySelectorAll("*"))){let l=K(D(s));if(l)r++;if(!l||!he(l.file,e))continue;n.push({el:s,line:l.line??-1})}if(!t||t.length===0)return{matched:n,fromFile:n,withSource:r};if(t.length===1&&t[0][0]===t[0][1]){let s=t[0][0],l=n.filter((u)=>u.line===s);if(l.length>0)return{matched:l,fromFile:n,withSource:r};let a=n.filter((u)=>u.line>0&&u.line<s);return a.sort((u,k)=>k.line-u.line),{matched:a.slice(0,1),fromFile:n,withSource:r}}return{matched:n.filter((s)=>t.some(([l,a])=>s.line>=l-8&&s.line<=a+1)),fromFile:n,withSource:r}}function ye(e,t){m();let{matched:n,fromFile:r,withSource:i}=pe(e,t),o=0;for(let{el:s}of n){if(o>=40)break;let l=s.getBoundingClientRect();if(l.width>0&&l.height>0)x(l),o++}if(o===0)c({type:"highlight-miss",path:e,fileMatched:r.length,withSource:i,ranges:t})}window.addEventListener("message",(e)=>{if(e.source!==window.parent)return;if(w.length&&!w.includes(e.origin))return;let t=e.data;if(!t?.__toyon)return;M=e.origin;let n=t;switch(n.type){case"reload":location.reload();break;case"navigate":if(/^[/?#]/.test(n.path))location.assign(n.path);break;case"back":case"forward":{let r=window.navigation;if((n.type==="back"?r?.canGoBack:r?.canGoForward)===!1)break;if(n.type==="back")history.back();else history.forward();break}case"links":{let r=new Set,i=[];for(let o of Array.from(document.querySelectorAll("a[href]"))){if(o.origin!==location.origin||o.target==="_blank"||o.hasAttribute("download"))continue;let s=o.pathname+(o.hash.startsWith("#/")?o.hash:"");if(s.startsWith("/__toyon")||r.has(s))continue;r.add(s);let l=(o.textContent||o.getAttribute("aria-label")||"").replace(/\s+/g," ").trim();if(i.push({path:s,text:l.slice(0,80)}),i.length>=200)break}c({type:"links",links:i});break}case"pick-start":de(n.verb==="code");break;case"pick-mods":if(v&&(h!==n.alt||f!==n.shift)){if(h=n.alt,f=n.shift,d)C(d)}break;case"pick-cancel":B();break;case"highlight-file":ye(n.path,n.ranges??null);break;case"highlight-computed":{m();let r=/^(color|font|line-height|letter-|text-|word-)/,i=(l)=>{if(/^(INPUT|TEXTAREA|SELECT)$/.test(l.tagName))return!0;for(let a=l.firstChild;a;a=a.nextSibling)if(a.nodeType===3&&a.textContent&&a.textContent.trim())return!0;return!1},o=document.querySelectorAll("*"),s=!0;for(let l=0;l<o.length;l++){let a=getComputedStyle(o[l]),u=([H,Q])=>a.getPropertyValue(H)===Q&&(!r.test(H)||i(o[l]));if(n.match==="any"?n.props.some(u):n.props.every(u))x(o[l].getBoundingClientRect(),s&&n.label?g(n.label):void 0),s=!1}break}case"highlight-selector":{m();try{let r=document.querySelectorAll(n.selector);for(let i=0;i<r.length;i++)x(r[i].getBoundingClientRect(),i===0&&n.label?g(n.label):void 0)}catch{}break}case"highlight-clear":m();break;case"zen":U=n.on;break;case"theme":if(/^#[0-9a-f]{3,8}$/i.test(n.accent))E=n.accent;if(/^#[0-9a-f]{3,8}$/i.test(n.accentFg))G=n.accentFg;break}});
1
+ var b=[{id:"quick-open",key:"p"},{id:"commands",key:"p",shift:!0,bareAlias:"F1"},{id:"search",key:"f",shift:!0},{id:"chats",key:"g"},{id:"changes",key:"b",ctrlAlias:{key:"g",shift:!0}},{id:"files",key:"e",shift:!0},{id:"panel-tab-prev",key:"ArrowLeft",alt:!0,textKeeps:!0},{id:"panel-tab-next",key:"ArrowRight",alt:!0,textKeeps:!0},{id:"composer",key:"l"},{id:"rail",key:"k",shift:!0},{id:"keys",key:","},{id:"terminal",key:"j",ctrlAlias:{key:"`"}},{id:"design",key:"d"},{id:"term-tab",key:"`",ctrl:!0,shift:!0},{id:"pick",key:"e"},{id:"inspect",key:"i"},{id:"zen",key:"."},{id:"new",key:"k",aliases:["n"]},{id:"worktree",key:"1-9"},{id:"wt-prev",key:"ArrowUp",alt:!0,ctrlAlias:{key:"Tab",shift:!0},cmdShiftAlias:["[","{"]},{id:"wt-next",key:"ArrowDown",alt:!0,ctrlAlias:{key:"Tab"},cmdShiftAlias:["]","}"]},{id:"wt-unseen-prev",key:"ArrowUp",alt:!0,shift:!0},{id:"wt-unseen-next",key:"ArrowDown",alt:!0,shift:!0},{id:"wt-archive",key:"Backspace",alt:!0,textKeeps:!0},{id:"mark-unread",key:"u",alt:!0,textKeeps:!0},{id:"project",key:"o",ctrlAlias:{key:"r",hostOnly:!0},cmdShiftAlias:["o"]},{id:"refs",key:"g",shift:!0},{id:"routes",key:"u"},{id:"reload",key:"r"},{id:"back",key:"ArrowLeft",textKeeps:!0},{id:"forward",key:"ArrowRight",textKeeps:!0},{id:"close",key:"w"}],I=new Set(["zen","close","back","forward"]);function W(e,{guest:t=!1}={}){if(e.altKey){if(e.metaKey||e.ctrlKey)return null;let r=b.find((i)=>i.alt&&!!i.shift===e.shiftKey&&(i.key===e.key||i.key.length===1&&e.code===`Key${i.key.toUpperCase()}`));return r&&r.id!=="worktree"?{id:r.id}:null}if(!e.metaKey&&!e.ctrlKey){if(e.shiftKey||t)return null;let r=b.find((i)=>i.bareAlias===e.key);return r&&r.id!=="worktree"?{id:r.id}:null}if(!!e.ctrlKey===e.metaKey)return null;let n=e.key.length===1?e.key.toLowerCase():e.key;if(e.metaKey&&!e.shiftKey&&n>="1"&&n<="9")return{id:"worktree",digit:Number(n)};for(let r of b){if(r.id==="worktree")continue;let i=r.ctrlAlias;if(i&&e.ctrlKey&&i.key===n&&!!i.shift===e.shiftKey&&!(t&&i.hostOnly))return{id:r.id};if(e.metaKey&&e.shiftKey&&r.cmdShiftAlias?.includes(n))return{id:r.id};if(r.alt)continue;if(e.ctrlKey&&!r.ctrl||!!r.shift!==e.shiftKey)continue;if(r.key===n||r.aliases?.includes(n))return{id:r.id}}return null}function j(e){if(e.altKey||e.key.toLowerCase()!=="s")return!1;return!!e.ctrlKey!==e.metaKey}function X(e){let t=b.find((n)=>n.id===e);if(!t)throw Error(`unknown chord ${e}`);return t}function q(e){let t=e;if(!t?.tagName)return!1;if(t.isContentEditable||t.tagName==="TEXTAREA")return!0;return t.tagName==="INPUT"&&!["button","checkbox","radio","range","submit","reset"].includes(t.type??"")}var te=Symbol.for("react.forward_ref"),ne=Symbol.for("react.memo");function z(e){if(e._debugSource?.fileName)return{file:e._debugSource.fileName,line:e._debugSource.lineNumber};let t=e._debugStack?.stack;if(t){let n=t.match(/https?:\/\/[^/]+(\/src\/[^)\s?]+)(?:\?[^:)\s]*)?:(\d+):\d+/);if(n?.[1])return{file:n[1],line:Number(n[2])||void 0}}return null}function T(e){for(let t=e;t;t=t.return){let n=z(t);if(n)return n}return null}function C(e){if(typeof e==="function")return e.displayName||e.name||null;let t=e;if(t?.$$typeof===te)return t.displayName||C(t.render);if(t?.$$typeof===ne)return t.displayName||C(t.type);return null}var re=(e)=>typeof e.type==="string"||e.tag===6,ie=(e,t)=>!!e&&(e===t||!!e.stateNode&&e.stateNode===t.stateNode);function oe(e){let t=null,n=(r)=>{for(let i=r.child??null;i;i=i.sibling??null)if(re(i)){if(t)return!1;t=i}else if(!n(i))return!1;return!0};return n(e)?t:null}function le(e){for(let t=e;t;t=t.return){let n=z(t);if(n)return t._debugOwner===null?null:n}return null}function L(e){let t=T(e),n=null,r=null,i=!1;for(let o=e;o;o=o.return){let s=C(o.type);if(!s)continue;let l=!!e&&ie(oe(o),e),a=le(o);if(n&&!(l&&a))break;if(n=s,r=a,i=l,!l)break}if(r&&t&&r.file===t.file&&r.line===t.line)r=null;return{src:t,comp:n,call:r,wraps:i}}function _({src:e,call:t,wraps:n},r){let i=n?t:e,o=n?e:t;return(r?o??i:i??o)??null}if(window.__toyonBridge)throw Error("Toyon bridge already installed");window.__toyonBridge=!0;var w=window.__toyonShellOrigins??[],M=null,c=(e)=>{try{let t=M?[M]:w.length?w:["*"];for(let n of t)window.parent.postMessage({__toyon:!0,...e},n)}catch{}};c({type:"loaded",url:location.href,title:document.title});var x="#fa891e",G="#1d2021";window.addEventListener("vite:afterUpdate",()=>c({type:"hmr"}));window.addEventListener("error",(e)=>{c({type:"page-error",message:String(e.message),source:e.filename,line:e.lineno})});window.addEventListener("unhandledrejection",(e)=>{c({type:"page-error",message:`unhandled rejection: ${String(e.reason)}`})});var K=()=>c({type:"navigated",url:location.href,title:document.title}),se=history.pushState.bind(history);history.pushState=(...e)=>{se(...e),K()};var ae=history.replaceState.bind(history);history.replaceState=(...e)=>{ae(...e),K()};window.addEventListener("popstate",K);window.addEventListener("hashchange",K);var N=document.title;if(document.head)new MutationObserver(()=>{if(document.title===N)return;N=document.title,c({type:"title",title:N})}).observe(document.head,{childList:!0,subtree:!0,characterData:!0});var Y=!1,P=null;window.addEventListener("keyup",(e)=>{if(window.__toyonShell||e.key!==P)return;P=null,c({type:"key",key:e.key,meta:!1,up:!0})},!0);window.addEventListener("keydown",(e)=>{if(window.__toyonShell)return;if(j(e))e.preventDefault();let t=W(e,{guest:!0});if(t&&X(t.id).textKeeps&&q(e.target))return;if(Y){if(!t||!I.has(t.id))return}else if(e.key==="Escape"){c({type:"key",key:"Escape",meta:!1});return}else if(!t)return;e.preventDefault(),e.stopPropagation(),c({type:"key",key:e.key,code:e.code,meta:e.metaKey,ctrl:e.ctrlKey,shift:e.shiftKey,alt:e.altKey}),P=e.altKey?"Alt":e.ctrlKey?"Control":e.metaKey?"Meta":null},!0);var R=()=>c({type:"focus",gesture:navigator.userActivation?.isActive??!0});window.addEventListener("focusin",R,!0);window.addEventListener("focus",R);if(document.hasFocus()&&document.activeElement&&document.activeElement!==document.body)R();var Z=(e)=>!e.defaultPrevented&&!!e.dataTransfer?.types.includes("Files");window.addEventListener("dragover",(e)=>{if(!Z(e))return;if(e.preventDefault(),e.dataTransfer)e.dataTransfer.dropEffect="copy";c({type:"drag-files"})});window.addEventListener("drop",(e)=>{if(!Z(e))return;e.preventDefault(),c({type:"drop-files",files:Array.from(e.dataTransfer?.files??[])})});function B(e){for(let t of Object.keys(e))if(t.startsWith("__reactFiber$"))return e[t]??null;return null}var p=null;function ce(){if(p?.isConnected)return p;return p=document.createElement("div"),p.style.cssText="position:fixed;inset:0;pointer-events:none;z-index:2147483647;",document.documentElement.appendChild(p),p}function m(){if(p)p.innerHTML=""}var F=20;function E(e,t,n=!0){let r=document.createElement("div");if(r.style.cssText=`position:fixed;left:${e.left}px;top:${e.top}px;width:${e.width}px;height:${e.height}px;outline:2px solid ${x};outline-offset:-1px;${n?`background:${x}14;`:""}border-radius:2px;`,t){let i=e.top>=F,o=e.bottom+F<=window.innerHeight;if(t.style.top=i?`${-F}px`:o?`${e.height+4}px`:"2px",!i&&!o)t.style.left="2px";r.appendChild(t)}if(ce().appendChild(r),t){let i=t.getBoundingClientRect(),o=i.right-(window.innerWidth-2);if(o>0){let s=Number.parseFloat(t.style.left||"0");t.style.left=`${s-Math.min(o,Math.max(0,i.left-2))}px`}}}function g(e,t){let n=document.createElement("div");if(n.textContent=e,n.style.cssText=`position:absolute;left:0;background:${x};color:${G};font:11px -apple-system,sans-serif;padding:1px 6px;border-radius:3px;white-space:nowrap;`,t){let r=document.createElement("span");r.textContent=` ${t}`,r.style.opacity="0.55",n.appendChild(r)}return n}var v=!1,d=null,y=!1,f=!1,h=!1,O=(e)=>`${Q(e.file)}${e.line?`:${e.line}`:""}`;function A(e){d=e,m();let t=L(B(e)),{src:n,comp:r,call:i}=t,o=_(t,f),s=o===n?i:n,l=y!==h,a=(...k)=>k.filter(Boolean).join(" ")||void 0,u=l?o?g(`open ${O(o)}`,a(s&&`⇧ ${O(s)}`,h&&"⌥ chat")):g(`find ${U(e)}`,a(h&&"⌥ chat")):g(r?`<${r} />${o?` · ${O(o)}`:""}`:U(e),a(!h&&"⌥ code"));E(e.getBoundingClientRect(),u,l)}function U(e){let t=e.tagName.toLowerCase(),n=e.classList[0];return(e.id?`${t}#${e.id}`:n?`${t}.${n}`:t).slice(0,48)}var ue=new Set(["name","placeholder","aria-label","title","alt","for","href","type","role","src"]);function de(e){let t="";for(let r of Array.from(e.childNodes))if(r.nodeType===Node.TEXT_NODE)t+=r.textContent??"";let n=[];for(let r of Array.from(e.attributes)){if(n.length===12)break;if(r.value&&(ue.has(r.name)||r.name.startsWith("data-")))n.push([r.name.slice(0,40),r.value.slice(0,200)])}return{tag:e.tagName.toLowerCase().slice(0,40),id:e.id.slice(0,200),classes:Array.from(e.classList).slice(0,16).map((r)=>r.slice(0,120)),text:t.replace(/\s+/g," ").trim().slice(0,200),attrs:n}}function V(e){let t=document.elementFromPoint(e.clientX,e.clientY);if(!t)return;let n=t!==d||e.altKey!==y||e.shiftKey!==f;if(y=e.altKey,f=e.shiftKey,n)A(t)}function J(e){e.preventDefault(),e.stopPropagation();let t=d??document.elementFromPoint(e.clientX,e.clientY),n=L(t?B(t):null),{src:r,comp:i,call:o}=n;f=e.shiftKey;let s=_(n,f),l=e.altKey!==h;if(D(),!t)return;c({type:"picked",verb:l?"code":"chat",site:s&&s===o?"call":"source",component:i,file:r?.file??null,line:r?.line??null,callFile:o?.file??null,callLine:o?.line??null,selector:ye(t),tag:t.tagName.toLowerCase(),classes:t.className?.toString?.().slice(0,200)??"",text:t.textContent?.trim().slice(0,120)??"",html:t.outerHTML.slice(0,600),route:location.pathname+location.search,element:de(t)})}function S(e){if(e.key==="Escape"&&e.type==="keydown"){e.preventDefault(),e.stopPropagation(),D(),c({type:"pick-cancel"});return}if((e.key==="Alt"||e.key==="Shift")&&(y!==e.altKey||f!==e.shiftKey)){if(y=e.altKey,f=e.shiftKey,d)A(d)}}function fe(e){if(h=e,v){if(d)A(d);return}v=!0,d=null,y=!1,f=!1,document.addEventListener("mousemove",V,!0),document.addEventListener("click",J,!0),document.addEventListener("keydown",S,!0),document.addEventListener("keyup",S,!0),document.documentElement.style.cursor="crosshair"}function D(){v=!1,h=!1,d=null,y=!1,f=!1,m(),document.removeEventListener("mousemove",V,!0),document.removeEventListener("click",J,!0),document.removeEventListener("keydown",S,!0),document.removeEventListener("keyup",S,!0),document.documentElement.style.cursor=""}function ye(e){let t=[],n=e;for(let r=0;n&&n!==document.documentElement&&r<6;r++){if(n.id){t.unshift(`#${CSS.escape(n.id)}`);break}let i=n.parentElement,o=i?Array.from(i.children).indexOf(n)+1:1;t.unshift(`${n.tagName.toLowerCase()}:nth-child(${o})`),n=i}return t.join(" > ")}function Q(e){let t=e.lastIndexOf("/src/");return t>=0?e.slice(t+1):e.split("/").slice(-2).join("/")}function pe(e,t){return e.endsWith(t)||t.endsWith(Q(e))}function he(e,t){let n=[],r=0;for(let s of Array.from(document.querySelectorAll("*"))){let l=T(B(s));if(l)r++;if(!l||!pe(l.file,e))continue;n.push({el:s,line:l.line??-1})}if(!t||t.length===0)return{matched:n,fromFile:n,withSource:r};if(t.length===1&&t[0][0]===t[0][1]){let s=t[0][0],l=n.filter((u)=>u.line===s);if(l.length>0)return{matched:l,fromFile:n,withSource:r};let a=n.filter((u)=>u.line>0&&u.line<s);return a.sort((u,k)=>k.line-u.line),{matched:a.slice(0,1),fromFile:n,withSource:r}}return{matched:n.filter((s)=>t.some(([l,a])=>s.line>=l-8&&s.line<=a+1)),fromFile:n,withSource:r}}function me(e,t){m();let{matched:n,fromFile:r,withSource:i}=he(e,t),o=0;for(let{el:s}of n){if(o>=40)break;let l=s.getBoundingClientRect();if(l.width>0&&l.height>0)E(l),o++}if(o===0)c({type:"highlight-miss",path:e,fileMatched:r.length,withSource:i,ranges:t})}window.addEventListener("message",(e)=>{if(e.source!==window.parent)return;if(w.length&&!w.includes(e.origin))return;let t=e.data;if(!t?.__toyon)return;M=e.origin;let n=t;switch(n.type){case"reload":location.reload();break;case"navigate":if(/^[/?#]/.test(n.path))location.assign(n.path);break;case"back":case"forward":{let r=window.navigation;if((n.type==="back"?r?.canGoBack:r?.canGoForward)===!1)break;if(n.type==="back")history.back();else history.forward();break}case"links":{let r=new Set,i=[];for(let o of Array.from(document.querySelectorAll("a[href]"))){if(o.origin!==location.origin||o.target==="_blank"||o.hasAttribute("download"))continue;let s=o.pathname+(o.hash.startsWith("#/")?o.hash:"");if(s.startsWith("/__toyon")||r.has(s))continue;r.add(s);let l=(o.textContent||o.getAttribute("aria-label")||"").replace(/\s+/g," ").trim();if(i.push({path:s,text:l.slice(0,80)}),i.length>=200)break}c({type:"links",links:i});break}case"pick-start":fe(n.verb==="code");break;case"pick-mods":if(v&&(y!==n.alt||f!==n.shift)){if(y=n.alt,f=n.shift,d)A(d)}break;case"pick-cancel":D();break;case"highlight-file":me(n.path,n.ranges??null);break;case"highlight-computed":{m();let r=/^(color|font|line-height|letter-|text-|word-)/,i=(l)=>{if(/^(INPUT|TEXTAREA|SELECT)$/.test(l.tagName))return!0;for(let a=l.firstChild;a;a=a.nextSibling)if(a.nodeType===3&&a.textContent&&a.textContent.trim())return!0;return!1},o=document.querySelectorAll("*"),s=!0;for(let l=0;l<o.length;l++){let a=getComputedStyle(o[l]),u=([H,ee])=>a.getPropertyValue(H)===ee&&(!r.test(H)||i(o[l]));if(n.match==="any"?n.props.some(u):n.props.every(u))E(o[l].getBoundingClientRect(),s&&n.label?g(n.label):void 0),s=!1}break}case"highlight-selector":{m();try{let r=document.querySelectorAll(n.selector);for(let i=0;i<r.length;i++)E(r[i].getBoundingClientRect(),i===0&&n.label?g(n.label):void 0)}catch{}break}case"highlight-clear":m();break;case"zen":Y=n.on;break;case"theme":if(/^#[0-9a-f]{3,8}$/i.test(n.accent))x=n.accent;if(/^#[0-9a-f]{3,8}$/i.test(n.accentFg))G=n.accentFg;break}});
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli.ts
5
- import { existsSync as existsSync10 } from "fs";
5
+ import { existsSync as existsSync10, statSync as statSync4 } from "fs";
6
6
  import { join as join8, resolve } from "path";
7
7
 
8
8
  // ../shared/src/daemon.ts
@@ -5158,7 +5158,7 @@ function parseVersion(v) {
5158
5158
  // package.json
5159
5159
  var package_default = {
5160
5160
  name: "toyon",
5161
- version: "0.6.0",
5161
+ version: "0.8.0",
5162
5162
  description: "Toyon is where coding agents work on your web app while you check each change by using it. Every chat gets its own running copy with a live preview.",
5163
5163
  license: "MIT",
5164
5164
  type: "module",
@@ -5286,6 +5286,60 @@ function shellUrl(token, branded) {
5286
5286
  return branded ? `http://toyon.localhost/#token=${token}` : `http://toyon.localhost:${port}/#token=${token}`;
5287
5287
  }
5288
5288
 
5289
+ // src/launcher.m
5290
+ var launcher_default = `// The Toyon.app bundle's executable. A plain C stub that execs the launch script never hears
5291
+ // about a file dropped on the Dock icon: that arrives as an Apple Event, which only an
5292
+ // NSApplication receives. So this is the smallest Cocoa app there is: it collects the paths the
5293
+ // event names, answers the event (Finder waits on the answer, and an exec before it would leave
5294
+ // Finder hanging), and once launched execs the script with those paths. The open-files call comes
5295
+ // before the finish-launching one, which is why the paths are collected first and the exec waits
5296
+ // for the second. With nothing dropped the script runs with no arguments.
5297
+ //
5298
+ // Read into the CLI as text (app.ts) and compiled at \`toyon --install-app\` on the machine itself.
5299
+ #import <Cocoa/Cocoa.h>
5300
+ #include <libgen.h>
5301
+ #include <mach-o/dyld.h>
5302
+ #include <stdlib.h>
5303
+ #include <unistd.h>
5304
+
5305
+ @interface Launcher : NSObject <NSApplicationDelegate>
5306
+ @property(strong) NSMutableArray<NSString *> *paths;
5307
+ @end
5308
+
5309
+ @implementation Launcher
5310
+ - (void)application:(NSApplication *)app openFiles:(NSArray<NSString *> *)files {
5311
+ [self.paths addObjectsFromArray:files];
5312
+ [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
5313
+ }
5314
+ - (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
5315
+ return YES;
5316
+ }
5317
+ - (void)applicationDidFinishLaunching:(NSNotification *)note {
5318
+ char self_[4096];
5319
+ uint32_t n = sizeof(self_);
5320
+ if (_NSGetExecutablePath(self_, &n) != 0) exit(1);
5321
+ NSString *script = [NSString stringWithFormat:@"%s/../Resources/launch.sh", dirname(self_)];
5322
+ NSMutableArray<NSString *> *argv = [NSMutableArray arrayWithObjects:@"bash", script, nil];
5323
+ [argv addObjectsFromArray:self.paths];
5324
+ const char **cargv = calloc(argv.count + 1, sizeof(char *));
5325
+ for (NSUInteger i = 0; i < argv.count; i++) cargv[i] = [argv[i] fileSystemRepresentation];
5326
+ execv("/bin/bash", (char *const *)cargv);
5327
+ exit(1);
5328
+ }
5329
+ @end
5330
+
5331
+ int main(void) {
5332
+ @autoreleasepool {
5333
+ NSApplication *app = [NSApplication sharedApplication];
5334
+ Launcher *launcher = [Launcher new];
5335
+ launcher.paths = [NSMutableArray new];
5336
+ app.delegate = launcher;
5337
+ [app run];
5338
+ }
5339
+ return 0;
5340
+ }
5341
+ `;
5342
+
5289
5343
  // src/app.ts
5290
5344
  var CHROMIUMS = ["Google Chrome", "Arc", "Brave Browser", "Microsoft Edge", "Chromium"];
5291
5345
  var DATA_DIRS = {
@@ -5364,6 +5418,20 @@ function installApp(appUrl) {
5364
5418
  <key>CFBundlePackageType</key><string>APPL</string>
5365
5419
  <key>CFBundleExecutable</key><string>Toyon</string>
5366
5420
  <key>CFBundleIconFile</key><string>AppIcon</string>
5421
+ <!-- takes any file or folder dropped on it, without becoming the default opener for anything -->
5422
+ <key>CFBundleDocumentTypes</key>
5423
+ <array>
5424
+ <dict>
5425
+ <key>CFBundleTypeName</key><string>Anything</string>
5426
+ <key>CFBundleTypeRole</key><string>Editor</string>
5427
+ <key>LSHandlerRank</key><string>Alternate</string>
5428
+ <key>LSItemContentTypes</key>
5429
+ <array>
5430
+ <string>public.item</string>
5431
+ <string>public.folder</string>
5432
+ </array>
5433
+ </dict>
5434
+ </array>
5367
5435
  </dict></plist>
5368
5436
  `);
5369
5437
  const launcher = join3(resources, "launch.sh");
@@ -5386,6 +5454,22 @@ if ! curl -s --max-time 1 http://127.0.0.1:${port}/health >/dev/null 2>&1; then
5386
5454
  fi
5387
5455
 
5388
5456
  TOKEN=$(cat "$HOME/.toyon/token" 2>/dev/null)
5457
+
5458
+ # what was dropped on the icon, or opened with it: each path goes to the daemon, which opens a
5459
+ # project, a file in one, or a file on its own, and holds it for the window opened below. A
5460
+ # refusal is logged in the daemon's words, since nothing here has a screen (the daemon says it in
5461
+ # the shell too); a success is logged as its status alone, since the reply carries the file's text.
5462
+ for P in "$@"; do
5463
+ BODY=$("$BUN" -e 'console.log(JSON.stringify({ path: process.argv[1] }))' "$P" 2>/dev/null) || continue
5464
+ REPLY=$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "Authorization: Bearer $TOKEN" \\
5465
+ -H "content-type: application/json" --data "$BODY" http://127.0.0.1:${port}/open 2>/dev/null)
5466
+ if [ "$REPLY" != "200" ]; then
5467
+ REPLY="$REPLY $(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" \\
5468
+ --data "$BODY" http://127.0.0.1:${port}/open 2>&1 | head -c 300)"
5469
+ fi
5470
+ echo "$(date '+%Y-%m-%d %H:%M:%S') open $P: $REPLY" >> "$HOME/.toyon/launcher.log"
5471
+ done
5472
+
5389
5473
  URL="http://toyon.localhost:${port}/#token=$TOKEN"
5390
5474
  MANIFEST_ID="${manifestId}"
5391
5475
  CACHE_DIR="$HOME/.toyon/pwa"
@@ -5420,24 +5504,9 @@ exec open "$URL"
5420
5504
  `);
5421
5505
  chmodSync(launcher, 493);
5422
5506
  const stub = join3(macos, "Toyon");
5423
- const cSrc = join3(home, "launcher.c");
5424
- writeFileSync(cSrc, `#include <mach-o/dyld.h>
5425
- #include <stdio.h>
5426
- #include <stdint.h>
5427
- #include <stdlib.h>
5428
- #include <string.h>
5429
- #include <unistd.h>
5430
- #include <libgen.h>
5431
- int main(int argc, char **argv) {
5432
- char self[4096]; uint32_t n = sizeof(self);
5433
- if (_NSGetExecutablePath(self, &n) != 0) return 1;
5434
- char script[4600];
5435
- snprintf(script, sizeof(script), "%s/../Resources/launch.sh", dirname(self));
5436
- execl("/bin/bash", "bash", script, (char *)0);
5437
- return 1;
5438
- }
5439
- `);
5440
- const cc = spawnSync("clang", ["-O2", "-o", stub, cSrc], { stdio: "ignore" });
5507
+ const src = join3(home, "launcher.m");
5508
+ writeFileSync(src, launcher_default);
5509
+ const cc = spawnSync("clang", ["-O2", "-fobjc-arc", "-framework", "Cocoa", "-o", stub, src], { stdio: "ignore" });
5441
5510
  if (cc.status !== 0) {
5442
5511
  writeFileSync(stub, `#!/bin/bash
5443
5512
  exec /bin/bash "$(dirname "$0")/../Resources/launch.sh"
@@ -5621,6 +5690,7 @@ var HELP = `toyon: one chat per git worktree, every worktree running live
5621
5690
 
5622
5691
  usage
5623
5692
  toyon [path] start the daemon if it is not running, register the repo at path
5693
+ (or open the file at path: in its project, or on its own)
5624
5694
  (default: the current directory) and open the shell
5625
5695
  toyon stop stop the daemon and every dev server and agent it runs
5626
5696
  toyon restart stop the daemon and start it again from what is installed now; every
@@ -6148,6 +6218,9 @@ async function doctor() {
6148
6218
  lines.push(line(true, "updates", "off for this machine (TOYON_UPDATES=off)"));
6149
6219
  } else if (h.updates?.unreachable) {
6150
6220
  lines.push(line(false, "updates", `npm could not get toyon from ${h.updates.unreachable}, so Toyon cannot update itself; if you may use the public registry, run npm install -g toyon --registry=https://registry.npmjs.org/`));
6221
+ } else if (h.updates?.registry) {
6222
+ const newest = h.updates.latest ? `${h.updates.latest} is out` : `${version} is the newest it lists`;
6223
+ lines.push(line(true, "updates", `from ${h.updates.registry}; ${newest}`));
6151
6224
  }
6152
6225
  }
6153
6226
  const token = readToken();
@@ -8334,7 +8407,20 @@ async function open2(cmd) {
8334
8407
  console.error("daemon token missing; `toyon doctor` says where it looked");
8335
8408
  return 1;
8336
8409
  }
8337
- if (register) {
8410
+ const file = explicit && existsSync10(target) && statSync4(target).isFile();
8411
+ if (file) {
8412
+ const res = await fetch(`${base}/open`, {
8413
+ method: "POST",
8414
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
8415
+ body: JSON.stringify({ path: target })
8416
+ });
8417
+ if (!res.ok) {
8418
+ console.error(`could not open ${target}: ${await res.text()}`);
8419
+ return 1;
8420
+ }
8421
+ const opened = await res.json();
8422
+ console.log(opened.kind === "file" ? `toyon: opening ${target} in its project` : `toyon: opening ${target}`);
8423
+ } else if (register) {
8338
8424
  const res = await fetch(`${base}/register`, {
8339
8425
  method: "POST",
8340
8426
  headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },