@hienlh/ppm 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +52 -0
- package/dist/web/assets/{button-KIZetva8.js → button-CvHWF07y.js} +1 -1
- package/dist/web/assets/{chat-tab-CNXjLOhI.js → chat-tab-C4ovA2w4.js} +3 -3
- package/dist/web/assets/{code-editor-tGMPwYNs.js → code-editor-BgiyQO-M.js} +1 -1
- package/dist/web/assets/{dialog-D8ulRTfX.js → dialog-f3IZM-6v.js} +1 -1
- package/dist/web/assets/{diff-viewer-B4A8pPbo.js → diff-viewer-8_asmBRZ.js} +1 -1
- package/dist/web/assets/{dist-C4W3AGh3.js → dist-CCBctnax.js} +1 -1
- package/dist/web/assets/{git-graph-ODjrGZOQ.js → git-graph-BiyTIbCz.js} +1 -1
- package/dist/web/assets/{git-status-panel-B0Im1hrU.js → git-status-panel-BifyO31N.js} +1 -1
- package/dist/web/assets/index-DILaVO6p.css +2 -0
- package/dist/web/assets/index-DasstYgw.js +11 -0
- package/dist/web/assets/project-list-C7L3hZct.js +1 -0
- package/dist/web/assets/settings-tab-Cn5Ja0_J.js +1 -0
- package/dist/web/assets/{terminal-tab-DDf6S-Tu.js → terminal-tab-CyjhG4Ao.js} +1 -1
- package/dist/web/index.html +8 -8
- package/dist/web/sw.js +1 -1
- package/docs/code-standards.md +74 -0
- package/docs/codebase-summary.md +11 -7
- package/docs/deployment-guide.md +81 -11
- package/docs/project-overview-pdr.md +62 -2
- package/docs/system-architecture.md +24 -8
- package/package.json +5 -2
- package/src/cli/commands/init.ts +196 -43
- package/src/cli/commands/logs.ts +58 -0
- package/src/cli/commands/report.ts +60 -0
- package/src/cli/commands/status.ts +73 -0
- package/src/cli/commands/stop.ts +24 -10
- package/src/index.ts +48 -6
- package/src/server/index.ts +184 -17
- package/src/services/cloudflared.service.ts +99 -0
- package/src/services/tunnel.service.ts +100 -0
- package/src/types/config.ts +2 -0
- package/src/web/app.tsx +10 -2
- package/src/web/components/auth/login-screen.tsx +9 -2
- package/src/web/components/layout/sidebar.tsx +15 -0
- package/src/web/components/ui/sonner.tsx +9 -6
- package/src/web/hooks/use-health-check.ts +95 -0
- package/src/web/stores/settings-store.ts +19 -0
- package/src/web/stores/tab-store.ts +28 -8
- package/src/web/styles/globals.css +4 -3
- package/dist/web/assets/index-BePIZMuy.css +0 -2
- package/dist/web/assets/index-D2STBl88.js +0 -10
- package/dist/web/assets/project-list-VjQQcU3X.js +0 -1
- package/dist/web/assets/settings-tab-ChhdL0EG.js +0 -1
- /package/dist/web/assets/{dist-PA84y4Ga.js → dist-B6sG2GPc.js} +0 -0
- /package/dist/web/assets/{react-BSLFEYu8.js → react-gOPBns57.js} +0 -0
- /package/dist/web/assets/{utils-DpJF9mAi.js → utils-61GRB9Cb.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,n as t,r as n,t as r}from"./jsx-runtime-BFALxl05.js";import{t as i}from"./button-CvHWF07y.js";import{a,i as o,n as s,o as c,t as l}from"./dialog-f3IZM-6v.js";import{t as u}from"./utils-61GRB9Cb.js";import{t as d}from"./api-client-BgVufYKf.js";import{_ as f,b as p,c as m,h,l as g,n as _,v}from"./index-DasstYgw.js";var y=t(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),b=t(`folder-git-2`,[[`path`,{d:`M18 19a5 5 0 0 1-5-5v8`,key:`sz5oeg`}],[`path`,{d:`M9 20H4a2 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.9H20a2 2 0 0 1 2 2v5`,key:`1w6njk`}],[`circle`,{cx:`13`,cy:`12`,r:`2`,key:`1j92g6`}],[`circle`,{cx:`20`,cy:`19`,r:`2`,key:`1obnsp`}]]),x=e(n(),1),S=r();function C({value:e,onChange:t,onSelect:n,placeholder:r,autoFocus:i}){let[a,o]=(0,x.useState)([]),[s,c]=(0,x.useState)([]),[l,u]=(0,x.useState)(!1),[p,m]=(0,x.useState)(0),[h,g]=(0,x.useState)(!1),v=(0,x.useRef)(null),y=(0,x.useRef)(!1);(0,x.useEffect)(()=>{y.current||(y.current=!0,g(!0),d.get(`/api/projects/suggest-dirs`).then(e=>{o(e),c(e.slice(0,50)),u(e.length>0)}).catch(()=>o([])).finally(()=>g(!1)))},[]),(0,x.useEffect)(()=>{if(a.length===0)return;let t=e.trim().toLowerCase();c(t?a.filter(e=>e.path.toLowerCase().includes(t)||e.name.toLowerCase().includes(t)).slice(0,50):a.slice(0,50)),m(0)},[e,a]),(0,x.useEffect)(()=>{let e=v.current;e&&e.children[p]?.scrollIntoView({block:`nearest`})},[p]);let C=(0,x.useCallback)(e=>{t(e.path),n?.(e),u(!1)},[t,n]),w=(0,x.useCallback)(e=>{if(!(!l||s.length===0))switch(e.key){case`ArrowDown`:e.preventDefault(),m(e=>e<s.length-1?e+1:0);break;case`ArrowUp`:e.preventDefault(),m(e=>e>0?e-1:s.length-1);break;case`Tab`:case`Enter`:s[p]&&(e.preventDefault(),C(s[p]));break;case`Escape`:e.preventDefault(),u(!1);break}},[l,s,p,C]);return(0,S.jsxs)(`div`,{className:`relative`,children:[(0,S.jsxs)(`div`,{className:`relative`,children:[(0,S.jsx)(_,{placeholder:r??`/home/user/my-project`,value:e,onChange:e=>t(e.target.value),onKeyDown:w,onFocus:()=>s.length>0&&u(!0),onBlur:()=>setTimeout(()=>u(!1),200),autoFocus:i}),h&&(0,S.jsx)(f,{className:`absolute right-2 top-1/2 -translate-y-1/2 size-4 text-text-subtle animate-spin`})]}),l&&s.length>0&&(0,S.jsx)(`div`,{className:`absolute z-50 left-0 right-0 top-full mt-1 max-h-48 overflow-y-auto rounded-md border border-border bg-surface shadow-lg`,children:(0,S.jsx)(`div`,{ref:v,className:`py-1`,children:s.map((e,t)=>(0,S.jsxs)(`button`,{type:`button`,className:`flex items-center gap-2 w-full px-3 py-1.5 text-left text-sm transition-colors ${t===p?`bg-primary/10 text-primary`:`hover:bg-surface-hover text-text-primary`}`,onMouseEnter:()=>m(t),onMouseDown:t=>{t.preventDefault(),C(e)},children:[(0,S.jsx)(b,{className:`size-4 text-green-500 shrink-0`}),(0,S.jsxs)(`div`,{className:`min-w-0 flex-1 flex items-baseline gap-2`,children:[(0,S.jsx)(`span`,{className:`font-medium`,children:e.name}),(0,S.jsx)(`span`,{className:`text-xs text-text-subtle truncate`,children:e.path})]})]},e.path))})})]})}function w(){let{projects:e,activeProject:t,setActiveProject:n,fetchProjects:r,loading:f,error:b}=m(),w=g(e=>e.openTab),[T,E]=(0,x.useState)(!1),[D,O]=(0,x.useState)(``),[k,A]=(0,x.useState)(``),[j,M]=(0,x.useState)(``);(0,x.useEffect)(()=>{r()},[r]);function N(e){n(e)}function P(e){n(e),w({type:`terminal`,title:`Terminal - ${e.name}`,metadata:{projectName:e.name},projectId:e.name,closable:!0})}let F=(0,x.useCallback)(async()=>{if(D.trim()){M(``);try{await d.post(`/api/projects`,{path:D.trim(),name:k.trim()||void 0}),await r(),E(!1),O(``),A(``)}catch(e){M(e instanceof Error?e.message:`Failed to add project`)}}},[D,k,r]);return b?(0,S.jsx)(`div`,{className:`flex items-center justify-center h-full p-4`,children:(0,S.jsx)(`p`,{className:`text-error text-sm`,children:b})}):(0,S.jsxs)(`div`,{className:`h-full p-4 space-y-4 overflow-auto`,children:[(0,S.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,S.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Projects`}),(0,S.jsxs)(i,{variant:`outline`,size:`sm`,onClick:()=>E(!0),className:`gap-1.5`,children:[(0,S.jsx)(h,{className:`size-4`}),`Add Project`]})]}),f&&(0,S.jsx)(`p`,{className:`text-text-secondary text-sm`,children:`Loading projects...`}),!f&&e.length===0&&(0,S.jsxs)(`div`,{className:`text-center py-8 space-y-2`,children:[(0,S.jsx)(p,{className:`size-10 mx-auto text-text-subtle`}),(0,S.jsx)(`p`,{className:`text-text-secondary text-sm`,children:`No projects registered`}),(0,S.jsxs)(`p`,{className:`text-text-subtle text-xs`,children:[`Click "Add Project" or use `,(0,S.jsx)(`code`,{className:`font-mono bg-surface-elevated px-1 py-0.5 rounded`,children:`ppm projects add <path>`})]})]}),(0,S.jsx)(`div`,{className:`grid gap-3 sm:grid-cols-2 lg:grid-cols-3`,children:e.map(e=>(0,S.jsx)(`button`,{onClick:()=>N(e),onDoubleClick:()=>P(e),className:u(`text-left p-4 rounded-lg border transition-colors`,`min-h-[44px]`,t?.name===e.name?`bg-surface border-primary`:`bg-surface border-border hover:border-text-subtle`),children:(0,S.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,S.jsx)(p,{className:`size-5 text-primary shrink-0 mt-0.5`}),(0,S.jsxs)(`div`,{className:`flex-1 min-w-0 space-y-1`,children:[(0,S.jsx)(`p`,{className:`font-medium truncate`,children:e.name}),(0,S.jsx)(`p`,{className:`text-xs text-text-secondary truncate`,children:e.path}),e.branch&&(0,S.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-text-secondary`,children:[(0,S.jsx)(v,{className:`size-3`}),(0,S.jsx)(`span`,{children:e.branch}),e.status&&(0,S.jsx)(y,{className:u(`size-2 fill-current`,e.status===`clean`?`text-success`:`text-warning`)})]})]})]})},e.name))}),(0,S.jsx)(l,{open:T,onOpenChange:E,children:(0,S.jsxs)(s,{children:[(0,S.jsx)(a,{children:(0,S.jsx)(c,{children:`Add Project`})}),(0,S.jsxs)(`div`,{className:`space-y-3`,children:[(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Path (required)`}),(0,S.jsx)(C,{value:D,onChange:O,onSelect:e=>{k.trim()||A(e.name)},placeholder:`/home/user/my-project`,autoFocus:!0})]}),(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Name (optional)`}),(0,S.jsx)(_,{placeholder:`Auto-detected from folder name`,value:k,onChange:e=>A(e.target.value),onKeyDown:e=>e.key===`Enter`&&F()})]}),j&&(0,S.jsx)(`p`,{className:`text-sm text-error`,children:j})]}),(0,S.jsxs)(o,{children:[(0,S.jsx)(i,{variant:`outline`,onClick:()=>E(!1),children:`Cancel`}),(0,S.jsx)(i,{onClick:F,disabled:!D.trim(),children:`Add`})]})]})})]})}export{w as ProjectList};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,n as t,r as n,t as r}from"./jsx-runtime-BFALxl05.js";import{_ as i,a,b as o,c as s,d as c,f as l,h as u,i as d,l as f,m as p,n as m,o as h,r as g,s as _,t as v,u as y,x as b}from"./button-CvHWF07y.js";import{a as x,i as S,n as C,o as w,r as T,t as E}from"./dist-CCBctnax.js";import{n as ee,t as D}from"./dist-B6sG2GPc.js";import{t as O}from"./utils-61GRB9Cb.js";import{t as k}from"./api-client-BgVufYKf.js";import{D as A,O as j,d as M,f as N,n as P,t as F}from"./index-DasstYgw.js";var te=t(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),I=t(`monitor`,[[`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`}]]),L=t(`moon`,[[`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`}]]),R=t(`sun`,[[`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`}]]),z=e(n(),1);function B(e){let t=z.useRef({value:e,previous:e});return z.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var V=r(),H=`Label`,ne=z.forwardRef((e,t)=>(0,V.jsx)(u.label,{...e,ref:t,onMouseDown:t=>{t.target.closest(`button, input, select, textarea`)||(e.onMouseDown?.(t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}}));ne.displayName=H;var re=ne,ie=e(b(),1),ae=[` `,`Enter`,`ArrowUp`,`ArrowDown`],oe=[` `,`Enter`],U=`Select`,[W,se,ce]=w(U),[G,le]=p(U,[ce,x]),K=x(),[ue,q]=G(U),[de,fe]=G(U),pe=e=>{let{__scopeSelect:t,children:n,open:r,defaultOpen:i,onOpenChange:a,value:o,defaultValue:s,onValueChange:c,dir:l,name:u,autoComplete:d,disabled:p,required:m,form:h}=e,g=K(t),[_,v]=z.useState(null),[b,x]=z.useState(null),[C,w]=z.useState(!1),T=ee(l),[E,D]=y({prop:r,defaultProp:i??!1,onChange:a,caller:U}),[O,k]=y({prop:o,defaultProp:s,onChange:c,caller:U}),A=z.useRef(null),j=_?h||!!_.closest(`form`):!0,[M,N]=z.useState(new Set),P=Array.from(M).map(e=>e.props.value).join(`;`);return(0,V.jsx)(S,{...g,children:(0,V.jsxs)(ue,{required:m,scope:t,trigger:_,onTriggerChange:v,valueNode:b,onValueNodeChange:x,valueNodeHasChildren:C,onValueNodeHasChildrenChange:w,contentId:f(),value:O,onValueChange:k,open:E,onOpenChange:D,dir:T,triggerPointerDownPosRef:A,disabled:p,children:[(0,V.jsx)(W.Provider,{scope:t,children:(0,V.jsx)(de,{scope:e.__scopeSelect,onNativeOptionAdd:z.useCallback(e=>{N(t=>new Set(t).add(e))},[]),onNativeOptionRemove:z.useCallback(e=>{N(t=>{let n=new Set(t);return n.delete(e),n})},[]),children:n})}),j?(0,V.jsxs)(rt,{"aria-hidden":!0,required:m,tabIndex:-1,name:u,autoComplete:d,value:O,onChange:e=>k(e.target.value),disabled:p,form:h,children:[O===void 0?(0,V.jsx)(`option`,{value:``}):null,Array.from(M)]},P):null]})})};pe.displayName=U;var me=`SelectTrigger`,he=z.forwardRef((e,t)=>{let{__scopeSelect:n,disabled:r=!1,...i}=e,a=K(n),s=q(me,n),c=s.disabled||r,d=o(t,s.onTriggerChange),f=se(n),p=z.useRef(`touch`),[m,h,g]=at(e=>{let t=f().filter(e=>!e.disabled),n=ot(t,e,t.find(e=>e.value===s.value));n!==void 0&&s.onValueChange(n.value)}),_=e=>{c||(s.onOpenChange(!0),g()),e&&(s.triggerPointerDownPosRef.current={x:Math.round(e.pageX),y:Math.round(e.pageY)})};return(0,V.jsx)(E,{asChild:!0,...a,children:(0,V.jsx)(u.button,{type:`button`,role:`combobox`,"aria-controls":s.contentId,"aria-expanded":s.open,"aria-required":s.required,"aria-autocomplete":`none`,dir:s.dir,"data-state":s.open?`open`:`closed`,disabled:c,"data-disabled":c?``:void 0,"data-placeholder":it(s.value)?``:void 0,...i,ref:d,onClick:l(i.onClick,e=>{e.currentTarget.focus(),p.current!==`mouse`&&_(e)}),onPointerDown:l(i.onPointerDown,e=>{p.current=e.pointerType;let t=e.target;t.hasPointerCapture(e.pointerId)&&t.releasePointerCapture(e.pointerId),e.button===0&&e.ctrlKey===!1&&e.pointerType===`mouse`&&(_(e),e.preventDefault())}),onKeyDown:l(i.onKeyDown,e=>{let t=m.current!==``;!(e.ctrlKey||e.altKey||e.metaKey)&&e.key.length===1&&h(e.key),!(t&&e.key===` `)&&ae.includes(e.key)&&(_(),e.preventDefault())})})})});he.displayName=me;var ge=`SelectValue`,_e=z.forwardRef((e,t)=>{let{__scopeSelect:n,className:r,style:i,children:a,placeholder:s=``,...l}=e,d=q(ge,n),{onValueNodeHasChildrenChange:f}=d,p=a!==void 0,m=o(t,d.onValueNodeChange);return c(()=>{f(p)},[f,p]),(0,V.jsx)(u.span,{...l,ref:m,style:{pointerEvents:`none`},children:it(d.value)?(0,V.jsx)(V.Fragment,{children:s}):a})});_e.displayName=ge;var ve=`SelectIcon`,ye=z.forwardRef((e,t)=>{let{__scopeSelect:n,children:r,...i}=e;return(0,V.jsx)(u.span,{"aria-hidden":!0,...i,ref:t,children:r||`▼`})});ye.displayName=ve;var be=`SelectPortal`,xe=e=>(0,V.jsx)(a,{asChild:!0,...e});xe.displayName=be;var J=`SelectContent`,Se=z.forwardRef((e,t)=>{let n=q(J,e.__scopeSelect),[r,i]=z.useState();if(c(()=>{i(new DocumentFragment)},[]),!n.open){let t=r;return t?ie.createPortal((0,V.jsx)(Ce,{scope:e.__scopeSelect,children:(0,V.jsx)(W.Slot,{scope:e.__scopeSelect,children:(0,V.jsx)(`div`,{children:e.children})})}),t):null}return(0,V.jsx)(Ee,{...e,ref:t})});Se.displayName=J;var Y=10,[Ce,X]=G(J),we=`SelectContentImpl`,Te=i(`SelectContent.RemoveScroll`),Ee=z.forwardRef((e,t)=>{let{__scopeSelect:n,position:r=`item-aligned`,onCloseAutoFocus:i,onEscapeKeyDown:a,onPointerDownOutside:s,side:c,sideOffset:u,align:f,alignOffset:p,arrowPadding:v,collisionBoundary:y,collisionPadding:b,sticky:x,hideWhenDetached:S,avoidCollisions:C,...w}=e,T=q(J,n),[E,ee]=z.useState(null),[D,O]=z.useState(null),k=o(t,e=>ee(e)),[A,j]=z.useState(null),[M,N]=z.useState(null),P=se(n),[F,te]=z.useState(!1),I=z.useRef(!1);z.useEffect(()=>{if(E)return m(E)},[E]),d();let L=z.useCallback(e=>{let[t,...n]=P().map(e=>e.ref.current),[r]=n.slice(-1),i=document.activeElement;for(let n of e)if(n===i||(n?.scrollIntoView({block:`nearest`}),n===t&&D&&(D.scrollTop=0),n===r&&D&&(D.scrollTop=D.scrollHeight),n?.focus(),document.activeElement!==i))return},[P,D]),R=z.useCallback(()=>L([A,E]),[L,A,E]);z.useEffect(()=>{F&&R()},[F,R]);let{onOpenChange:B,triggerPointerDownPosRef:H}=T;z.useEffect(()=>{if(E){let e={x:0,y:0},t=t=>{e={x:Math.abs(Math.round(t.pageX)-(H.current?.x??0)),y:Math.abs(Math.round(t.pageY)-(H.current?.y??0))}},n=n=>{e.x<=10&&e.y<=10?n.preventDefault():E.contains(n.target)||B(!1),document.removeEventListener(`pointermove`,t),H.current=null};return H.current!==null&&(document.addEventListener(`pointermove`,t),document.addEventListener(`pointerup`,n,{capture:!0,once:!0})),()=>{document.removeEventListener(`pointermove`,t),document.removeEventListener(`pointerup`,n,{capture:!0})}}},[E,B,H]),z.useEffect(()=>{let e=()=>B(!1);return window.addEventListener(`blur`,e),window.addEventListener(`resize`,e),()=>{window.removeEventListener(`blur`,e),window.removeEventListener(`resize`,e)}},[B]);let[ne,re]=at(e=>{let t=P().filter(e=>!e.disabled),n=ot(t,e,t.find(e=>e.ref.current===document.activeElement));n&&setTimeout(()=>n.ref.current.focus())}),ie=z.useCallback((e,t,n)=>{let r=!I.current&&!n;(T.value!==void 0&&T.value===t||r)&&(j(e),r&&(I.current=!0))},[T.value]),ae=z.useCallback(()=>E?.focus(),[E]),oe=z.useCallback((e,t,n)=>{let r=!I.current&&!n;(T.value!==void 0&&T.value===t||r)&&N(e)},[T.value]),U=r===`popper`?Ae:Oe,W=U===Ae?{side:c,sideOffset:u,align:f,alignOffset:p,arrowPadding:v,collisionBoundary:y,collisionPadding:b,sticky:x,hideWhenDetached:S,avoidCollisions:C}:{};return(0,V.jsx)(Ce,{scope:n,content:E,viewport:D,onViewportChange:O,itemRefCallback:ie,selectedItem:A,onItemLeave:ae,itemTextRefCallback:oe,focusSelectedItem:R,selectedItemText:M,position:r,isPositioned:F,searchRef:ne,children:(0,V.jsx)(g,{as:Te,allowPinchZoom:!0,children:(0,V.jsx)(h,{asChild:!0,trapped:T.open,onMountAutoFocus:e=>{e.preventDefault()},onUnmountAutoFocus:l(i,e=>{T.trigger?.focus({preventScroll:!0}),e.preventDefault()}),children:(0,V.jsx)(_,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:a,onPointerDownOutside:s,onFocusOutside:e=>e.preventDefault(),onDismiss:()=>T.onOpenChange(!1),children:(0,V.jsx)(U,{role:`listbox`,id:T.contentId,"data-state":T.open?`open`:`closed`,dir:T.dir,onContextMenu:e=>e.preventDefault(),...w,...W,onPlaced:()=>te(!0),ref:k,style:{display:`flex`,flexDirection:`column`,outline:`none`,...w.style},onKeyDown:l(w.onKeyDown,e=>{let t=e.ctrlKey||e.altKey||e.metaKey;if(e.key===`Tab`&&e.preventDefault(),!t&&e.key.length===1&&re(e.key),[`ArrowUp`,`ArrowDown`,`Home`,`End`].includes(e.key)){let t=P().filter(e=>!e.disabled).map(e=>e.ref.current);if([`ArrowUp`,`End`].includes(e.key)&&(t=t.slice().reverse()),[`ArrowUp`,`ArrowDown`].includes(e.key)){let n=e.target,r=t.indexOf(n);t=t.slice(r+1)}setTimeout(()=>L(t)),e.preventDefault()}})})})})})})});Ee.displayName=we;var De=`SelectItemAlignedPosition`,Oe=z.forwardRef((e,t)=>{let{__scopeSelect:n,onPlaced:r,...i}=e,a=q(J,n),s=X(J,n),[l,d]=z.useState(null),[f,p]=z.useState(null),m=o(t,e=>p(e)),h=se(n),g=z.useRef(!1),_=z.useRef(!0),{viewport:v,selectedItem:y,selectedItemText:b,focusSelectedItem:x}=s,S=z.useCallback(()=>{if(a.trigger&&a.valueNode&&l&&f&&v&&y&&b){let e=a.trigger.getBoundingClientRect(),t=f.getBoundingClientRect(),n=a.valueNode.getBoundingClientRect(),i=b.getBoundingClientRect();if(a.dir!==`rtl`){let r=i.left-t.left,a=n.left-r,o=e.left-a,s=e.width+o,c=Math.max(s,t.width),u=window.innerWidth-Y,d=D(a,[Y,Math.max(Y,u-c)]);l.style.minWidth=s+`px`,l.style.left=d+`px`}else{let r=t.right-i.right,a=window.innerWidth-n.right-r,o=window.innerWidth-e.right-a,s=e.width+o,c=Math.max(s,t.width),u=window.innerWidth-Y,d=D(a,[Y,Math.max(Y,u-c)]);l.style.minWidth=s+`px`,l.style.right=d+`px`}let o=h(),s=window.innerHeight-Y*2,c=v.scrollHeight,u=window.getComputedStyle(f),d=parseInt(u.borderTopWidth,10),p=parseInt(u.paddingTop,10),m=parseInt(u.borderBottomWidth,10),_=parseInt(u.paddingBottom,10),x=d+p+c+_+m,S=Math.min(y.offsetHeight*5,x),C=window.getComputedStyle(v),w=parseInt(C.paddingTop,10),T=parseInt(C.paddingBottom,10),E=e.top+e.height/2-Y,ee=s-E,O=y.offsetHeight/2,k=y.offsetTop+O,A=d+p+k,j=x-A;if(A<=E){let e=o.length>0&&y===o[o.length-1].ref.current;l.style.bottom=`0px`;let t=f.clientHeight-v.offsetTop-v.offsetHeight,n=A+Math.max(ee,O+(e?T:0)+t+m);l.style.height=n+`px`}else{let e=o.length>0&&y===o[0].ref.current;l.style.top=`0px`;let t=Math.max(E,d+v.offsetTop+(e?w:0)+O)+j;l.style.height=t+`px`,v.scrollTop=A-E+v.offsetTop}l.style.margin=`${Y}px 0`,l.style.minHeight=S+`px`,l.style.maxHeight=s+`px`,r?.(),requestAnimationFrame(()=>g.current=!0)}},[h,a.trigger,a.valueNode,l,f,v,y,b,a.dir,r]);c(()=>S(),[S]);let[C,w]=z.useState();return c(()=>{f&&w(window.getComputedStyle(f).zIndex)},[f]),(0,V.jsx)(je,{scope:n,contentWrapper:l,shouldExpandOnScrollRef:g,onScrollButtonChange:z.useCallback(e=>{e&&_.current===!0&&(S(),x?.(),_.current=!1)},[S,x]),children:(0,V.jsx)(`div`,{ref:d,style:{display:`flex`,flexDirection:`column`,position:`fixed`,zIndex:C},children:(0,V.jsx)(u.div,{...i,ref:m,style:{boxSizing:`border-box`,maxHeight:`100%`,...i.style}})})})});Oe.displayName=De;var ke=`SelectPopperPosition`,Ae=z.forwardRef((e,t)=>{let{__scopeSelect:n,align:r=`start`,collisionPadding:i=Y,...a}=e,o=K(n);return(0,V.jsx)(T,{...o,...a,ref:t,align:r,collisionPadding:i,style:{boxSizing:`border-box`,...a.style,"--radix-select-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-select-content-available-width":`var(--radix-popper-available-width)`,"--radix-select-content-available-height":`var(--radix-popper-available-height)`,"--radix-select-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-select-trigger-height":`var(--radix-popper-anchor-height)`}})});Ae.displayName=ke;var[je,Me]=G(J,{}),Ne=`SelectViewport`,Pe=z.forwardRef((e,t)=>{let{__scopeSelect:n,nonce:r,...i}=e,a=X(Ne,n),s=Me(Ne,n),c=o(t,a.onViewportChange),d=z.useRef(0);return(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`style`,{dangerouslySetInnerHTML:{__html:`[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}`},nonce:r}),(0,V.jsx)(W.Slot,{scope:n,children:(0,V.jsx)(u.div,{"data-radix-select-viewport":``,role:`presentation`,...i,ref:c,style:{position:`relative`,flex:1,overflow:`hidden auto`,...i.style},onScroll:l(i.onScroll,e=>{let t=e.currentTarget,{contentWrapper:n,shouldExpandOnScrollRef:r}=s;if(r?.current&&n){let e=Math.abs(d.current-t.scrollTop);if(e>0){let r=window.innerHeight-Y*2,i=parseFloat(n.style.minHeight),a=parseFloat(n.style.height),o=Math.max(i,a);if(o<r){let i=o+e,a=Math.min(r,i),s=i-a;n.style.height=a+`px`,n.style.bottom===`0px`&&(t.scrollTop=s>0?s:0,n.style.justifyContent=`flex-end`)}}}d.current=t.scrollTop})})})]})});Pe.displayName=Ne;var Fe=`SelectGroup`,[Ie,Le]=G(Fe),Re=z.forwardRef((e,t)=>{let{__scopeSelect:n,...r}=e,i=f();return(0,V.jsx)(Ie,{scope:n,id:i,children:(0,V.jsx)(u.div,{role:`group`,"aria-labelledby":i,...r,ref:t})})});Re.displayName=Fe;var ze=`SelectLabel`,Be=z.forwardRef((e,t)=>{let{__scopeSelect:n,...r}=e,i=Le(ze,n);return(0,V.jsx)(u.div,{id:i.id,...r,ref:t})});Be.displayName=ze;var Z=`SelectItem`,[Ve,He]=G(Z),Ue=z.forwardRef((e,t)=>{let{__scopeSelect:n,value:r,disabled:i=!1,textValue:a,...s}=e,c=q(Z,n),d=X(Z,n),p=c.value===r,[m,h]=z.useState(a??``),[g,_]=z.useState(!1),v=o(t,e=>d.itemRefCallback?.(e,r,i)),y=f(),b=z.useRef(`touch`),x=()=>{i||(c.onValueChange(r),c.onOpenChange(!1))};if(r===``)throw Error(`A <Select.Item /> must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.`);return(0,V.jsx)(Ve,{scope:n,value:r,disabled:i,textId:y,isSelected:p,onItemTextChange:z.useCallback(e=>{h(t=>t||(e?.textContent??``).trim())},[]),children:(0,V.jsx)(W.ItemSlot,{scope:n,value:r,disabled:i,textValue:m,children:(0,V.jsx)(u.div,{role:`option`,"aria-labelledby":y,"data-highlighted":g?``:void 0,"aria-selected":p&&g,"data-state":p?`checked`:`unchecked`,"aria-disabled":i||void 0,"data-disabled":i?``:void 0,tabIndex:i?void 0:-1,...s,ref:v,onFocus:l(s.onFocus,()=>_(!0)),onBlur:l(s.onBlur,()=>_(!1)),onClick:l(s.onClick,()=>{b.current!==`mouse`&&x()}),onPointerUp:l(s.onPointerUp,()=>{b.current===`mouse`&&x()}),onPointerDown:l(s.onPointerDown,e=>{b.current=e.pointerType}),onPointerMove:l(s.onPointerMove,e=>{b.current=e.pointerType,i?d.onItemLeave?.():b.current===`mouse`&&e.currentTarget.focus({preventScroll:!0})}),onPointerLeave:l(s.onPointerLeave,e=>{e.currentTarget===document.activeElement&&d.onItemLeave?.()}),onKeyDown:l(s.onKeyDown,e=>{d.searchRef?.current!==``&&e.key===` `||(oe.includes(e.key)&&x(),e.key===` `&&e.preventDefault())})})})})});Ue.displayName=Z;var Q=`SelectItemText`,We=z.forwardRef((e,t)=>{let{__scopeSelect:n,className:r,style:i,...a}=e,s=q(Q,n),l=X(Q,n),d=He(Q,n),f=fe(Q,n),[p,m]=z.useState(null),h=o(t,e=>m(e),d.onItemTextChange,e=>l.itemTextRefCallback?.(e,d.value,d.disabled)),g=p?.textContent,_=z.useMemo(()=>(0,V.jsx)(`option`,{value:d.value,disabled:d.disabled,children:g},d.value),[d.disabled,d.value,g]),{onNativeOptionAdd:v,onNativeOptionRemove:y}=f;return c(()=>(v(_),()=>y(_)),[v,y,_]),(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(u.span,{id:d.textId,...a,ref:h}),d.isSelected&&s.valueNode&&!s.valueNodeHasChildren?ie.createPortal(a.children,s.valueNode):null]})});We.displayName=Q;var Ge=`SelectItemIndicator`,Ke=z.forwardRef((e,t)=>{let{__scopeSelect:n,...r}=e;return He(Ge,n).isSelected?(0,V.jsx)(u.span,{"aria-hidden":!0,...r,ref:t}):null});Ke.displayName=Ge;var qe=`SelectScrollUpButton`,Je=z.forwardRef((e,t)=>{let n=X(qe,e.__scopeSelect),r=Me(qe,e.__scopeSelect),[i,a]=z.useState(!1),s=o(t,r.onScrollButtonChange);return c(()=>{if(n.viewport&&n.isPositioned){let e=function(){a(t.scrollTop>0)},t=n.viewport;return e(),t.addEventListener(`scroll`,e),()=>t.removeEventListener(`scroll`,e)}},[n.viewport,n.isPositioned]),i?(0,V.jsx)(Ze,{...e,ref:s,onAutoScroll:()=>{let{viewport:e,selectedItem:t}=n;e&&t&&(e.scrollTop-=t.offsetHeight)}}):null});Je.displayName=qe;var Ye=`SelectScrollDownButton`,Xe=z.forwardRef((e,t)=>{let n=X(Ye,e.__scopeSelect),r=Me(Ye,e.__scopeSelect),[i,a]=z.useState(!1),s=o(t,r.onScrollButtonChange);return c(()=>{if(n.viewport&&n.isPositioned){let e=function(){let e=t.scrollHeight-t.clientHeight;a(Math.ceil(t.scrollTop)<e)},t=n.viewport;return e(),t.addEventListener(`scroll`,e),()=>t.removeEventListener(`scroll`,e)}},[n.viewport,n.isPositioned]),i?(0,V.jsx)(Ze,{...e,ref:s,onAutoScroll:()=>{let{viewport:e,selectedItem:t}=n;e&&t&&(e.scrollTop+=t.offsetHeight)}}):null});Xe.displayName=Ye;var Ze=z.forwardRef((e,t)=>{let{__scopeSelect:n,onAutoScroll:r,...i}=e,a=X(`SelectScrollButton`,n),o=z.useRef(null),s=se(n),d=z.useCallback(()=>{o.current!==null&&(window.clearInterval(o.current),o.current=null)},[]);return z.useEffect(()=>()=>d(),[d]),c(()=>{s().find(e=>e.ref.current===document.activeElement)?.ref.current?.scrollIntoView({block:`nearest`})},[s]),(0,V.jsx)(u.div,{"aria-hidden":!0,...i,ref:t,style:{flexShrink:0,...i.style},onPointerDown:l(i.onPointerDown,()=>{o.current===null&&(o.current=window.setInterval(r,50))}),onPointerMove:l(i.onPointerMove,()=>{a.onItemLeave?.(),o.current===null&&(o.current=window.setInterval(r,50))}),onPointerLeave:l(i.onPointerLeave,()=>{d()})})}),Qe=`SelectSeparator`,$e=z.forwardRef((e,t)=>{let{__scopeSelect:n,...r}=e;return(0,V.jsx)(u.div,{"aria-hidden":!0,...r,ref:t})});$e.displayName=Qe;var et=`SelectArrow`,tt=z.forwardRef((e,t)=>{let{__scopeSelect:n,...r}=e,i=K(n),a=q(et,n),o=X(et,n);return a.open&&o.position===`popper`?(0,V.jsx)(C,{...i,...r,ref:t}):null});tt.displayName=et;var nt=`SelectBubbleInput`,rt=z.forwardRef(({__scopeSelect:e,value:t,...n},r)=>{let i=z.useRef(null),a=o(r,i),s=B(t);return z.useEffect(()=>{let e=i.current;if(!e)return;let n=window.HTMLSelectElement.prototype,r=Object.getOwnPropertyDescriptor(n,`value`).set;if(s!==t&&r){let n=new Event(`change`,{bubbles:!0});r.call(e,t),e.dispatchEvent(n)}},[s,t]),(0,V.jsx)(u.select,{...n,style:{...M,...n.style},ref:a,defaultValue:t})});rt.displayName=nt;function it(e){return e===``||e===void 0}function at(e){let t=s(e),n=z.useRef(``),r=z.useRef(0),i=z.useCallback(e=>{let i=n.current+e;t(i),(function e(t){n.current=t,window.clearTimeout(r.current),t!==``&&(r.current=window.setTimeout(()=>e(``),1e3))})(i)},[t]),a=z.useCallback(()=>{n.current=``,window.clearTimeout(r.current)},[]);return z.useEffect(()=>()=>window.clearTimeout(r.current),[]),[n,i,a]}function ot(e,t,n){let r=t.length>1&&Array.from(t).every(e=>e===t[0])?t[0]:t,i=n?e.indexOf(n):-1,a=st(e,Math.max(i,0));r.length===1&&(a=a.filter(e=>e!==n));let o=a.find(e=>e.textValue.toLowerCase().startsWith(r.toLowerCase()));return o===n?void 0:o}function st(e,t){return e.map((n,r)=>e[(t+r)%e.length])}var ct=pe,lt=he,ut=_e,dt=ye,ft=xe,pt=Se,mt=Pe,ht=Ue,gt=We,_t=Ke,vt=Je,yt=Xe;function $({className:e,...t}){return(0,V.jsx)(re,{"data-slot":`label`,className:O(`flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50`,e),...t})}function bt({...e}){return(0,V.jsx)(ct,{"data-slot":`select`,...e})}function xt({...e}){return(0,V.jsx)(ut,{"data-slot":`select-value`,...e})}function St({className:e,size:t=`default`,children:n,...r}){return(0,V.jsxs)(lt,{"data-slot":`select-trigger`,"data-size":t,className:O(`flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground`,e),...r,children:[n,(0,V.jsx)(dt,{asChild:!0,children:(0,V.jsx)(A,{className:`size-4 opacity-50`})})]})}function Ct({className:e,children:t,position:n=`item-aligned`,align:r=`center`,...i}){return(0,V.jsx)(ft,{children:(0,V.jsxs)(pt,{"data-slot":`select-content`,className:O(`relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95`,n===`popper`&&`data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1`,e),position:n,align:r,...i,children:[(0,V.jsx)(Tt,{}),(0,V.jsx)(mt,{className:O(`p-1`,n===`popper`&&`h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1`),children:t}),(0,V.jsx)(Et,{})]})})}function wt({className:e,children:t,...n}){return(0,V.jsxs)(ht,{"data-slot":`select-item`,className:O(`relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,e),...n,children:[(0,V.jsx)(`span`,{"data-slot":`select-item-indicator`,className:`absolute right-2 flex size-3.5 items-center justify-center`,children:(0,V.jsx)(_t,{children:(0,V.jsx)(j,{className:`size-4`})})}),(0,V.jsx)(gt,{children:t})]})}function Tt({className:e,...t}){return(0,V.jsx)(vt,{"data-slot":`select-scroll-up-button`,className:O(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,V.jsx)(te,{className:`size-4`})})}function Et({className:e,...t}){return(0,V.jsx)(yt,{"data-slot":`select-scroll-down-button`,className:O(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,V.jsx)(A,{className:`size-4`})})}function Dt(){return k.get(`/api/settings/ai`)}function Ot(e){return k.put(`/api/settings/ai`,e)}var kt=[{value:`claude-sonnet-4-6`,label:`Claude Sonnet 4.6`},{value:`claude-opus-4-6`,label:`Claude Opus 4.6`},{value:`claude-haiku-4-5`,label:`Claude Haiku 4.5`}],At=[{value:`low`,label:`Low`},{value:`medium`,label:`Medium`},{value:`high`,label:`High`},{value:`max`,label:`Max`}];function jt(){let[e,t]=(0,z.useState)(null),[n,r]=(0,z.useState)(!1),[i,a]=(0,z.useState)(null),[o,s]=(0,z.useState)(0);(0,z.useEffect)(()=>{Dt().then(t).catch(e=>a(e.message))},[]);let c=e?.default_provider??`claude`,l=e?.providers[c],u=async(n,i)=>{if(e){r(!0),a(null);try{t(await Ot({providers:{[c]:{[n]:i}}})),s(e=>e+1)}catch(e){a(e.message)}finally{r(!1)}}};return e?(0,V.jsxs)(`div`,{className:`space-y-4`,children:[(0,V.jsx)(`h3`,{className:`text-sm font-medium text-text-secondary`,children:`AI Provider`}),(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,V.jsx)($,{htmlFor:`ai-model`,children:`Model`}),(0,V.jsxs)(bt,{value:l?.model??`claude-sonnet-4-6`,onValueChange:e=>u(`model`,e),children:[(0,V.jsx)(St,{id:`ai-model`,className:`w-full`,children:(0,V.jsx)(xt,{})}),(0,V.jsx)(Ct,{children:kt.map(e=>(0,V.jsx)(wt,{value:e.value,children:e.label},e.value))})]})]}),(0,V.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,V.jsx)($,{htmlFor:`ai-effort`,children:`Effort`}),(0,V.jsxs)(bt,{value:l?.effort??`high`,onValueChange:e=>u(`effort`,e),children:[(0,V.jsx)(St,{id:`ai-effort`,className:`w-full`,children:(0,V.jsx)(xt,{})}),(0,V.jsx)(Ct,{children:At.map(e=>(0,V.jsx)(wt,{value:e.value,children:e.label},e.value))})]})]}),(0,V.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,V.jsx)($,{htmlFor:`ai-max-turns`,children:`Max Turns (1-500)`}),(0,V.jsx)(P,{id:`ai-max-turns`,type:`number`,min:1,max:500,defaultValue:l?.max_turns??100,onBlur:e=>{let t=parseInt(e.target.value);isNaN(t)||u(`max_turns`,t)}},`turns-${o}`)]}),(0,V.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,V.jsx)($,{htmlFor:`ai-budget`,children:`Max Budget (USD)`}),(0,V.jsx)(P,{id:`ai-budget`,type:`number`,step:.1,min:.01,max:50,defaultValue:l?.max_budget_usd??``,placeholder:`No limit`,onBlur:e=>{let t=parseFloat(e.target.value);u(`max_budget_usd`,isNaN(t)?void 0:t)}},`budget-${o}`)]}),(0,V.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,V.jsx)($,{htmlFor:`ai-thinking`,children:`Thinking Budget (tokens)`}),(0,V.jsx)(P,{id:`ai-thinking`,type:`number`,min:0,defaultValue:l?.thinking_budget_tokens??``,placeholder:`Disabled`,onBlur:e=>{let t=parseInt(e.target.value);u(`thinking_budget_tokens`,isNaN(t)?void 0:t)}},`thinking-${o}`)]})]}),n&&(0,V.jsx)(`p`,{className:`text-xs text-text-subtle`,children:`Saving...`}),i&&(0,V.jsx)(`p`,{className:`text-xs text-red-500`,children:i})]}):(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`h3`,{className:`text-sm font-medium text-text-secondary`,children:`AI Provider`}),(0,V.jsx)(`p`,{className:`text-sm text-text-subtle`,children:i?`Error: ${i}`:`Loading...`})]})}var Mt=[{value:`light`,label:`Light`,icon:R},{value:`dark`,label:`Dark`,icon:L},{value:`system`,label:`System`,icon:I}];function Nt(){let{theme:e,setTheme:t}=N();return(0,V.jsxs)(`div`,{className:`h-full p-4 space-y-6 overflow-auto max-w-lg`,children:[(0,V.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Settings`}),(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`h3`,{className:`text-sm font-medium text-text-secondary`,children:`Theme`}),(0,V.jsx)(`div`,{className:`flex gap-2`,children:Mt.map(n=>{let r=n.icon;return(0,V.jsxs)(v,{variant:e===n.value?`default`:`outline`,size:`lg`,onClick:()=>t(n.value),className:O(`flex-1 gap-2`,e===n.value&&`ring-2 ring-primary`),children:[(0,V.jsx)(r,{className:`size-4`}),n.label]},n.value)})})]}),(0,V.jsx)(F,{}),(0,V.jsx)(jt,{}),(0,V.jsx)(F,{}),(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`h3`,{className:`text-sm font-medium text-text-secondary`,children:`About`}),(0,V.jsx)(`p`,{className:`text-sm text-text-secondary`,children:`PPM — Personal Project Manager`}),(0,V.jsx)(`p`,{className:`text-xs text-text-subtle`,children:`A mobile-first web IDE for managing your projects.`})]})]})}export{Nt as SettingsTab};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,n as t,r as n,t as r}from"./jsx-runtime-BFALxl05.js";import{t as i}from"./copy-B-kLwqzg.js";import{t as a}from"./utils-DpJF9mAi.js";var o=t(`clipboard-paste`,[[`path`,{d:`M11 14h10`,key:`1w8e9d`}],[`path`,{d:`M16 4h2a2 2 0 0 1 2 2v1.344`,key:`1e62lh`}],[`path`,{d:`m17 18 4-4-4-4`,key:`z2g111`}],[`path`,{d:`M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113`,key:`bjbb7m`}],[`rect`,{x:`8`,y:`2`,width:`8`,height:`4`,rx:`1`,key:`ublpy`}]]),s=e(n(),1),c=Object.defineProperty,l=Object.getOwnPropertyDescriptor,u=(e,t)=>{for(var n in t)c(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{for(var i=r>1?void 0:r?l(t,n):t,a=e.length-1,o;a>=0;a--)(o=e[a])&&(i=(r?o(t,n,i):o(i))||i);return r&&i&&c(t,n,i),i},f=(e,t)=>(n,r)=>t(n,r,e),p=`Terminal input`,m={get:()=>p,set:e=>p=e},h=`Too much output to announce, navigate to rows manually to read`,g={get:()=>h,set:e=>h=e};function _(e){return e.replace(/\r?\n/g,`\r`)}function v(e,t){return t?`\x1B[200~`+e+`\x1B[201~`:e}function ee(e,t){e.clipboardData&&e.clipboardData.setData(`text/plain`,t.selectionText),e.preventDefault()}function te(e,t,n,r){e.stopPropagation(),e.clipboardData&&ne(e.clipboardData.getData(`text/plain`),t,n,r)}function ne(e,t,n,r){e=_(e),e=v(e,n.decPrivateModes.bracketedPasteMode&&r.rawOptions.ignoreBracketedPasteMode!==!0),n.triggerDataEvent(e,!0),t.value=``}function re(e,t,n){let r=n.getBoundingClientRect(),i=e.clientX-r.left-10,a=e.clientY-r.top-10;t.style.width=`20px`,t.style.height=`20px`,t.style.left=`${i}px`,t.style.top=`${a}px`,t.style.zIndex=`1000`,t.focus()}function ie(e,t,n,r,i){re(e,t,n),i&&r.rightClickSelect(e),t.value=r.selectionText,t.select()}function y(e){return e>65535?(e-=65536,String.fromCharCode((e>>10)+55296)+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)}function ae(e,t=0,n=e.length){let r=``;for(let i=t;i<n;++i){let t=e[i];t>65535?(t-=65536,r+=String.fromCharCode((t>>10)+55296)+String.fromCharCode(t%1024+56320)):r+=String.fromCharCode(t)}return r}var oe=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){let n=e.length;if(!n)return 0;let r=0,i=0;if(this._interim){let n=e.charCodeAt(i++);56320<=n&&n<=57343?t[r++]=(this._interim-55296)*1024+n-56320+65536:(t[r++]=this._interim,t[r++]=n),this._interim=0}for(let a=i;a<n;++a){let i=e.charCodeAt(a);if(55296<=i&&i<=56319){if(++a>=n)return this._interim=i,r;let o=e.charCodeAt(a);56320<=o&&o<=57343?t[r++]=(i-55296)*1024+o-56320+65536:(t[r++]=i,t[r++]=o);continue}i!==65279&&(t[r++]=i)}return r}},b=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){let n=e.length;if(!n)return 0;let r=0,i,a,o,s,c=0,l=0;if(this.interim[0]){let i=!1,a=this.interim[0];a&=(a&224)==192?31:(a&240)==224?15:7;let o=0,s;for(;(s=this.interim[++o]&63)&&o<4;)a<<=6,a|=s;let c=(this.interim[0]&224)==192?2:(this.interim[0]&240)==224?3:4,u=c-o;for(;l<u;){if(l>=n)return 0;if(s=e[l++],(s&192)!=128){l--,i=!0;break}else this.interim[o++]=s,a<<=6,a|=s&63}i||(c===2?a<128?l--:t[r++]=a:c===3?a<2048||a>=55296&&a<=57343||a===65279||(t[r++]=a):a<65536||a>1114111||(t[r++]=a)),this.interim.fill(0)}let u=n-4,d=l;for(;d<n;){for(;d<u&&!((i=e[d])&128)&&!((a=e[d+1])&128)&&!((o=e[d+2])&128)&&!((s=e[d+3])&128);)t[r++]=i,t[r++]=a,t[r++]=o,t[r++]=s,d+=4;if(i=e[d++],i<128)t[r++]=i;else if((i&224)==192){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(c=(i&31)<<6|a&63,c<128){d--;continue}t[r++]=c}else if((i&240)==224){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,r;if(o=e[d++],(o&192)!=128){d--;continue}if(c=(i&15)<<12|(a&63)<<6|o&63,c<2048||c>=55296&&c<=57343||c===65279)continue;t[r++]=c}else if((i&248)==240){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,r;if(o=e[d++],(o&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,this.interim[2]=o,r;if(s=e[d++],(s&192)!=128){d--;continue}if(c=(i&7)<<18|(a&63)<<12|(o&63)<<6|s&63,c<65536||c>1114111)continue;t[r++]=c}}return r}},se=``,ce=` `,le=class e{constructor(){this.fg=0,this.bg=0,this.extended=new x}static toColorRGB(e){return[e>>>16&255,e>>>8&255,e&255]}static fromColorRGB(e){return(e[0]&255)<<16|(e[1]&255)<<8|e[2]&255}clone(){let t=new e;return t.fg=this.fg,t.bg=this.bg,t.extended=this.extended.clone(),t}isInverse(){return this.fg&67108864}isBold(){return this.fg&134217728}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:this.fg&268435456}isBlink(){return this.fg&536870912}isInvisible(){return this.fg&1073741824}isItalic(){return this.bg&67108864}isDim(){return this.bg&134217728}isStrikethrough(){return this.fg&2147483648}isProtected(){return this.bg&536870912}isOverline(){return this.bg&1073741824}getFgColorMode(){return this.fg&50331648}getBgColorMode(){return this.bg&50331648}isFgRGB(){return(this.fg&50331648)==50331648}isBgRGB(){return(this.bg&50331648)==50331648}isFgPalette(){return(this.fg&50331648)==16777216||(this.fg&50331648)==33554432}isBgPalette(){return(this.bg&50331648)==16777216||(this.bg&50331648)==33554432}isFgDefault(){return(this.fg&50331648)==0}isBgDefault(){return(this.bg&50331648)==0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(this.fg&50331648){case 16777216:case 33554432:return this.fg&255;case 50331648:return this.fg&16777215;default:return-1}}getBgColor(){switch(this.bg&50331648){case 16777216:case 33554432:return this.bg&255;case 50331648:return this.bg&16777215;default:return-1}}hasExtendedAttrs(){return this.bg&268435456}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(this.bg&268435456&&~this.extended.underlineColor)switch(this.extended.underlineColor&50331648){case 16777216:case 33554432:return this.extended.underlineColor&255;case 50331648:return this.extended.underlineColor&16777215;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return this.bg&268435456&&~this.extended.underlineColor?this.extended.underlineColor&50331648:this.getFgColorMode()}isUnderlineColorRGB(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==50331648:this.isFgRGB()}isUnderlineColorPalette(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==16777216||(this.extended.underlineColor&50331648)==33554432:this.isFgPalette()}isUnderlineColorDefault(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==0:this.isFgDefault()}getUnderlineStyle(){return this.fg&268435456?this.bg&268435456?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}},x=class e{constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}get ext(){return this._urlId?this._ext&-469762049|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(this._ext&469762048)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return this._ext&67108863}set underlineColor(e){this._ext&=-67108864,this._ext|=e&67108863}get urlId(){return this._urlId}set urlId(e){this._urlId=e}get underlineVariantOffset(){let e=(this._ext&3758096384)>>29;return e<0?e^4294967288:e}set underlineVariantOffset(e){this._ext&=536870911,this._ext|=e<<29&3758096384}clone(){return new e(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}},S=class e extends le{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new x,this.combinedData=``}static fromCharData(t){let n=new e;return n.setFromCharData(t),n}isCombined(){return this.content&2097152}getWidth(){return this.content>>22}getChars(){return this.content&2097152?this.combinedData:this.content&2097151?y(this.content&2097151):``}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):this.content&2097151}setFromCharData(e){this.fg=e[0],this.bg=0;let t=!1;if(e[1].length>2)t=!0;else if(e[1].length===2){let n=e[1].charCodeAt(0);if(55296<=n&&n<=56319){let r=e[1].charCodeAt(1);56320<=r&&r<=57343?this.content=(n-55296)*1024+r-56320+65536|e[2]<<22:t=!0}else t=!0}else this.content=e[1].charCodeAt(0)|e[2]<<22;t&&(this.combinedData=e[1],this.content=2097152|e[2]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}},ue=`di$target`,de=`di$dependencies`,C=new Map;function fe(e){return e[de]||[]}function w(e){if(C.has(e))return C.get(e);let t=function(e,n,r){if(arguments.length!==3)throw Error(`@IServiceName-decorator can only be used to decorate a parameter`);pe(t,e,r)};return t._id=e,C.set(e,t),t}function pe(e,t,n){t[ue]===t?t[de].push({id:e,index:n}):(t[de]=[{id:e,index:n}],t[ue]=t)}var T=w(`BufferService`),me=w(`CoreMouseService`),E=w(`CoreService`),he=w(`CharsetService`),ge=w(`InstantiationService`),_e=w(`LogService`),D=w(`OptionsService`),ve=w(`OscLinkService`),ye=w(`UnicodeService`),be=w(`DecorationService`),xe=class{constructor(e,t,n){this._bufferService=e,this._optionsService=t,this._oscLinkService=n}provideLinks(e,t){let n=this._bufferService.buffer.lines.get(e-1);if(!n){t(void 0);return}let r=[],i=this._optionsService.rawOptions.linkHandler,a=new S,o=n.getTrimmedLength(),s=-1,c=-1,l=!1;for(let t=0;t<o;t++)if(!(c===-1&&!n.hasContent(t))){if(n.loadCell(t,a),a.hasExtendedAttrs()&&a.extended.urlId)if(c===-1){c=t,s=a.extended.urlId;continue}else l=a.extended.urlId!==s;else c!==-1&&(l=!0);if(l||c!==-1&&t===o-1){let n=this._oscLinkService.getLinkData(s)?.uri;if(n){let a={start:{x:c+1,y:e},end:{x:t+(!l&&t===o-1?1:0),y:e}},s=!1;if(!i?.allowNonHttpProtocols)try{let e=new URL(n);[`http:`,`https:`].includes(e.protocol)||(s=!0)}catch{s=!0}s||r.push({text:n,range:a,activate:(e,t)=>i?i.activate(e,t,a):Se(e,t),hover:(e,t)=>i?.hover?.(e,t,a),leave:(e,t)=>i?.leave?.(e,t,a)})}l=!1,a.hasExtendedAttrs()&&a.extended.urlId?(c=t,s=a.extended.urlId):(c=-1,s=-1)}}t(r)}};xe=d([f(0,T),f(1,D),f(2,ve)],xe);function Se(e,t){if(confirm(`Do you want to navigate to ${t}?
|
|
1
|
+
import{a as e,n as t,r as n,t as r}from"./jsx-runtime-BFALxl05.js";import{t as i}from"./copy-B-kLwqzg.js";import{t as a}from"./utils-61GRB9Cb.js";var o=t(`clipboard-paste`,[[`path`,{d:`M11 14h10`,key:`1w8e9d`}],[`path`,{d:`M16 4h2a2 2 0 0 1 2 2v1.344`,key:`1e62lh`}],[`path`,{d:`m17 18 4-4-4-4`,key:`z2g111`}],[`path`,{d:`M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113`,key:`bjbb7m`}],[`rect`,{x:`8`,y:`2`,width:`8`,height:`4`,rx:`1`,key:`ublpy`}]]),s=e(n(),1),c=Object.defineProperty,l=Object.getOwnPropertyDescriptor,u=(e,t)=>{for(var n in t)c(e,n,{get:t[n],enumerable:!0})},d=(e,t,n,r)=>{for(var i=r>1?void 0:r?l(t,n):t,a=e.length-1,o;a>=0;a--)(o=e[a])&&(i=(r?o(t,n,i):o(i))||i);return r&&i&&c(t,n,i),i},f=(e,t)=>(n,r)=>t(n,r,e),p=`Terminal input`,m={get:()=>p,set:e=>p=e},h=`Too much output to announce, navigate to rows manually to read`,g={get:()=>h,set:e=>h=e};function _(e){return e.replace(/\r?\n/g,`\r`)}function v(e,t){return t?`\x1B[200~`+e+`\x1B[201~`:e}function ee(e,t){e.clipboardData&&e.clipboardData.setData(`text/plain`,t.selectionText),e.preventDefault()}function te(e,t,n,r){e.stopPropagation(),e.clipboardData&&ne(e.clipboardData.getData(`text/plain`),t,n,r)}function ne(e,t,n,r){e=_(e),e=v(e,n.decPrivateModes.bracketedPasteMode&&r.rawOptions.ignoreBracketedPasteMode!==!0),n.triggerDataEvent(e,!0),t.value=``}function re(e,t,n){let r=n.getBoundingClientRect(),i=e.clientX-r.left-10,a=e.clientY-r.top-10;t.style.width=`20px`,t.style.height=`20px`,t.style.left=`${i}px`,t.style.top=`${a}px`,t.style.zIndex=`1000`,t.focus()}function ie(e,t,n,r,i){re(e,t,n),i&&r.rightClickSelect(e),t.value=r.selectionText,t.select()}function y(e){return e>65535?(e-=65536,String.fromCharCode((e>>10)+55296)+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)}function ae(e,t=0,n=e.length){let r=``;for(let i=t;i<n;++i){let t=e[i];t>65535?(t-=65536,r+=String.fromCharCode((t>>10)+55296)+String.fromCharCode(t%1024+56320)):r+=String.fromCharCode(t)}return r}var oe=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){let n=e.length;if(!n)return 0;let r=0,i=0;if(this._interim){let n=e.charCodeAt(i++);56320<=n&&n<=57343?t[r++]=(this._interim-55296)*1024+n-56320+65536:(t[r++]=this._interim,t[r++]=n),this._interim=0}for(let a=i;a<n;++a){let i=e.charCodeAt(a);if(55296<=i&&i<=56319){if(++a>=n)return this._interim=i,r;let o=e.charCodeAt(a);56320<=o&&o<=57343?t[r++]=(i-55296)*1024+o-56320+65536:(t[r++]=i,t[r++]=o);continue}i!==65279&&(t[r++]=i)}return r}},b=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){let n=e.length;if(!n)return 0;let r=0,i,a,o,s,c=0,l=0;if(this.interim[0]){let i=!1,a=this.interim[0];a&=(a&224)==192?31:(a&240)==224?15:7;let o=0,s;for(;(s=this.interim[++o]&63)&&o<4;)a<<=6,a|=s;let c=(this.interim[0]&224)==192?2:(this.interim[0]&240)==224?3:4,u=c-o;for(;l<u;){if(l>=n)return 0;if(s=e[l++],(s&192)!=128){l--,i=!0;break}else this.interim[o++]=s,a<<=6,a|=s&63}i||(c===2?a<128?l--:t[r++]=a:c===3?a<2048||a>=55296&&a<=57343||a===65279||(t[r++]=a):a<65536||a>1114111||(t[r++]=a)),this.interim.fill(0)}let u=n-4,d=l;for(;d<n;){for(;d<u&&!((i=e[d])&128)&&!((a=e[d+1])&128)&&!((o=e[d+2])&128)&&!((s=e[d+3])&128);)t[r++]=i,t[r++]=a,t[r++]=o,t[r++]=s,d+=4;if(i=e[d++],i<128)t[r++]=i;else if((i&224)==192){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(c=(i&31)<<6|a&63,c<128){d--;continue}t[r++]=c}else if((i&240)==224){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,r;if(o=e[d++],(o&192)!=128){d--;continue}if(c=(i&15)<<12|(a&63)<<6|o&63,c<2048||c>=55296&&c<=57343||c===65279)continue;t[r++]=c}else if((i&248)==240){if(d>=n)return this.interim[0]=i,r;if(a=e[d++],(a&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,r;if(o=e[d++],(o&192)!=128){d--;continue}if(d>=n)return this.interim[0]=i,this.interim[1]=a,this.interim[2]=o,r;if(s=e[d++],(s&192)!=128){d--;continue}if(c=(i&7)<<18|(a&63)<<12|(o&63)<<6|s&63,c<65536||c>1114111)continue;t[r++]=c}}return r}},se=``,ce=` `,le=class e{constructor(){this.fg=0,this.bg=0,this.extended=new x}static toColorRGB(e){return[e>>>16&255,e>>>8&255,e&255]}static fromColorRGB(e){return(e[0]&255)<<16|(e[1]&255)<<8|e[2]&255}clone(){let t=new e;return t.fg=this.fg,t.bg=this.bg,t.extended=this.extended.clone(),t}isInverse(){return this.fg&67108864}isBold(){return this.fg&134217728}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:this.fg&268435456}isBlink(){return this.fg&536870912}isInvisible(){return this.fg&1073741824}isItalic(){return this.bg&67108864}isDim(){return this.bg&134217728}isStrikethrough(){return this.fg&2147483648}isProtected(){return this.bg&536870912}isOverline(){return this.bg&1073741824}getFgColorMode(){return this.fg&50331648}getBgColorMode(){return this.bg&50331648}isFgRGB(){return(this.fg&50331648)==50331648}isBgRGB(){return(this.bg&50331648)==50331648}isFgPalette(){return(this.fg&50331648)==16777216||(this.fg&50331648)==33554432}isBgPalette(){return(this.bg&50331648)==16777216||(this.bg&50331648)==33554432}isFgDefault(){return(this.fg&50331648)==0}isBgDefault(){return(this.bg&50331648)==0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(this.fg&50331648){case 16777216:case 33554432:return this.fg&255;case 50331648:return this.fg&16777215;default:return-1}}getBgColor(){switch(this.bg&50331648){case 16777216:case 33554432:return this.bg&255;case 50331648:return this.bg&16777215;default:return-1}}hasExtendedAttrs(){return this.bg&268435456}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(this.bg&268435456&&~this.extended.underlineColor)switch(this.extended.underlineColor&50331648){case 16777216:case 33554432:return this.extended.underlineColor&255;case 50331648:return this.extended.underlineColor&16777215;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return this.bg&268435456&&~this.extended.underlineColor?this.extended.underlineColor&50331648:this.getFgColorMode()}isUnderlineColorRGB(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==50331648:this.isFgRGB()}isUnderlineColorPalette(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==16777216||(this.extended.underlineColor&50331648)==33554432:this.isFgPalette()}isUnderlineColorDefault(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)==0:this.isFgDefault()}getUnderlineStyle(){return this.fg&268435456?this.bg&268435456?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}},x=class e{constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}get ext(){return this._urlId?this._ext&-469762049|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(this._ext&469762048)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return this._ext&67108863}set underlineColor(e){this._ext&=-67108864,this._ext|=e&67108863}get urlId(){return this._urlId}set urlId(e){this._urlId=e}get underlineVariantOffset(){let e=(this._ext&3758096384)>>29;return e<0?e^4294967288:e}set underlineVariantOffset(e){this._ext&=536870911,this._ext|=e<<29&3758096384}clone(){return new e(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}},S=class e extends le{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new x,this.combinedData=``}static fromCharData(t){let n=new e;return n.setFromCharData(t),n}isCombined(){return this.content&2097152}getWidth(){return this.content>>22}getChars(){return this.content&2097152?this.combinedData:this.content&2097151?y(this.content&2097151):``}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):this.content&2097151}setFromCharData(e){this.fg=e[0],this.bg=0;let t=!1;if(e[1].length>2)t=!0;else if(e[1].length===2){let n=e[1].charCodeAt(0);if(55296<=n&&n<=56319){let r=e[1].charCodeAt(1);56320<=r&&r<=57343?this.content=(n-55296)*1024+r-56320+65536|e[2]<<22:t=!0}else t=!0}else this.content=e[1].charCodeAt(0)|e[2]<<22;t&&(this.combinedData=e[1],this.content=2097152|e[2]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}},ue=`di$target`,de=`di$dependencies`,C=new Map;function fe(e){return e[de]||[]}function w(e){if(C.has(e))return C.get(e);let t=function(e,n,r){if(arguments.length!==3)throw Error(`@IServiceName-decorator can only be used to decorate a parameter`);pe(t,e,r)};return t._id=e,C.set(e,t),t}function pe(e,t,n){t[ue]===t?t[de].push({id:e,index:n}):(t[de]=[{id:e,index:n}],t[ue]=t)}var T=w(`BufferService`),me=w(`CoreMouseService`),E=w(`CoreService`),he=w(`CharsetService`),ge=w(`InstantiationService`),_e=w(`LogService`),D=w(`OptionsService`),ve=w(`OscLinkService`),ye=w(`UnicodeService`),be=w(`DecorationService`),xe=class{constructor(e,t,n){this._bufferService=e,this._optionsService=t,this._oscLinkService=n}provideLinks(e,t){let n=this._bufferService.buffer.lines.get(e-1);if(!n){t(void 0);return}let r=[],i=this._optionsService.rawOptions.linkHandler,a=new S,o=n.getTrimmedLength(),s=-1,c=-1,l=!1;for(let t=0;t<o;t++)if(!(c===-1&&!n.hasContent(t))){if(n.loadCell(t,a),a.hasExtendedAttrs()&&a.extended.urlId)if(c===-1){c=t,s=a.extended.urlId;continue}else l=a.extended.urlId!==s;else c!==-1&&(l=!0);if(l||c!==-1&&t===o-1){let n=this._oscLinkService.getLinkData(s)?.uri;if(n){let a={start:{x:c+1,y:e},end:{x:t+(!l&&t===o-1?1:0),y:e}},s=!1;if(!i?.allowNonHttpProtocols)try{let e=new URL(n);[`http:`,`https:`].includes(e.protocol)||(s=!0)}catch{s=!0}s||r.push({text:n,range:a,activate:(e,t)=>i?i.activate(e,t,a):Se(e,t),hover:(e,t)=>i?.hover?.(e,t,a),leave:(e,t)=>i?.leave?.(e,t,a)})}l=!1,a.hasExtendedAttrs()&&a.extended.urlId?(c=t,s=a.extended.urlId):(c=-1,s=-1)}}t(r)}};xe=d([f(0,T),f(1,D),f(2,ve)],xe);function Se(e,t){if(confirm(`Do you want to navigate to ${t}?
|
|
2
2
|
|
|
3
3
|
WARNING: This link could potentially be dangerous`)){let e=window.open();if(e){try{e.opener=null}catch{}e.location.href=t}else console.warn(`Opening link blocked as opener could not be cleared`)}}var Ce=w(`CharSizeService`),we=w(`CoreBrowserService`),Te=w(`MouseService`),Ee=w(`RenderService`),De=w(`SelectionService`),Oe=w(`CharacterJoinerService`),ke=w(`ThemeService`),Ae=w(`LinkProviderService`),je=new class{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(()=>{throw e.stack?Le.isErrorNoTelemetry(e)?new Le(e.message+`
|
|
4
4
|
|
package/dist/web/index.html
CHANGED
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-DasstYgw.js"></script>
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/jsx-runtime-BFALxl05.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/utils-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/button-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/dist-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/dist-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/utils-61GRB9Cb.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/button-CvHWF07y.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-CCBctnax.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-B6sG2GPc.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/x-BxhOxZ5p.js">
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/dialog-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/react-
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/dialog-f3IZM-6v.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/react-gOPBns57.js">
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/api-client-BgVufYKf.js">
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DILaVO6p.css">
|
|
22
22
|
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
23
23
|
<body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
|
|
24
24
|
<div id="root"></div>
|
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let s,e={};const i=(i,l)=>(i=new URL(i+".js",l).href,e[i]||new Promise(e=>{if("document"in self){const s=document.createElement("script");s.src=i,s.onload=e,document.head.appendChild(s)}else s=i,importScripts(i),e()}).then(()=>{let s=e[i];if(!s)throw new Error(`Module ${i} didn’t register its module`);return s}));self.define=(l,
|
|
1
|
+
if(!self.define){let s,e={};const i=(i,l)=>(i=new URL(i+".js",l).href,e[i]||new Promise(e=>{if("document"in self){const s=document.createElement("script");s.src=i,s.onload=e,document.head.appendChild(s)}else s=i,importScripts(i),e()}).then(()=>{let s=e[i];if(!s)throw new Error(`Module ${i} didn’t register its module`);return s}));self.define=(l,n)=>{const r=s||("document"in self?document.currentScript.src:"")||location.href;if(e[r])return;let t={};const u=s=>i(s,r),o={module:{uri:r},exports:t,require:u};e[r]=Promise.all(l.map(s=>o[s]||u(s))).then(s=>(n(...s),t))}}define(["./workbox-3e722498"],function(s){"use strict";self.skipWaiting(),s.clientsClaim(),s.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"index.html",revision:"a901473b3162fa81ec87e095b1092e71"},{url:"icon-512.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"icon-192.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"assets/x-BxhOxZ5p.js",revision:null},{url:"assets/utils-61GRB9Cb.js",revision:null},{url:"assets/trash-2-CjahwKg8.js",revision:null},{url:"assets/terminal-tab-CyjhG4Ao.js",revision:null},{url:"assets/terminal-tab-BrP-ENHg.css",revision:null},{url:"assets/settings-tab-Cn5Ja0_J.js",revision:null},{url:"assets/refresh-cw-DJSjl6Ev.js",revision:null},{url:"assets/react-gOPBns57.js",revision:null},{url:"assets/project-list-C7L3hZct.js",revision:null},{url:"assets/marked.esm-Cv8mjgnt.js",revision:null},{url:"assets/jsx-runtime-BFALxl05.js",revision:null},{url:"assets/index-DasstYgw.js",revision:null},{url:"assets/index-DILaVO6p.css",revision:null},{url:"assets/git-status-panel-BifyO31N.js",revision:null},{url:"assets/git-graph-BiyTIbCz.js",revision:null},{url:"assets/external-link-Dim3NH6h.js",revision:null},{url:"assets/dist-CCBctnax.js",revision:null},{url:"assets/dist-CBiGQxfr.js",revision:null},{url:"assets/dist-B6sG2GPc.js",revision:null},{url:"assets/diff-viewer-8_asmBRZ.js",revision:null},{url:"assets/dialog-f3IZM-6v.js",revision:null},{url:"assets/copy-B-kLwqzg.js",revision:null},{url:"assets/code-editor-BgiyQO-M.js",revision:null},{url:"assets/chat-tab-C4ovA2w4.js",revision:null},{url:"assets/button-CvHWF07y.js",revision:null},{url:"assets/arrow-up-from-line-DjfWTP75.js",revision:null},{url:"assets/api-client-BgVufYKf.js",revision:null},{url:"manifest.webmanifest",revision:"79c8870653c8f419f2e3323085e1f4be"}],{}),s.cleanupOutdatedCaches(),s.registerRoute(new s.NavigationRoute(s.createHandlerBoundToURL("index.html"))),s.registerRoute(/^https?:\/\/.*\/api\//,new s.NetworkOnly,"GET"),s.registerRoute(/^https?:\/\/.*\/ws\//,new s.NetworkOnly,"GET")});
|
package/docs/code-standards.md
CHANGED
|
@@ -572,3 +572,77 @@ refactor/service-layer-cleanup
|
|
|
572
572
|
docs/deployment-guide
|
|
573
573
|
```
|
|
574
574
|
|
|
575
|
+
## CLI Design Patterns
|
|
576
|
+
|
|
577
|
+
### Command Option Handling
|
|
578
|
+
When adding new options to CLI commands (e.g., `ppm start`):
|
|
579
|
+
|
|
580
|
+
**Option Naming:**
|
|
581
|
+
- Use long form: `--foreground`, `--share` (not short-only)
|
|
582
|
+
- Add short form if common: `-f`, `-s` (optional)
|
|
583
|
+
- Keep defaults sensible (e.g., daemon mode is default)
|
|
584
|
+
|
|
585
|
+
**Implementation Pattern:**
|
|
586
|
+
```typescript
|
|
587
|
+
program
|
|
588
|
+
.command("start")
|
|
589
|
+
.option("-p, --port <port>", "Port to listen on")
|
|
590
|
+
.option("-f, --foreground", "Run in foreground")
|
|
591
|
+
.option("-s, --share", "Enable public URL via tunnel")
|
|
592
|
+
.action(async (options) => {
|
|
593
|
+
// options.port, options.foreground, options.share as booleans
|
|
594
|
+
});
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**Server Handling:**
|
|
598
|
+
```typescript
|
|
599
|
+
export async function startServer(options: {
|
|
600
|
+
port?: string;
|
|
601
|
+
foreground?: boolean;
|
|
602
|
+
share?: boolean;
|
|
603
|
+
config?: string;
|
|
604
|
+
}) {
|
|
605
|
+
const isDaemon = !options.foreground; // Explicit: daemon is default
|
|
606
|
+
|
|
607
|
+
if (isDaemon) {
|
|
608
|
+
// Spawn child process
|
|
609
|
+
const child = Bun.spawn(/* ... */);
|
|
610
|
+
// Poll for status.json, show URLs
|
|
611
|
+
} else {
|
|
612
|
+
// Foreground: serve with logs
|
|
613
|
+
const server = Bun.serve(/* ... */);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (options.share) {
|
|
617
|
+
// Start tunnel (works in both daemon + foreground)
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Status File Format
|
|
623
|
+
Daemon process communicates back via JSON file at `~/.ppm/status.json`:
|
|
624
|
+
|
|
625
|
+
```json
|
|
626
|
+
{
|
|
627
|
+
"pid": 12345,
|
|
628
|
+
"port": 8080,
|
|
629
|
+
"host": "0.0.0.0",
|
|
630
|
+
"shareUrl": "https://abc-123.trycloudflare.com"
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
**Backward Compatibility:** Fallback to `~/.ppm/ppm.pid` for legacy support.
|
|
635
|
+
|
|
636
|
+
### Feature Service Loading (Lazy)
|
|
637
|
+
Services that require external dependencies (e.g., cloudflared) should be lazy-imported:
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
if (options.share) {
|
|
641
|
+
// Only download cloudflared if --share was used
|
|
642
|
+
const { ensureCloudflared } = await import("../services/cloudflared.service.ts");
|
|
643
|
+
await ensureCloudflared();
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
This keeps startup fast when features aren't used.
|
|
648
|
+
|
package/docs/codebase-summary.md
CHANGED
|
@@ -10,8 +10,8 @@ ppm/
|
|
|
10
10
|
│ ├── index.ts # CLI entry point (Commander.js program)
|
|
11
11
|
│ ├── cli/
|
|
12
12
|
│ │ ├── commands/ # CLI command implementations (8 files, 907 LOC)
|
|
13
|
-
│ │ │ ├── start.ts # Start server (
|
|
14
|
-
│ │ │ ├── stop.ts # Stop daemon (graceful shutdown)
|
|
13
|
+
│ │ │ ├── start.ts # Start server (background by default, --foreground/-f, --share/-s for tunnel)
|
|
14
|
+
│ │ │ ├── stop.ts # Stop daemon (reads status.json or ppm.pid, graceful shutdown)
|
|
15
15
|
│ │ │ ├── open.ts # Open browser to http://localhost:PORT
|
|
16
16
|
│ │ │ ├── init.ts # Initialize ppm.yaml config (scan git repos)
|
|
17
17
|
│ │ │ ├── projects.ts # Add/remove/list projects
|
|
@@ -42,7 +42,7 @@ ppm/
|
|
|
42
42
|
│ │ ├── claude-agent-sdk.ts # Primary: @anthropic-ai/claude-agent-sdk. Reads config from configService.
|
|
43
43
|
│ │ ├── mock-provider.ts # Test provider (ignores config)
|
|
44
44
|
│ │ └── registry.ts # ProviderRegistry (singleton, router to active provider)
|
|
45
|
-
│ ├── services/ # Business logic (
|
|
45
|
+
│ ├── services/ # Business logic (11 files, 1761 LOC)
|
|
46
46
|
│ │ ├── chat.service.ts # Session lifecycle, message streaming, streaming to clients
|
|
47
47
|
│ │ ├── git.service.ts # Git operations (372 LOC): status, diff, log, graph, branches
|
|
48
48
|
│ │ ├── file.service.ts # File ops (261 LOC): tree, read, write, delete, mkdir, path validation
|
|
@@ -51,7 +51,9 @@ ppm/
|
|
|
51
51
|
│ │ ├── config.service.ts # YAML config loading (91 LOC)
|
|
52
52
|
│ │ ├── slash-items.service.ts # /slash command detection & completion
|
|
53
53
|
│ │ ├── claude-usage.service.ts # Token usage via ccburn library
|
|
54
|
-
│ │
|
|
54
|
+
│ │ ├── git-dirs.service.ts # Cached git directory discovery
|
|
55
|
+
│ │ ├── cloudflared.service.ts # Download cloudflared binary from GitHub (platform-specific)
|
|
56
|
+
│ │ └── tunnel.service.ts # Cloudflare Quick Tunnel lifecycle (spawn, capture URL, cleanup)
|
|
55
57
|
│ ├── types/ # TypeScript interfaces (6 files, 258 LOC)
|
|
56
58
|
│ │ ├── api.ts # ApiResponse envelope, WebSocket message types
|
|
57
59
|
│ │ ├── chat.ts # Session, Message, ChatEvent types
|
|
@@ -142,8 +144,8 @@ ppm/
|
|
|
142
144
|
### CLI Layer (src/cli/)
|
|
143
145
|
- **Responsibility:** Command-line interface for managing PPM
|
|
144
146
|
- **Key Functions:**
|
|
145
|
-
- `start` — Start Hono server
|
|
146
|
-
- `stop` —
|
|
147
|
+
- `start` — Start Hono server (background by default, --foreground/-f for foreground, --share/-s for tunnel)
|
|
148
|
+
- `stop` — Stop daemon (reads status.json first, falls back to ppm.pid)
|
|
147
149
|
- `open` — Launch browser to active server
|
|
148
150
|
- `init` — Scan filesystem for git repos, create ppm.yaml
|
|
149
151
|
- `projects` — Add/remove/list projects in config
|
|
@@ -164,7 +166,7 @@ ppm/
|
|
|
164
166
|
- **Pattern:** Project-scoped routing via ProviderRegistry
|
|
165
167
|
|
|
166
168
|
### Service Layer (src/services/)
|
|
167
|
-
- **Responsibility:** Business logic, data operations
|
|
169
|
+
- **Responsibility:** Business logic, data operations, infrastructure (tunneling)
|
|
168
170
|
- **Services:**
|
|
169
171
|
- **ChatService** — Session lifecycle, message queueing, streaming
|
|
170
172
|
- **GitService** — Git commands via simple-git
|
|
@@ -172,6 +174,8 @@ ppm/
|
|
|
172
174
|
- **ProjectService** — YAML registry management
|
|
173
175
|
- **TerminalService** — PTY lifecycle, shell spawning
|
|
174
176
|
- **ConfigService** — Config file loading
|
|
177
|
+
- **CloudflaredService** — Download/cache cloudflared binary (platform-aware, shows progress)
|
|
178
|
+
- **TunnelService** — Spawn tunnel, extract URL from stderr, cleanup on exit
|
|
175
179
|
- **Pattern:** Singleton services, dependency injection via imports
|
|
176
180
|
|
|
177
181
|
### Provider Layer (src/providers/)
|
package/docs/deployment-guide.md
CHANGED
|
@@ -165,23 +165,84 @@ Server runs in foreground. Press `Ctrl+C` to stop.
|
|
|
165
165
|
|
|
166
166
|
### Daemon Mode (Production)
|
|
167
167
|
|
|
168
|
+
**Daemon is now the default** — `ppm start` runs in background. Use `--foreground/-f` to run with logs visible (for debugging).
|
|
169
|
+
|
|
168
170
|
```bash
|
|
169
|
-
# Start as background daemon
|
|
170
|
-
ppm start
|
|
171
|
+
# Start as background daemon (default)
|
|
172
|
+
ppm start
|
|
171
173
|
|
|
172
|
-
#
|
|
173
|
-
|
|
174
|
+
# Start with public URL via Cloudflare Quick Tunnel (v2+)
|
|
175
|
+
ppm start --share
|
|
174
176
|
|
|
175
|
-
#
|
|
177
|
+
# Start in foreground (debugging, shows logs)
|
|
178
|
+
ppm start --foreground
|
|
179
|
+
|
|
180
|
+
# Server status stored in ~/.ppm/status.json
|
|
181
|
+
cat ~/.ppm/status.json
|
|
182
|
+
|
|
183
|
+
# Check if running
|
|
176
184
|
ps aux | grep ppm
|
|
177
185
|
|
|
178
186
|
# Stop daemon
|
|
179
187
|
ppm stop
|
|
180
188
|
|
|
181
|
-
#
|
|
182
|
-
|
|
189
|
+
# Graceful shutdown: SIGTERM sent, tunnel stopped, cleanup files removed
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Status File Format (v2+):**
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"pid": 12345,
|
|
196
|
+
"port": 8080,
|
|
197
|
+
"host": "0.0.0.0",
|
|
198
|
+
"shareUrl": "https://abc-123.trycloudflare.com"
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Backward Compatibility:** If `~/.ppm/status.json` doesn't exist, `ppm stop` falls back to reading `~/.ppm/ppm.pid`.
|
|
203
|
+
|
|
204
|
+
### Public URL Sharing via Cloudflare Tunnel (v2+)
|
|
205
|
+
|
|
206
|
+
**Feature:** `ppm start --share` creates a temporary public URL for your local PPM instance via Cloudflare Quick Tunnel.
|
|
207
|
+
|
|
208
|
+
**How It Works:**
|
|
209
|
+
1. `ppm start --share` (or `-s`) spawns a background daemon
|
|
210
|
+
2. Parent process downloads cloudflared binary to `~/.ppm/bin/` if missing (shows download progress)
|
|
211
|
+
3. Daemon spawns cloudflared tunnel process
|
|
212
|
+
4. Tunnel URL extracted from stderr (e.g., `https://abc-123.trycloudflare.com`)
|
|
213
|
+
5. URL saved to `~/.ppm/status.json` for easy access
|
|
214
|
+
6. Parent displays: "Share: https://abc-123.trycloudflare.com"
|
|
215
|
+
|
|
216
|
+
**Requirements:**
|
|
217
|
+
- Internet connectivity (tunnel uses Cloudflare's infrastructure)
|
|
218
|
+
- ~15 MB disk space for cloudflared binary (downloaded once, cached)
|
|
219
|
+
|
|
220
|
+
**Security Warning:**
|
|
221
|
+
If `auth.enabled` is false in `~/.ppm/config.yaml`, PPM displays warning:
|
|
222
|
+
```
|
|
223
|
+
⚠ Warning: auth is disabled — your IDE is publicly accessible!
|
|
224
|
+
Enable auth in ~/.ppm/config.yaml or restart without --share.
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Recommended: Always enable auth before using `--share`.
|
|
228
|
+
|
|
229
|
+
**Example:**
|
|
230
|
+
```bash
|
|
231
|
+
# Share with auth enabled
|
|
232
|
+
ppm start --share # Safe: public URL, but auth required
|
|
233
|
+
|
|
234
|
+
# Share without auth (not recommended)
|
|
235
|
+
ppm start --share # Warning: anyone can access your IDE
|
|
236
|
+
|
|
237
|
+
# Disable sharing
|
|
238
|
+
ppm stop # Tunnel process stopped automatically
|
|
183
239
|
```
|
|
184
240
|
|
|
241
|
+
**Cleanup:**
|
|
242
|
+
- `ppm stop` gracefully shuts down the tunnel
|
|
243
|
+
- Cloudflared process killed via SIGTERM
|
|
244
|
+
- No dangling tunnels left behind
|
|
245
|
+
|
|
185
246
|
### Via systemd (Linux)
|
|
186
247
|
|
|
187
248
|
Create `/etc/systemd/system/ppm.service`:
|
|
@@ -438,17 +499,25 @@ ppm chat send "test message" 2>&1
|
|
|
438
499
|
### Server Won't Start
|
|
439
500
|
|
|
440
501
|
```bash
|
|
441
|
-
# Check
|
|
442
|
-
|
|
502
|
+
# Check status (v2+)
|
|
503
|
+
cat ~/.ppm/status.json
|
|
504
|
+
|
|
505
|
+
# Verify server is running
|
|
506
|
+
ps aux | grep ppm
|
|
507
|
+
|
|
508
|
+
# View logs (foreground mode only)
|
|
509
|
+
ppm start --foreground # Shows real-time logs
|
|
443
510
|
|
|
444
511
|
# Verify config is valid YAML
|
|
445
512
|
ppm config get port
|
|
446
513
|
|
|
447
514
|
# Clear cache and restart
|
|
448
515
|
rm -rf ~/.ppm/cache
|
|
449
|
-
ppm start
|
|
516
|
+
ppm start
|
|
450
517
|
```
|
|
451
518
|
|
|
519
|
+
**Note:** Daemon mode doesn't write to a log file by default. Use `ppm start --foreground` to see logs for debugging, or set up systemd with log redirection (see "Via systemd" section above).
|
|
520
|
+
|
|
452
521
|
---
|
|
453
522
|
|
|
454
523
|
## Security Checklist
|
|
@@ -625,7 +694,8 @@ For monitoring integrations (Prometheus, DataDog):
|
|
|
625
694
|
### Getting Help
|
|
626
695
|
|
|
627
696
|
1. **GitHub Issues:** https://github.com/hienlh/ppm/issues
|
|
628
|
-
2. **Logs:**
|
|
697
|
+
2. **Logs:** Run `ppm start --foreground` to see real-time logs; check `~/.ppm/status.json` for daemon status
|
|
629
698
|
3. **Configuration:** Validate `ppm.yaml` syntax
|
|
630
699
|
4. **Dependencies:** Ensure Bun, Git, Node are installed and up-to-date
|
|
700
|
+
5. **Tunnel Issues:** Check `~/.ppm/bin/cloudflared` exists; re-download if corrupted
|
|
631
701
|
|
|
@@ -100,13 +100,66 @@ Built on the **Bun runtime** for performance, PPM enables developers to:
|
|
|
100
100
|
| **Mobile** | iOS Safari, Android Chrome | Responsive design, touch-friendly UI |
|
|
101
101
|
| **Offline** | Basic file browsing, editor | Service worker caching (PWA) |
|
|
102
102
|
|
|
103
|
+
## CLI Commands (v2+)
|
|
104
|
+
|
|
105
|
+
### ppm start
|
|
106
|
+
Start the server in **background daemon mode** (default) or foreground. Supports optional public URL sharing via Cloudflare Quick Tunnel.
|
|
107
|
+
|
|
108
|
+
**Syntax:**
|
|
109
|
+
```bash
|
|
110
|
+
ppm start [options]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Options:**
|
|
114
|
+
- `-p, --port <port>` — Port to listen on (default: from config.yaml)
|
|
115
|
+
- `-f, --foreground` — Run in foreground (blocking, shows logs). Default: background daemon.
|
|
116
|
+
- `-d, --daemon` — Explicit daemon flag (kept for compatibility, no-op since daemon is default)
|
|
117
|
+
- `-s, --share` — Enable public URL sharing via Cloudflare tunnel
|
|
118
|
+
- `-c, --config <path>` — Path to config file (default: ~/.ppm/config.yaml)
|
|
119
|
+
|
|
120
|
+
**Behavior:**
|
|
121
|
+
- **Background (default):** Process exits immediately. Daemon runs with output to null. Status saved to `~/.ppm/status.json`. Parent polls for status (up to 30s).
|
|
122
|
+
- **Foreground:** Blocks with logs displayed. All WebSocket and tunnel features work normally.
|
|
123
|
+
- **--share flag:** Downloads cloudflared binary to `~/.ppm/bin/` if missing (shows progress). Spawns tunnel in separate child. URL extracted from stderr and saved to status.json.
|
|
124
|
+
- **Auth warning:** If `--share` is used without auth enabled, warns user that IDE is publicly accessible.
|
|
125
|
+
|
|
126
|
+
**Example:**
|
|
127
|
+
```bash
|
|
128
|
+
ppm start --share # Daemon + tunnel
|
|
129
|
+
ppm start --foreground # Foreground for debugging
|
|
130
|
+
ppm start -p 3000 -f # Custom port + foreground
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### ppm stop
|
|
134
|
+
Stop the background daemon gracefully.
|
|
135
|
+
|
|
136
|
+
**Syntax:**
|
|
137
|
+
```bash
|
|
138
|
+
ppm stop
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Behavior:**
|
|
142
|
+
- Reads `~/.ppm/status.json` (new format) or falls back to `~/.ppm/ppm.pid` (legacy)
|
|
143
|
+
- Sends SIGTERM to process
|
|
144
|
+
- Cleans up status.json and ppm.pid files
|
|
145
|
+
- Tunnel process (if running) killed via signal handler
|
|
146
|
+
|
|
147
|
+
**Example:**
|
|
148
|
+
```bash
|
|
149
|
+
ppm stop # Stop daemon
|
|
150
|
+
```
|
|
151
|
+
|
|
103
152
|
## Architecture Highlights
|
|
104
153
|
|
|
105
154
|
```
|
|
106
155
|
┌─────────────────────────────────────┐
|
|
107
|
-
│ CLI (Commander.js) │
|
|
156
|
+
│ CLI (Commander.js) │ Start/stop daemon, manage projects
|
|
157
|
+
│ ├─ ppm start [--foreground --share]│
|
|
158
|
+
│ └─ ppm stop │
|
|
108
159
|
├─────────────────────────────────────┤
|
|
109
160
|
│ Hono Server (Bun.serve + WebSocket)│ REST API, WS for terminal/chat
|
|
161
|
+
│ ├─ Tunnel Service (Cloudflare) │ Optional public URL
|
|
162
|
+
│ └─ Daemon Mode (background process)│
|
|
110
163
|
├────────────────────┬────────────────┤
|
|
111
164
|
│ Services Layer │ Providers │ Business logic, AI adapters
|
|
112
165
|
├────────────────────┴────────────────┤
|
|
@@ -137,6 +190,13 @@ Built on the **Bun runtime** for performance, PPM enables developers to:
|
|
|
137
190
|
| Version | Status | Focus | Date |
|
|
138
191
|
|---------|--------|-------|------|
|
|
139
192
|
| **v1** | Complete | Initial prototype (single project, basic chat, terminal) | Feb 2025 |
|
|
140
|
-
| **v2** | In Progress | Multi-project, project-scoped APIs, improved UI/UX | Mar 2025 |
|
|
193
|
+
| **v2** | In Progress | Multi-project, project-scoped APIs, improved UI/UX, daemon mode, --share flag | Mar 2025 |
|
|
141
194
|
| **v3** | Planned | Collaborative editing, plugin architecture | Q2 2025 |
|
|
142
195
|
|
|
196
|
+
### v2 Changes (Mar 2025)
|
|
197
|
+
- **Daemon Mode as Default:** `ppm start` now runs background daemon by default. `--foreground/-f` flag for debugging.
|
|
198
|
+
- **Public URL Sharing:** `ppm start --share` creates Cloudflare Quick Tunnel public URL. Auto-downloads cloudflared binary.
|
|
199
|
+
- **Status File:** `~/.ppm/status.json` (new format) replaces `ppm.pid` with backward compatibility.
|
|
200
|
+
- **Auth Warning:** Warns if `--share` used without auth enabled.
|
|
201
|
+
- **New Services:** `cloudflared.service.ts` (binary download), `tunnel.service.ts` (tunnel lifecycle).
|
|
202
|
+
|
|
@@ -137,6 +137,8 @@ WS /ws/project/:name/terminal/:id → Terminal I/O
|
|
|
137
137
|
| **ProjectService** | Project registry (YAML) | add, remove, get, list |
|
|
138
138
|
| **ConfigService** | Config file management | load, save, getToken |
|
|
139
139
|
| **ProviderRegistry** | AI provider routing | getDefault, send (delegates) |
|
|
140
|
+
| **CloudflaredService** | Download cloudflared binary | ensureCloudflared, getCloudflaredPath |
|
|
141
|
+
| **TunnelService** | Cloudflare Quick Tunnel lifecycle | startTunnel, stopTunnel, getTunnelUrl |
|
|
140
142
|
|
|
141
143
|
**Key Files:** `src/services/*.service.ts`
|
|
142
144
|
|
|
@@ -485,17 +487,31 @@ Linux/macOS Host
|
|
|
485
487
|
└── ~/.ppm/ (optional: session cache, logs)
|
|
486
488
|
```
|
|
487
489
|
|
|
488
|
-
### Daemon Mode (
|
|
490
|
+
### Daemon Mode (Default)
|
|
489
491
|
```
|
|
490
|
-
$ ppm start
|
|
491
|
-
→ Background process (
|
|
492
|
-
→
|
|
493
|
-
→
|
|
492
|
+
$ ppm start
|
|
493
|
+
→ Background process (background by default)
|
|
494
|
+
→ Status saved to ~/.ppm/status.json (with PID, port, host, shareUrl)
|
|
495
|
+
→ Fallback compat: ppm.pid read/written for backward compatibility
|
|
496
|
+
|
|
497
|
+
$ ppm start --foreground
|
|
498
|
+
→ Runs in foreground (debugging, CI/CD)
|
|
499
|
+
→ WebSocket and all features fully functional
|
|
500
|
+
→ Tunnel (--share) works in foreground mode
|
|
501
|
+
|
|
502
|
+
$ ppm start --share
|
|
503
|
+
→ Daemon mode + Cloudflare Quick Tunnel
|
|
504
|
+
→ Downloads cloudflared to ~/.ppm/bin/ (if missing, shows progress)
|
|
505
|
+
→ Spawns tunnel process, extracts public URL from stderr
|
|
506
|
+
→ URL saved to status.json for parent process
|
|
507
|
+
→ Auth warning if auth.enabled is false
|
|
494
508
|
|
|
495
509
|
$ ppm stop
|
|
496
|
-
→ Reads
|
|
497
|
-
→
|
|
498
|
-
→
|
|
510
|
+
→ Reads ~/.ppm/status.json first (new format)
|
|
511
|
+
→ Falls back to ppm.pid (compat)
|
|
512
|
+
→ Sends SIGTERM to daemon
|
|
513
|
+
→ Cleans up status.json and ppm.pid
|
|
514
|
+
→ Graceful shutdown (close WS, cleanup PTY, stop tunnel)
|
|
499
515
|
```
|
|
500
516
|
|
|
501
517
|
### Future: Multi-Machine (Not in v2)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hienlh/ppm",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Personal Project Manager — mobile-first web IDE with AI assistance",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"ppm": "src/index.ts"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"dev": "bun run --hot src/index.ts start",
|
|
11
|
+
"dev": "bun run --hot src/index.ts start -f & bun run vite --config vite.config.ts",
|
|
12
|
+
"dev:server": "bun run --hot src/index.ts start -f",
|
|
12
13
|
"dev:web": "bun run vite --config vite.config.ts",
|
|
13
14
|
"build:web": "bun run vite build --config vite.config.ts",
|
|
14
15
|
"build": "bun run build:web && bun build src/index.ts --compile --outfile dist/ppm",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"@codemirror/lang-python": "^6.2.1",
|
|
44
45
|
"@codemirror/merge": "^6.12.1",
|
|
45
46
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
47
|
+
"@inquirer/prompts": "^8.3.0",
|
|
46
48
|
"@uiw/react-codemirror": "^4.25.8",
|
|
47
49
|
"@xterm/addon-fit": "^0.11.0",
|
|
48
50
|
"@xterm/addon-web-links": "^0.12.0",
|
|
@@ -58,6 +60,7 @@
|
|
|
58
60
|
"lucide-react": "^0.577.0",
|
|
59
61
|
"marked": "^17.0.4",
|
|
60
62
|
"next-themes": "^0.4.6",
|
|
63
|
+
"qrcode-terminal": "^0.12.0",
|
|
61
64
|
"radix-ui": "^1.4.3",
|
|
62
65
|
"react": "^19.2.4",
|
|
63
66
|
"react-dom": "^19.2.4",
|