@hienlh/ppm 0.2.2 → 0.2.4

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 (35) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/web/assets/api-client-B_eCZViO.js +1 -0
  3. package/dist/web/assets/chat-tab-FOn2nq1x.js +6 -0
  4. package/dist/web/assets/{code-editor-BgiyQO-M.js → code-editor-R0uEZQ-h.js} +1 -1
  5. package/dist/web/assets/{diff-viewer-8_asmBRZ.js → diff-viewer-DDQ2Z0sz.js} +1 -1
  6. package/dist/web/assets/{git-graph-BiyTIbCz.js → git-graph-ugBsFNaz.js} +1 -1
  7. package/dist/web/assets/{git-status-panel-BifyO31N.js → git-status-panel-UMKtdAxp.js} +1 -1
  8. package/dist/web/assets/index-CGDMk8DE.css +2 -0
  9. package/dist/web/assets/index-Dmu22zQo.js +12 -0
  10. package/dist/web/assets/project-list-D38uQSpC.js +1 -0
  11. package/dist/web/assets/{settings-tab-Cn5Ja0_J.js → settings-tab-BpyCSbii.js} +1 -1
  12. package/dist/web/index.html +3 -3
  13. package/dist/web/sw.js +1 -1
  14. package/package.json +4 -4
  15. package/src/server/index.ts +32 -2
  16. package/src/server/routes/chat.ts +13 -18
  17. package/src/server/routes/projects.ts +12 -0
  18. package/src/server/routes/static.ts +2 -2
  19. package/src/services/claude-usage.service.ts +93 -74
  20. package/src/services/project.service.ts +43 -0
  21. package/src/types/chat.ts +0 -2
  22. package/src/web/components/chat/chat-tab.tsx +24 -7
  23. package/src/web/components/chat/usage-badge.tsx +23 -23
  24. package/src/web/components/layout/mobile-drawer.tsx +19 -1
  25. package/src/web/components/layout/sidebar.tsx +15 -4
  26. package/src/web/components/projects/project-list.tsx +153 -4
  27. package/src/web/hooks/use-chat.ts +30 -41
  28. package/src/web/hooks/use-usage.ts +65 -0
  29. package/src/web/lib/api-client.ts +9 -0
  30. package/src/web/lib/report-bug.ts +33 -0
  31. package/dist/web/assets/api-client-BgVufYKf.js +0 -1
  32. package/dist/web/assets/chat-tab-C4ovA2w4.js +0 -6
  33. package/dist/web/assets/index-DILaVO6p.css +0 -2
  34. package/dist/web/assets/index-DasstYgw.js +0 -11
  35. package/dist/web/assets/project-list-C7L3hZct.js +0 -1
@@ -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{t as a}from"./trash-2-CjahwKg8.js";import{a as o,i as s,n as c,o as l,r as u,t as d}from"./dialog-f3IZM-6v.js";import{t as f}from"./utils-61GRB9Cb.js";import{t as p}from"./api-client-B_eCZViO.js";import{_ as m,b as h,c as g,h as _,l as v,n as y,v as b}from"./index-Dmu22zQo.js";var x=t(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),S=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`}]]),C=t(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),w=e(n(),1),T=r();function E({value:e,onChange:t,onSelect:n,placeholder:r,autoFocus:i}){let[a,o]=(0,w.useState)([]),[s,c]=(0,w.useState)([]),[l,u]=(0,w.useState)(!1),[d,f]=(0,w.useState)(0),[h,g]=(0,w.useState)(!1),_=(0,w.useRef)(null),v=(0,w.useRef)(!1);(0,w.useEffect)(()=>{v.current||(v.current=!0,g(!0),p.get(`/api/projects/suggest-dirs`).then(e=>{o(e),c(e.slice(0,50)),u(e.length>0)}).catch(()=>o([])).finally(()=>g(!1)))},[]),(0,w.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)),f(0)},[e,a]),(0,w.useEffect)(()=>{let e=_.current;e&&e.children[d]?.scrollIntoView({block:`nearest`})},[d]);let b=(0,w.useCallback)(e=>{t(e.path),n?.(e),u(!1)},[t,n]),x=(0,w.useCallback)(e=>{if(!(!l||s.length===0))switch(e.key){case`ArrowDown`:e.preventDefault(),f(e=>e<s.length-1?e+1:0);break;case`ArrowUp`:e.preventDefault(),f(e=>e>0?e-1:s.length-1);break;case`Tab`:case`Enter`:s[d]&&(e.preventDefault(),b(s[d]));break;case`Escape`:e.preventDefault(),u(!1);break}},[l,s,d,b]);return(0,T.jsxs)(`div`,{className:`relative`,children:[(0,T.jsxs)(`div`,{className:`relative`,children:[(0,T.jsx)(y,{placeholder:r??`/home/user/my-project`,value:e,onChange:e=>t(e.target.value),onKeyDown:x,onFocus:()=>s.length>0&&u(!0),onBlur:()=>setTimeout(()=>u(!1),200),autoFocus:i}),h&&(0,T.jsx)(m,{className:`absolute right-2 top-1/2 -translate-y-1/2 size-4 text-text-subtle animate-spin`})]}),l&&s.length>0&&(0,T.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,T.jsx)(`div`,{ref:_,className:`py-1`,children:s.map((e,t)=>(0,T.jsxs)(`button`,{type:`button`,className:`flex items-center gap-2 w-full px-3 py-1.5 text-left text-sm transition-colors ${t===d?`bg-primary/10 text-primary`:`hover:bg-surface-hover text-text-primary`}`,onMouseEnter:()=>f(t),onMouseDown:t=>{t.preventDefault(),b(e)},children:[(0,T.jsx)(S,{className:`size-4 text-green-500 shrink-0`}),(0,T.jsxs)(`div`,{className:`min-w-0 flex-1 flex items-baseline gap-2`,children:[(0,T.jsx)(`span`,{className:`font-medium`,children:e.name}),(0,T.jsx)(`span`,{className:`text-xs text-text-subtle truncate`,children:e.path})]})]},e.path))})})]})}function D(){let{projects:e,activeProject:t,setActiveProject:n,fetchProjects:r,loading:m,error:S}=g(),D=v(e=>e.openTab),[O,k]=(0,w.useState)(!1),[A,j]=(0,w.useState)(``),[M,N]=(0,w.useState)(``),[P,F]=(0,w.useState)(``),[I,L]=(0,w.useState)(null),[R,z]=(0,w.useState)(``),[B,V]=(0,w.useState)(``),[H,U]=(0,w.useState)(``),[W,G]=(0,w.useState)(null),[K,q]=(0,w.useState)(``);(0,w.useEffect)(()=>{r()},[r]);function J(e){n(e)}function Y(e){n(e),D({type:`terminal`,title:`Terminal - ${e.name}`,metadata:{projectName:e.name},projectId:e.name,closable:!0})}let X=(0,w.useCallback)(async()=>{if(A.trim()){F(``);try{await p.post(`/api/projects`,{path:A.trim(),name:M.trim()||void 0}),await r(),k(!1),j(``),N(``)}catch(e){F(e instanceof Error?e.message:`Failed to add project`)}}},[A,M,r]);function Z(e,t){t.stopPropagation(),L(e),z(e.name),V(e.path),U(``)}let Q=(0,w.useCallback)(async()=>{if(!(!I||!R.trim())){U(``);try{if(await p.patch(`/api/projects/${encodeURIComponent(I.name)}`,{name:R.trim(),path:B.trim()||void 0}),await r(),t?.name===I.name){let e=g.getState().projects.find(e=>e.name===R.trim());e&&n(e)}L(null)}catch(e){U(e instanceof Error?e.message:`Failed to update project`)}}},[I,R,B,r,t,n]);function $(e,t){t.stopPropagation(),G(e),q(``)}let ee=(0,w.useCallback)(async()=>{if(W){q(``);try{if(await p.del(`/api/projects/${encodeURIComponent(W.name)}`),t?.name===W.name){let t=e.filter(e=>e.name!==W.name);t.length>0&&n(t[0])}await r(),G(null)}catch(e){q(e instanceof Error?e.message:`Failed to delete project`)}}},[W,t,e,r,n]);return S?(0,T.jsx)(`div`,{className:`flex items-center justify-center h-full p-4`,children:(0,T.jsx)(`p`,{className:`text-error text-sm`,children:S})}):(0,T.jsxs)(`div`,{className:`h-full p-4 space-y-4 overflow-auto`,children:[(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Projects`}),(0,T.jsxs)(i,{variant:`outline`,size:`sm`,onClick:()=>k(!0),className:`gap-1.5`,children:[(0,T.jsx)(_,{className:`size-4`}),`Add Project`]})]}),m&&(0,T.jsx)(`p`,{className:`text-text-secondary text-sm`,children:`Loading projects...`}),!m&&e.length===0&&(0,T.jsxs)(`div`,{className:`text-center py-8 space-y-2`,children:[(0,T.jsx)(h,{className:`size-10 mx-auto text-text-subtle`}),(0,T.jsx)(`p`,{className:`text-text-secondary text-sm`,children:`No projects registered`}),(0,T.jsxs)(`p`,{className:`text-text-subtle text-xs`,children:[`Click "Add Project" or use `,(0,T.jsx)(`code`,{className:`font-mono bg-surface-elevated px-1 py-0.5 rounded`,children:`ppm projects add <path>`})]})]}),(0,T.jsx)(`div`,{className:`grid gap-3 sm:grid-cols-2 lg:grid-cols-3`,children:e.map(e=>(0,T.jsxs)(`button`,{onClick:()=>J(e),onDoubleClick:()=>Y(e),className:f(`group text-left p-4 rounded-lg border transition-colors relative`,`min-h-[44px]`,t?.name===e.name?`bg-surface border-primary`:`bg-surface border-border hover:border-text-subtle`),children:[(0,T.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity`,children:[(0,T.jsx)(`button`,{onClick:t=>Z(e,t),className:`p-1.5 rounded-md hover:bg-surface-elevated text-text-subtle hover:text-text-primary transition-colors`,title:`Edit project`,children:(0,T.jsx)(C,{className:`size-3.5`})}),(0,T.jsx)(`button`,{onClick:t=>$(e,t),className:`p-1.5 rounded-md hover:bg-error/10 text-text-subtle hover:text-error transition-colors`,title:`Remove project`,children:(0,T.jsx)(a,{className:`size-3.5`})})]}),(0,T.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,T.jsx)(h,{className:`size-5 text-primary shrink-0 mt-0.5`}),(0,T.jsxs)(`div`,{className:`flex-1 min-w-0 space-y-1`,children:[(0,T.jsx)(`p`,{className:`font-medium truncate`,children:e.name}),(0,T.jsx)(`p`,{className:`text-xs text-text-secondary truncate`,children:e.path}),e.branch&&(0,T.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-text-secondary`,children:[(0,T.jsx)(b,{className:`size-3`}),(0,T.jsx)(`span`,{children:e.branch}),e.status&&(0,T.jsx)(x,{className:f(`size-2 fill-current`,e.status===`clean`?`text-success`:`text-warning`)})]})]})]})]},e.name))}),(0,T.jsx)(d,{open:O,onOpenChange:k,children:(0,T.jsxs)(c,{children:[(0,T.jsx)(o,{children:(0,T.jsx)(l,{children:`Add Project`})}),(0,T.jsxs)(`div`,{className:`space-y-3`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Path (required)`}),(0,T.jsx)(E,{value:A,onChange:j,onSelect:e=>{M.trim()||N(e.name)},placeholder:`/home/user/my-project`,autoFocus:!0})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Name (optional)`}),(0,T.jsx)(y,{placeholder:`Auto-detected from folder name`,value:M,onChange:e=>N(e.target.value),onKeyDown:e=>e.key===`Enter`&&X()})]}),P&&(0,T.jsx)(`p`,{className:`text-sm text-error`,children:P})]}),(0,T.jsxs)(s,{children:[(0,T.jsx)(i,{variant:`outline`,onClick:()=>k(!1),children:`Cancel`}),(0,T.jsx)(i,{onClick:X,disabled:!A.trim(),children:`Add`})]})]})}),(0,T.jsx)(d,{open:!!I,onOpenChange:e=>!e&&L(null),children:(0,T.jsxs)(c,{children:[(0,T.jsx)(o,{children:(0,T.jsx)(l,{children:`Edit Project`})}),(0,T.jsxs)(`div`,{className:`space-y-3`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Name`}),(0,T.jsx)(y,{value:R,onChange:e=>z(e.target.value),onKeyDown:e=>e.key===`Enter`&&Q(),autoFocus:!0})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`label`,{className:`text-sm text-text-secondary`,children:`Path`}),(0,T.jsx)(E,{value:B,onChange:V,placeholder:`/home/user/my-project`})]}),H&&(0,T.jsx)(`p`,{className:`text-sm text-error`,children:H})]}),(0,T.jsxs)(s,{children:[(0,T.jsx)(i,{variant:`outline`,onClick:()=>L(null),children:`Cancel`}),(0,T.jsx)(i,{onClick:Q,disabled:!R.trim(),children:`Save`})]})]})}),(0,T.jsx)(d,{open:!!W,onOpenChange:e=>!e&&G(null),children:(0,T.jsxs)(c,{children:[(0,T.jsxs)(o,{children:[(0,T.jsx)(l,{children:`Remove Project`}),(0,T.jsxs)(u,{children:[`Remove `,(0,T.jsx)(`strong`,{children:W?.name}),` from PPM? This only unregisters it — project files on disk are not affected.`]})]}),K&&(0,T.jsx)(`p`,{className:`text-sm text-error`,children:K}),(0,T.jsxs)(s,{children:[(0,T.jsx)(i,{variant:`outline`,onClick:()=>G(null),children:`Cancel`}),(0,T.jsx)(i,{variant:`destructive`,onClick:ee,children:`Remove`})]})]})})]})}export{D as ProjectList};
@@ -1 +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
+ 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-B_eCZViO.js";import{D as A,O as j,d as M,f as N,n as P,t as F}from"./index-Dmu22zQo.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};
@@ -8,7 +8,7 @@
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-DasstYgw.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-Dmu22zQo.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-BFALxl05.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/utils-61GRB9Cb.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/button-CvHWF07y.js">
@@ -17,8 +17,8 @@
17
17
  <link rel="modulepreload" crossorigin href="/assets/x-BxhOxZ5p.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/dialog-f3IZM-6v.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/react-gOPBns57.js">
20
- <link rel="modulepreload" crossorigin href="/assets/api-client-BgVufYKf.js">
21
- <link rel="stylesheet" crossorigin href="/assets/index-DILaVO6p.css">
20
+ <link rel="modulepreload" crossorigin href="/assets/api-client-B_eCZViO.js">
21
+ <link rel="stylesheet" crossorigin href="/assets/index-CGDMk8DE.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,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")});
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:"305a75eade4e51359ee34fc3205b13e7"},{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-BpyCSbii.js",revision:null},{url:"assets/refresh-cw-DJSjl6Ev.js",revision:null},{url:"assets/react-gOPBns57.js",revision:null},{url:"assets/project-list-D38uQSpC.js",revision:null},{url:"assets/marked.esm-Cv8mjgnt.js",revision:null},{url:"assets/jsx-runtime-BFALxl05.js",revision:null},{url:"assets/index-Dmu22zQo.js",revision:null},{url:"assets/index-CGDMk8DE.css",revision:null},{url:"assets/git-status-panel-UMKtdAxp.js",revision:null},{url:"assets/git-graph-ugBsFNaz.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-DDQ2Z0sz.js",revision:null},{url:"assets/dialog-f3IZM-6v.js",revision:null},{url:"assets/copy-B-kLwqzg.js",revision:null},{url:"assets/code-editor-R0uEZQ-h.js",revision:null},{url:"assets/chat-tab-FOn2nq1x.js",revision:null},{url:"assets/button-CvHWF07y.js",revision:null},{url:"assets/arrow-up-from-line-DjfWTP75.js",revision:null},{url:"assets/api-client-B_eCZViO.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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,7 @@
8
8
  "ppm": "src/index.ts"
9
9
  },
10
10
  "scripts": {
11
- "dev": "bun run --hot src/index.ts start -f & bun run vite --config vite.config.ts",
11
+ "dev": "bun run --hot src/index.ts start & bun run vite --config vite.config.ts & wait",
12
12
  "dev:server": "bun run --hot src/index.ts start -f",
13
13
  "dev:web": "bun run vite --config vite.config.ts",
14
14
  "build:web": "bun run vite build --config vite.config.ts",
@@ -16,7 +16,7 @@
16
16
  "start": "bun run src/index.ts start",
17
17
  "typecheck": "bunx tsc --noEmit",
18
18
  "prepublishOnly": "bun run build:web",
19
- "postinstall": "node node_modules/ccburn/scripts/postinstall.js"
19
+ "postinstall": "echo 'postinstall done'"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@tailwindcss/vite": "^4.2.1",
@@ -49,7 +49,7 @@
49
49
  "@xterm/addon-fit": "^0.11.0",
50
50
  "@xterm/addon-web-links": "^0.12.0",
51
51
  "@xterm/xterm": "^6.0.0",
52
- "ccburn": "^0.6.0",
52
+
53
53
  "class-variance-authority": "^0.7.1",
54
54
  "clsx": "^2.1.1",
55
55
  "codemirror": "^6.0.2",
@@ -118,6 +118,24 @@ export async function startServer(options: {
118
118
  // Setup log file (both foreground and daemon modes)
119
119
  await setupLogFile();
120
120
 
121
+ // Check if port is already in use before starting
122
+ const portInUse = await new Promise<boolean>((resolve) => {
123
+ const net = require("node:net") as typeof import("node:net");
124
+ const tester = net.createServer()
125
+ .once("error", (err: NodeJS.ErrnoException) => {
126
+ resolve(err.code === "EADDRINUSE");
127
+ })
128
+ .once("listening", () => {
129
+ tester.close(() => resolve(false));
130
+ })
131
+ .listen(port, host);
132
+ });
133
+ if (portInUse) {
134
+ console.error(`\n ✗ Port ${port} is already in use.`);
135
+ console.error(` Run 'ppm stop' first or use a different port with --port.\n`);
136
+ process.exit(1);
137
+ }
138
+
121
139
  const isDaemon = !options.foreground;
122
140
 
123
141
  if (isDaemon) {
@@ -254,14 +272,20 @@ export async function startServer(options: {
254
272
  idleTimeout: 960,
255
273
  sendPong: true,
256
274
  open(ws: any) {
257
- if (ws.data?.type === "chat") chatWebSocket.open(ws);
275
+ if (ws.data?.type === "health") {
276
+ ws.send(JSON.stringify({ type: "health", status: "ok" }));
277
+ } else if (ws.data?.type === "chat") chatWebSocket.open(ws);
258
278
  else terminalWebSocket.open(ws);
259
279
  },
260
280
  message(ws: any, msg: any) {
261
- if (ws.data?.type === "chat") chatWebSocket.message(ws, msg);
281
+ if (ws.data?.type === "health") {
282
+ // Respond to ping with pong
283
+ ws.send(JSON.stringify({ type: "health", status: "ok" }));
284
+ } else if (ws.data?.type === "chat") chatWebSocket.message(ws, msg);
262
285
  else terminalWebSocket.message(ws, msg);
263
286
  },
264
287
  close(ws: any) {
288
+ if (ws.data?.type === "health") return;
265
289
  if (ws.data?.type === "chat") chatWebSocket.close(ws);
266
290
  else terminalWebSocket.close(ws);
267
291
  },
@@ -324,6 +348,12 @@ if (process.argv.includes("__serve__")) {
324
348
  fetch(req, server) {
325
349
  const url = new URL(req.url);
326
350
 
351
+ if (url.pathname === "/ws/health") {
352
+ const upgraded = server.upgrade(req, { data: { type: "health" } });
353
+ if (upgraded) return undefined;
354
+ return new Response("WebSocket upgrade failed", { status: 400 });
355
+ }
356
+
327
357
  if (url.pathname.startsWith("/ws/project/")) {
328
358
  const parts = url.pathname.split("/");
329
359
  const projectName = parts[3] ?? "";
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
5
5
  import { chatService } from "../../services/chat.service.ts";
6
6
  import { providerRegistry } from "../../providers/registry.ts";
7
7
  import { listSlashItems } from "../../services/slash-items.service.ts";
8
- import { fetchClaudeUsage } from "../../services/claude-usage.service.ts";
8
+ import { waitForFreshUsage } from "../../services/claude-usage.service.ts";
9
9
  import { ok, err } from "../../types/api.ts";
10
10
 
11
11
  type Env = { Variables: { projectPath: string; projectName: string } };
@@ -23,24 +23,19 @@ chatRoutes.get("/slash-items", (c) => {
23
23
  }
24
24
  });
25
25
 
26
- /** GET /chat/usage — get current usage/rate-limit info via ccburn */
26
+ /** GET /chat/usage — await fresh data from ccburn (async, non-blocking to event loop) */
27
27
  chatRoutes.get("/usage", async (c) => {
28
- try {
29
- const usage = await fetchClaudeUsage();
30
- return c.json(ok({
31
- fiveHour: usage.session?.utilization,
32
- sevenDay: usage.weekly?.utilization,
33
- fiveHourResetsAt: usage.session?.resetsAt,
34
- sevenDayResetsAt: usage.weekly?.resetsAt,
35
- // Extra detail for popup
36
- session: usage.session,
37
- weekly: usage.weekly,
38
- weeklyOpus: usage.weeklyOpus,
39
- weeklySonnet: usage.weeklySonnet,
40
- }));
41
- } catch (e) {
42
- return c.json(err((e as Error).message), 500);
43
- }
28
+ const usage = await waitForFreshUsage();
29
+ return c.json(ok({
30
+ fiveHour: usage.session?.utilization,
31
+ sevenDay: usage.weekly?.utilization,
32
+ fiveHourResetsAt: usage.session?.resetsAt,
33
+ sevenDayResetsAt: usage.weekly?.resetsAt,
34
+ session: usage.session,
35
+ weekly: usage.weekly,
36
+ weeklyOpus: usage.weeklyOpus,
37
+ weeklySonnet: usage.weeklySonnet,
38
+ }));
44
39
  });
45
40
 
46
41
  /** GET /chat/providers — list available AI providers */
@@ -45,6 +45,18 @@ projectRoutes.get("/suggest-dirs", (c) => {
45
45
  }
46
46
  });
47
47
 
48
+ /** PATCH /api/projects/:name — update a project's name/path */
49
+ projectRoutes.patch("/:name", async (c) => {
50
+ try {
51
+ const name = c.req.param("name");
52
+ const body = await c.req.json<{ name?: string; path?: string }>();
53
+ const updated = projectService.update(name, body);
54
+ return c.json(ok(updated));
55
+ } catch (e) {
56
+ return c.json(err((e as Error).message), 400);
57
+ }
58
+ });
59
+
48
60
  /** DELETE /api/projects/:name — remove a project by name */
49
61
  projectRoutes.delete("/:name", (c) => {
50
62
  try {
@@ -17,10 +17,10 @@ staticRoutes.use(
17
17
  );
18
18
 
19
19
  /** SPA fallback — serve index.html for all unmatched routes */
20
- staticRoutes.get("*", (c) => {
20
+ staticRoutes.get("*", async (c) => {
21
21
  const indexPath = resolve(DIST_DIR, "index.html");
22
22
  if (existsSync(indexPath)) {
23
- return c.html(Bun.file(indexPath).text());
23
+ return c.html(await Bun.file(indexPath).text());
24
24
  }
25
25
  return c.text("Frontend not built. Run: bun run build:web", 404);
26
26
  });
@@ -1,15 +1,13 @@
1
- import { execFileSync } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { resolve, dirname } from "node:path";
1
+ import { homedir } from "node:os";
2
+ import { resolve } from "node:path";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
 
5
5
  export interface LimitBucket {
6
6
  utilization: number;
7
- budgetPace: number;
8
7
  resetsAt: string;
9
8
  resetsInMinutes: number | null;
10
9
  resetsInHours: number | null;
11
10
  windowHours: number;
12
- status: string;
13
11
  }
14
12
 
15
13
  export interface ClaudeUsage {
@@ -20,94 +18,115 @@ export interface ClaudeUsage {
20
18
  weeklySonnet?: LimitBucket;
21
19
  }
22
20
 
23
- /** Cache to avoid spawning ccburn too often */
21
+ const API_URL = "https://api.anthropic.com/api/oauth/usage";
22
+ const API_BETA = "oauth-2025-04-20";
23
+ const USER_AGENT = "claude-code/1.0";
24
+ const CACHE_TTL = 30_000; // 30s
25
+ const FETCH_TIMEOUT = 10_000; // 10s
26
+
27
+ /** Cached data + timestamp */
24
28
  let cache: { data: ClaudeUsage; timestamp: number } | null = null;
25
- const CACHE_TTL = 30_000; // 30 seconds
26
29
 
27
- /** Cached resolved path */
28
- let ccburnBin: string | undefined;
30
+ /** Cached OAuth token (read once from Keychain/file) */
31
+ let tokenCache: { token: string; timestamp: number } | null = null;
32
+ const TOKEN_TTL = 300_000; // re-read token every 5min
29
33
 
30
34
  /**
31
- * Resolve ccburn binary. Checks:
32
- * 1. node_modules/.bin/ccburn (relative to cwd — works for dev & prod)
33
- * 2. Sibling to compiled binary (dist/node_modules/.bin/ccburn)
34
- * 3. import.meta.dir based resolution
35
+ * Read OAuth access token from macOS Keychain, fallback to credentials file.
35
36
  */
36
- function getCcburnPath(): string {
37
- if (ccburnBin) return ccburnBin;
38
- const candidates = [
39
- resolve(process.cwd(), "node_modules/.bin/ccburn"),
40
- resolve(dirname(process.argv[1] ?? ""), "../node_modules/.bin/ccburn"),
41
- resolve(dirname(process.argv[1] ?? ""), "node_modules/.bin/ccburn"),
42
- ];
43
- for (const p of candidates) {
44
- if (existsSync(p)) {
45
- ccburnBin = p;
46
- return p;
37
+ function getAccessToken(): string {
38
+ if (tokenCache && Date.now() - tokenCache.timestamp < TOKEN_TTL) {
39
+ return tokenCache.token;
40
+ }
41
+
42
+ let creds: Record<string, any> | null = null;
43
+
44
+ // macOS Keychain
45
+ if (process.platform === "darwin") {
46
+ try {
47
+ const proc = Bun.spawnSync(["security", "find-generic-password", "-s", "Claude Code-credentials", "-w"]);
48
+ if (proc.exitCode === 0) {
49
+ creds = JSON.parse(proc.stdout.toString().trim());
50
+ }
51
+ } catch { /* fallback to file */ }
52
+ }
53
+
54
+ // Fallback: ~/.claude/.credentials.json
55
+ if (!creds) {
56
+ const credPath = resolve(homedir(), ".claude", ".credentials.json");
57
+ if (existsSync(credPath)) {
58
+ creds = JSON.parse(readFileSync(credPath, "utf-8"));
47
59
  }
48
60
  }
49
- throw new Error("ccburn not found — run: bun install");
61
+
62
+ const token = creds?.claudeAiOauth?.accessToken;
63
+ if (!token) throw new Error("No Claude OAuth token found");
64
+
65
+ tokenCache = { token, timestamp: Date.now() };
66
+ return token;
50
67
  }
51
68
 
52
69
  /**
53
- * Fetch current usage/rate-limit info via ccburn (bundled dependency).
54
- * ccburn handles credential retrieval (Keychain on macOS, etc.)
55
- * and calls Anthropic's internal usage endpoint.
70
+ * Fetch usage from Anthropic OAuth API — native async, zero process spawn.
56
71
  */
57
- export async function fetchClaudeUsage(): Promise<ClaudeUsage> {
72
+ async function fetchUsageFromApi(): Promise<ClaudeUsage> {
73
+ const token = getAccessToken();
74
+ const res = await fetch(API_URL, {
75
+ headers: {
76
+ Accept: "application/json",
77
+ Authorization: `Bearer ${token}`,
78
+ "anthropic-beta": API_BETA,
79
+ "User-Agent": USER_AGENT,
80
+ },
81
+ signal: AbortSignal.timeout(FETCH_TIMEOUT),
82
+ });
83
+
84
+ if (!res.ok) {
85
+ throw new Error(`Usage API returned ${res.status}`);
86
+ }
87
+
88
+ const raw = (await res.json()) as Record<string, any>;
89
+ const now = new Date().toISOString();
90
+ const data: ClaudeUsage = { timestamp: now };
91
+
92
+ if (raw.five_hour) data.session = parseApiBucket(raw.five_hour, 5);
93
+ if (raw.seven_day) data.weekly = parseApiBucket(raw.seven_day, 168);
94
+ if (raw.seven_day_opus) data.weeklyOpus = parseApiBucket(raw.seven_day_opus, 168);
95
+ if (raw.seven_day_sonnet) data.weeklySonnet = parseApiBucket(raw.seven_day_sonnet, 168);
96
+
97
+ return data;
98
+ }
99
+
100
+ /** Parse an API bucket (utilization is 0-100 from API, normalize to 0-1) */
101
+ function parseApiBucket(raw: Record<string, any>, windowHours: number): LimitBucket {
102
+ const utilization = (raw.utilization ?? 0) / 100;
103
+ const resetsAt = raw.resets_at ?? "";
104
+ const diff = resetsAt ? new Date(resetsAt).getTime() - Date.now() : 0;
105
+ const totalMins = diff > 0 ? Math.ceil(diff / 60_000) : 0;
106
+
107
+ return {
108
+ utilization,
109
+ resetsAt,
110
+ resetsInMinutes: windowHours <= 5 ? totalMins : null,
111
+ resetsInHours: windowHours > 5 ? Math.round((totalMins / 60) * 100) / 100 : null,
112
+ windowHours,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Get cached usage or fetch fresh data.
118
+ * Fully async, never blocks event loop — just a native fetch().
119
+ */
120
+ export async function waitForFreshUsage(): Promise<ClaudeUsage> {
58
121
  if (cache && Date.now() - cache.timestamp < CACHE_TTL) {
59
122
  return cache.data;
60
123
  }
61
124
 
62
125
  try {
63
- const bin = getCcburnPath();
64
- const raw = execFileSync(bin, ["--json"], {
65
- encoding: "utf-8",
66
- timeout: 10_000,
67
- stdio: ["pipe", "pipe", "pipe"],
68
- }).trim();
69
-
70
- // Extract JSON (skip any warnings before it)
71
- const jsonStart = raw.indexOf("{");
72
- const jsonStr = jsonStart > 0 ? raw.slice(jsonStart) : raw;
73
- if (!jsonStr) return cache?.data ?? {};
74
-
75
- const json = JSON.parse(jsonStr) as Record<string, unknown>;
76
- const limits = json.limits as Record<string, unknown> | undefined;
77
- if (!limits) return cache?.data ?? {};
78
-
79
- const data: ClaudeUsage = {
80
- timestamp: json.timestamp as string | undefined,
81
- };
82
-
83
- if (limits.session && typeof limits.session === "object") {
84
- data.session = parseBucket(limits.session as Record<string, unknown>);
85
- }
86
- if (limits.weekly && typeof limits.weekly === "object") {
87
- data.weekly = parseBucket(limits.weekly as Record<string, unknown>);
88
- }
89
- if (limits.weekly_opus && typeof limits.weekly_opus === "object") {
90
- data.weeklyOpus = parseBucket(limits.weekly_opus as Record<string, unknown>);
91
- }
92
- if (limits.weekly_sonnet && typeof limits.weekly_sonnet === "object") {
93
- data.weeklySonnet = parseBucket(limits.weekly_sonnet as Record<string, unknown>);
94
- }
95
-
126
+ const data = await fetchUsageFromApi();
96
127
  cache = { data, timestamp: Date.now() };
97
128
  return data;
98
129
  } catch {
99
130
  return cache?.data ?? {};
100
131
  }
101
132
  }
102
-
103
- function parseBucket(raw: Record<string, unknown>): LimitBucket {
104
- return {
105
- utilization: (raw.utilization as number) ?? 0,
106
- budgetPace: (raw.budget_pace as number) ?? 0,
107
- resetsAt: (raw.resets_at as string) ?? "",
108
- resetsInMinutes: (raw.resets_in_minutes as number) ?? null,
109
- resetsInHours: (raw.resets_in_hours as number) ?? null,
110
- windowHours: (raw.window_hours as number) ?? 0,
111
- status: (raw.status as string) ?? "",
112
- };
113
- }
@@ -40,6 +40,49 @@ class ProjectService {
40
40
  return entry;
41
41
  }
42
42
 
43
+ /** Update a project's name and/or path */
44
+ update(
45
+ currentName: string,
46
+ updates: { name?: string; path?: string },
47
+ ): ProjectConfig {
48
+ const projects = configService.get("projects");
49
+ const idx = projects.findIndex((p) => p.name === currentName);
50
+ if (idx === -1) {
51
+ throw new Error(`Project not found: ${currentName}`);
52
+ }
53
+
54
+ const current = projects[idx]!;
55
+ const newName = updates.name?.trim() || current.name;
56
+ const newPath = updates.path ? resolve(updates.path) : current.path;
57
+
58
+ // Validate new path exists
59
+ if (updates.path && !existsSync(newPath)) {
60
+ throw new Error(`Path does not exist: ${newPath}`);
61
+ }
62
+
63
+ // Check name uniqueness (skip self)
64
+ if (
65
+ newName !== currentName &&
66
+ projects.some((p) => p.name === newName)
67
+ ) {
68
+ throw new Error(`Project "${newName}" already exists`);
69
+ }
70
+
71
+ // Check path uniqueness (skip self)
72
+ if (
73
+ newPath !== current.path &&
74
+ projects.some((p, i) => i !== idx && resolve(p.path) === newPath)
75
+ ) {
76
+ throw new Error(`Path "${newPath}" already registered`);
77
+ }
78
+
79
+ const updated: ProjectConfig = { path: newPath, name: newName };
80
+ projects[idx] = updated;
81
+ configService.set("projects", projects);
82
+ configService.save();
83
+ return updated;
84
+ }
85
+
43
86
  /** Remove a project by name or path */
44
87
  remove(nameOrPath: string): void {
45
88
  const projects = configService.get("projects");
package/src/types/chat.ts CHANGED
@@ -41,12 +41,10 @@ export interface SessionInfo {
41
41
 
42
42
  export interface LimitBucket {
43
43
  utilization: number;
44
- budgetPace: number;
45
44
  resetsAt: string;
46
45
  resetsInMinutes: number | null;
47
46
  resetsInHours: number | null;
48
47
  windowHours: number;
49
- status: string;
50
48
  }
51
49
 
52
50
  export interface UsageInfo {