@hienlh/ppm 0.1.0 → 0.1.2

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 (76) hide show
  1. package/dist/web/assets/api-client-Bnf9LAt4.js +1 -0
  2. package/dist/web/assets/arrow-up-from-line-BXL5dtbG.js +1 -0
  3. package/dist/web/assets/button-BxijdhtM.js +1 -0
  4. package/dist/web/assets/chat-tab-BZopEuub.js +61 -0
  5. package/dist/web/assets/code-editor-hbllHzj7.js +2 -0
  6. package/dist/web/assets/createLucideIcon-Dy1wlrF7.js +1 -0
  7. package/dist/web/assets/dialog-RczsXsmw.js +45 -0
  8. package/dist/web/assets/diff-viewer-D6ixPlNB.js +4 -0
  9. package/dist/web/assets/dist-CSp7ir0r.js +46 -0
  10. package/dist/web/assets/external-link-WSiY-639.js +1 -0
  11. package/dist/web/assets/git-graph-DXMB_DoT.js +1 -0
  12. package/dist/web/assets/git-status-panel-D8ZUQrRF.js +1 -0
  13. package/dist/web/assets/index-DGSLw2GE.js +10 -0
  14. package/dist/web/assets/index-DYd_2slk.css +2 -0
  15. package/dist/web/assets/jsx-runtime-BnxRlLMJ.js +1 -0
  16. package/dist/web/assets/project-list-DWVXEimw.js +1 -0
  17. package/dist/web/assets/react-Uzd0zARU.js +1 -0
  18. package/dist/web/assets/refresh-cw-DtopuYJf.js +1 -0
  19. package/dist/web/assets/settings-tab-DJRzIAuP.js +1 -0
  20. package/dist/web/assets/terminal-tab-BrP-ENHg.css +1 -0
  21. package/dist/web/assets/terminal-tab-CbwaI-oq.js +36 -0
  22. package/dist/web/assets/trash-2-CHLebaNh.js +1 -0
  23. package/dist/web/assets/utils-Cgi2TYRi.js +1 -0
  24. package/dist/web/assets/x-BISR7bpK.js +1 -0
  25. package/dist/web/icon-192.svg +5 -0
  26. package/dist/web/icon-512.svg +5 -0
  27. package/dist/web/index.html +25 -0
  28. package/dist/web/manifest.webmanifest +1 -0
  29. package/dist/web/registerSW.js +1 -0
  30. package/dist/web/sw.js +1 -0
  31. package/dist/web/workbox-3e722498.js +1 -0
  32. package/package.json +2 -1
  33. package/src/server/index.ts +17 -3
  34. package/.claude/agent-memory/tester/MEMORY.md +0 -3
  35. package/.claude/agent-memory/tester/project-ppm-test-conventions.md +0 -32
  36. package/.github/workflows/release.yml +0 -46
  37. package/plans/260314-2009-ppm-implementation/phase-01-project-skeleton.md +0 -81
  38. package/plans/260314-2009-ppm-implementation/phase-02-backend-core.md +0 -148
  39. package/plans/260314-2009-ppm-implementation/phase-03-frontend-shell.md +0 -256
  40. package/plans/260314-2009-ppm-implementation/phase-04-file-explorer-editor.md +0 -120
  41. package/plans/260314-2009-ppm-implementation/phase-05-web-terminal.md +0 -174
  42. package/plans/260314-2009-ppm-implementation/phase-06-git-integration.md +0 -244
  43. package/plans/260314-2009-ppm-implementation/phase-07-ai-chat.md +0 -242
  44. package/plans/260314-2009-ppm-implementation/phase-08-cli-commands.md +0 -143
  45. package/plans/260314-2009-ppm-implementation/phase-09-pwa-build-deploy.md +0 -209
  46. package/plans/260314-2009-ppm-implementation/phase-10-testing.md +0 -311
  47. package/plans/260314-2009-ppm-implementation/plan.md +0 -202
  48. package/plans/260315-0356-project-scoped-api-refactor/phase-01-backend-project-router.md +0 -145
  49. package/plans/260315-0356-project-scoped-api-refactor/phase-02-frontend-api-migration.md +0 -107
  50. package/plans/260315-0356-project-scoped-api-refactor/phase-03-per-project-tabs.md +0 -100
  51. package/plans/260315-0356-project-scoped-api-refactor/phase-04-websocket-migration.md +0 -66
  52. package/plans/260315-0356-project-scoped-api-refactor/plan.md +0 -87
  53. package/plans/reports/brainstorm-260314-1938-final-techstack.md +0 -342
  54. package/plans/reports/docs-manager-260315-1314-documentation-creation.md +0 -386
  55. package/plans/reports/fullstack-developer-260314-2252-phase-02-backend-core.md +0 -57
  56. package/plans/reports/fullstack-developer-260314-2253-phase-03-frontend-shell.md +0 -70
  57. package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-api-terminal-ws.md +0 -49
  58. package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-explorer-editor-terminal.md +0 -52
  59. package/plans/reports/fullstack-developer-260314-2307-ai-chat-phase7.md +0 -58
  60. package/plans/reports/fullstack-developer-260314-2307-phase-06-git-integration.md +0 -33
  61. package/plans/reports/research-260314-1911-ppm-tech-stack.md +0 -318
  62. package/plans/reports/research-260314-1930-claude-code-integration.md +0 -293
  63. package/plans/reports/researcher-260314-2232-node-pty-bun-crash-analysis.md +0 -305
  64. package/plans/reports/researcher-260314-2232-ui-style.md +0 -942
  65. package/plans/reports/researcher-260315-0300-opcode-claude-interaction.md +0 -745
  66. package/plans/reports/researcher-260315-0303-opcode-deep-analysis.md +0 -742
  67. package/plans/reports/researcher-260315-0305-claude-agent-sdk-github-research.md +0 -423
  68. package/plans/reports/tester-260314-2053-initial-test-suite.md +0 -81
  69. package/repomix-output.xml +0 -23745
  70. package/tests/integration/api/chat-routes.test.ts +0 -95
  71. package/tests/integration/claude-agent-sdk-integration.test.ts +0 -228
  72. package/tests/integration/ws/chat-websocket.test.ts +0 -312
  73. package/tests/test-setup.ts +0 -5
  74. package/tests/unit/providers/claude-agent-sdk.test.ts +0 -339
  75. package/tests/unit/providers/mock-provider.test.ts +0 -143
  76. package/tests/unit/services/chat-service.test.ts +0 -100
@@ -0,0 +1 @@
1
+ import{t as e}from"./createLucideIcon-Dy1wlrF7.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t};
@@ -0,0 +1 @@
1
+ function e(t){var n,r,i=``;if(typeof t==`string`||typeof t==`number`)i+=t;else if(typeof t==`object`)if(Array.isArray(t)){var a=t.length;for(n=0;n<a;n++)t[n]&&(r=e(t[n]))&&(i&&(i+=` `),i+=r)}else for(r in t)t[r]&&(i&&(i+=` `),i+=r);return i}function t(){for(var t,n,r=0,i=``,a=arguments.length;r<a;r++)(t=arguments[r])&&(n=e(t))&&(i&&(i+=` `),i+=n);return i}var n=(e,t)=>{let n=Array(e.length+t.length);for(let t=0;t<e.length;t++)n[t]=e[t];for(let r=0;r<t.length;r++)n[e.length+r]=t[r];return n},r=(e,t)=>({classGroupId:e,validator:t}),i=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),a=`-`,o=[],s=`arbitrary..`,c=e=>{let t=ee(e),{conflictingClassGroups:r,conflictingClassGroupModifiers:i}=e;return{getClassGroupId:e=>{if(e.startsWith(`[`)&&e.endsWith(`]`))return u(e);let n=e.split(a);return l(n,n[0]===``&&n.length>1?1:0,t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=i[e],a=r[e];return t?a?n(a,t):t:a||o}return r[e]||o}}},l=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;let r=e[t],i=n.nextPart.get(r);if(i){let n=l(e,t+1,i);if(n)return n}let o=n.validators;if(o===null)return;let s=t===0?e.join(a):e.slice(t).join(a),c=o.length;for(let e=0;e<c;e++){let t=o[e];if(t.validator(s))return t.classGroupId}},u=e=>e.slice(1,-1).indexOf(`:`)===-1?void 0:(()=>{let t=e.slice(1,-1),n=t.indexOf(`:`),r=t.slice(0,n);return r?s+r:void 0})(),ee=e=>{let{theme:t,classGroups:n}=e;return d(n,t)},d=(e,t)=>{let n=i();for(let r in e){let i=e[r];f(i,n,r,t)}return n},f=(e,t,n,r)=>{let i=e.length;for(let a=0;a<i;a++){let i=e[a];p(i,t,n,r)}},p=(e,t,n,r)=>{if(typeof e==`string`){m(e,t,n);return}if(typeof e==`function`){h(e,t,n,r);return}g(e,t,n,r)},m=(e,t,n)=>{let r=e===``?t:_(t,e);r.classGroupId=n},h=(e,t,n,i)=>{if(v(e)){f(e(i),t,n,i);return}t.validators===null&&(t.validators=[]),t.validators.push(r(n,e))},g=(e,t,n,r)=>{let i=Object.entries(e),a=i.length;for(let e=0;e<a;e++){let[a,o]=i[e];f(o,_(t,a),n,r)}},_=(e,t)=>{let n=e,r=t.split(a),o=r.length;for(let e=0;e<o;e++){let t=r[e],a=n.nextPart.get(t);a||(a=i(),n.nextPart.set(t,a)),n=a}return n},v=e=>`isThemeGetter`in e&&e.isThemeGetter===!0,y=e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null),i=(i,a)=>{n[i]=a,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(e){let t=n[e];if(t!==void 0)return t;if((t=r[e])!==void 0)return i(e,t),t},set(e,t){e in n?n[e]=t:i(e,t)}}},b=`!`,x=`:`,S=[],C=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),w=e=>{let{prefix:t,experimentalParseClassName:n}=e,r=e=>{let t=[],n=0,r=0,i=0,a,o=e.length;for(let s=0;s<o;s++){let o=e[s];if(n===0&&r===0){if(o===x){t.push(e.slice(i,s)),i=s+1;continue}if(o===`/`){a=s;continue}}o===`[`?n++:o===`]`?n--:o===`(`?r++:o===`)`&&r--}let s=t.length===0?e:e.slice(i),c=s,l=!1;s.endsWith(b)?(c=s.slice(0,-1),l=!0):s.startsWith(b)&&(c=s.slice(1),l=!0);let u=a&&a>i?a-i:void 0;return C(t,l,c,u)};if(t){let e=t+x,n=r;r=t=>t.startsWith(e)?n(t.slice(e.length)):C(S,!1,t,void 0,!0)}if(n){let e=r;r=t=>n({className:t,parseClassName:e})}return r},te=e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,n)=>{t.set(e,1e6+n)}),e=>{let n=[],r=[];for(let i=0;i<e.length;i++){let a=e[i],o=a[0]===`[`,s=t.has(a);o||s?(r.length>0&&(r.sort(),n.push(...r),r=[]),n.push(a)):r.push(a)}return r.length>0&&(r.sort(),n.push(...r)),n}},ne=e=>({cache:y(e.cacheSize),parseClassName:w(e),sortModifiers:te(e),...c(e)}),T=/\s+/,re=(e,t)=>{let{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:a}=t,o=[],s=e.trim().split(T),c=``;for(let e=s.length-1;e>=0;--e){let t=s[e],{isExternal:l,modifiers:u,hasImportantModifier:ee,baseClassName:d,maybePostfixModifierPosition:f}=n(t);if(l){c=t+(c.length>0?` `+c:c);continue}let p=!!f,m=r(p?d.substring(0,f):d);if(!m){if(!p){c=t+(c.length>0?` `+c:c);continue}if(m=r(d),!m){c=t+(c.length>0?` `+c:c);continue}p=!1}let h=u.length===0?``:u.length===1?u[0]:a(u).join(`:`),g=ee?h+b:h,_=g+m;if(o.indexOf(_)>-1)continue;o.push(_);let v=i(m,p);for(let e=0;e<v.length;++e){let t=v[e];o.push(g+t)}c=t+(c.length>0?` `+c:c)}return c},E=(...e)=>{let t=0,n,r,i=``;for(;t<e.length;)(n=e[t++])&&(r=D(n))&&(i&&(i+=` `),i+=r);return i},D=e=>{if(typeof e==`string`)return e;let t,n=``;for(let r=0;r<e.length;r++)e[r]&&(t=D(e[r]))&&(n&&(n+=` `),n+=t);return n},O=(e,...t)=>{let n,r,i,a,o=o=>(n=ne(t.reduce((e,t)=>t(e),e())),r=n.cache.get,i=n.cache.set,a=s,s(o)),s=e=>{let t=r(e);if(t)return t;let a=re(e,n);return i(e,a),a};return a=o,(...e)=>a(E(...e))},k=[],A=e=>{let t=t=>t[e]||k;return t.isThemeGetter=!0,t},j=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,M=/^\((?:(\w[\w-]*):)?(.+)\)$/i,N=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,ie=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,ae=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,oe=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,P=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,F=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,I=e=>N.test(e),L=e=>!!e&&!Number.isNaN(Number(e)),R=e=>!!e&&Number.isInteger(Number(e)),se=e=>e.endsWith(`%`)&&L(e.slice(0,-1)),z=e=>ie.test(e),ce=()=>!0,B=e=>ae.test(e)&&!oe.test(e),V=()=>!1,le=e=>P.test(e),H=e=>F.test(e),ue=e=>!U(e)&&!K(e),de=e=>Y(e,$,V),U=e=>j.test(e),W=e=>Y(e,Se,B),fe=e=>Y(e,Ce,L),pe=e=>Y(e,Te,ce),me=e=>Y(e,we,V),he=e=>Y(e,Z,V),ge=e=>Y(e,Q,H),G=e=>Y(e,Ee,le),K=e=>M.test(e),q=e=>X(e,Se),_e=e=>X(e,we),ve=e=>X(e,Z),ye=e=>X(e,$),be=e=>X(e,Q),J=e=>X(e,Ee,!0),xe=e=>X(e,Te,!0),Y=(e,t,n)=>{let r=j.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},X=(e,t,n=!1)=>{let r=M.exec(e);return r?r[1]?t(r[1]):n:!1},Z=e=>e===`position`||e===`percentage`,Q=e=>e===`image`||e===`url`,$=e=>e===`length`||e===`size`||e===`bg-size`,Se=e=>e===`length`,Ce=e=>e===`number`,we=e=>e===`family-name`,Te=e=>e===`number`||e===`weight`,Ee=e=>e===`shadow`,De=O(()=>{let e=A(`color`),t=A(`font`),n=A(`text`),r=A(`font-weight`),i=A(`tracking`),a=A(`leading`),o=A(`breakpoint`),s=A(`container`),c=A(`spacing`),l=A(`radius`),u=A(`shadow`),ee=A(`inset-shadow`),d=A(`text-shadow`),f=A(`drop-shadow`),p=A(`blur`),m=A(`perspective`),h=A(`aspect`),g=A(`ease`),_=A(`animate`),v=()=>[`auto`,`avoid`,`all`,`avoid-page`,`page`,`left`,`right`,`column`],y=()=>[`center`,`top`,`bottom`,`left`,`right`,`top-left`,`left-top`,`top-right`,`right-top`,`bottom-right`,`right-bottom`,`bottom-left`,`left-bottom`],b=()=>[...y(),K,U],x=()=>[`auto`,`hidden`,`clip`,`visible`,`scroll`],S=()=>[`auto`,`contain`,`none`],C=()=>[K,U,c],w=()=>[I,`full`,`auto`,...C()],te=()=>[R,`none`,`subgrid`,K,U],ne=()=>[`auto`,{span:[`full`,R,K,U]},R,K,U],T=()=>[R,`auto`,K,U],re=()=>[`auto`,`min`,`max`,`fr`,K,U],E=()=>[`start`,`end`,`center`,`between`,`around`,`evenly`,`stretch`,`baseline`,`center-safe`,`end-safe`],D=()=>[`start`,`end`,`center`,`stretch`,`center-safe`,`end-safe`],O=()=>[`auto`,...C()],k=()=>[I,`auto`,`full`,`dvw`,`dvh`,`lvw`,`lvh`,`svw`,`svh`,`min`,`max`,`fit`,...C()],j=()=>[I,`screen`,`full`,`dvw`,`lvw`,`svw`,`min`,`max`,`fit`,...C()],M=()=>[I,`screen`,`full`,`lh`,`dvh`,`lvh`,`svh`,`min`,`max`,`fit`,...C()],N=()=>[e,K,U],ie=()=>[...y(),ve,he,{position:[K,U]}],ae=()=>[`no-repeat`,{repeat:[``,`x`,`y`,`space`,`round`]}],oe=()=>[`auto`,`cover`,`contain`,ye,de,{size:[K,U]}],P=()=>[se,q,W],F=()=>[``,`none`,`full`,l,K,U],B=()=>[``,L,q,W],V=()=>[`solid`,`dashed`,`dotted`,`double`],le=()=>[`normal`,`multiply`,`screen`,`overlay`,`darken`,`lighten`,`color-dodge`,`color-burn`,`hard-light`,`soft-light`,`difference`,`exclusion`,`hue`,`saturation`,`color`,`luminosity`],H=()=>[L,se,ve,he],Y=()=>[``,`none`,p,K,U],X=()=>[`none`,L,K,U],Z=()=>[`none`,L,K,U],Q=()=>[L,K,U],$=()=>[I,`full`,...C()];return{cacheSize:500,theme:{animate:[`spin`,`ping`,`pulse`,`bounce`],aspect:[`video`],blur:[z],breakpoint:[z],color:[ce],container:[z],"drop-shadow":[z],ease:[`in`,`out`,`in-out`],font:[ue],"font-weight":[`thin`,`extralight`,`light`,`normal`,`medium`,`semibold`,`bold`,`extrabold`,`black`],"inset-shadow":[z],leading:[`none`,`tight`,`snug`,`normal`,`relaxed`,`loose`],perspective:[`dramatic`,`near`,`normal`,`midrange`,`distant`,`none`],radius:[z],shadow:[z],spacing:[`px`,L],text:[z],"text-shadow":[z],tracking:[`tighter`,`tight`,`normal`,`wide`,`wider`,`widest`]},classGroups:{aspect:[{aspect:[`auto`,`square`,I,U,K,h]}],container:[`container`],columns:[{columns:[L,U,K,s]}],"break-after":[{"break-after":v()}],"break-before":[{"break-before":v()}],"break-inside":[{"break-inside":[`auto`,`avoid`,`avoid-page`,`avoid-column`]}],"box-decoration":[{"box-decoration":[`slice`,`clone`]}],box:[{box:[`border`,`content`]}],display:[`block`,`inline-block`,`inline`,`flex`,`inline-flex`,`table`,`inline-table`,`table-caption`,`table-cell`,`table-column`,`table-column-group`,`table-footer-group`,`table-header-group`,`table-row-group`,`table-row`,`flow-root`,`grid`,`inline-grid`,`contents`,`list-item`,`hidden`],sr:[`sr-only`,`not-sr-only`],float:[{float:[`right`,`left`,`none`,`start`,`end`]}],clear:[{clear:[`left`,`right`,`both`,`none`,`start`,`end`]}],isolation:[`isolate`,`isolation-auto`],"object-fit":[{object:[`contain`,`cover`,`fill`,`none`,`scale-down`]}],"object-position":[{object:b()}],overflow:[{overflow:x()}],"overflow-x":[{"overflow-x":x()}],"overflow-y":[{"overflow-y":x()}],overscroll:[{overscroll:S()}],"overscroll-x":[{"overscroll-x":S()}],"overscroll-y":[{"overscroll-y":S()}],position:[`static`,`fixed`,`absolute`,`relative`,`sticky`],inset:[{inset:w()}],"inset-x":[{"inset-x":w()}],"inset-y":[{"inset-y":w()}],start:[{"inset-s":w(),start:w()}],end:[{"inset-e":w(),end:w()}],"inset-bs":[{"inset-bs":w()}],"inset-be":[{"inset-be":w()}],top:[{top:w()}],right:[{right:w()}],bottom:[{bottom:w()}],left:[{left:w()}],visibility:[`visible`,`invisible`,`collapse`],z:[{z:[R,`auto`,K,U]}],basis:[{basis:[I,`full`,`auto`,s,...C()]}],"flex-direction":[{flex:[`row`,`row-reverse`,`col`,`col-reverse`]}],"flex-wrap":[{flex:[`nowrap`,`wrap`,`wrap-reverse`]}],flex:[{flex:[L,I,`auto`,`initial`,`none`,U]}],grow:[{grow:[``,L,K,U]}],shrink:[{shrink:[``,L,K,U]}],order:[{order:[R,`first`,`last`,`none`,K,U]}],"grid-cols":[{"grid-cols":te()}],"col-start-end":[{col:ne()}],"col-start":[{"col-start":T()}],"col-end":[{"col-end":T()}],"grid-rows":[{"grid-rows":te()}],"row-start-end":[{row:ne()}],"row-start":[{"row-start":T()}],"row-end":[{"row-end":T()}],"grid-flow":[{"grid-flow":[`row`,`col`,`dense`,`row-dense`,`col-dense`]}],"auto-cols":[{"auto-cols":re()}],"auto-rows":[{"auto-rows":re()}],gap:[{gap:C()}],"gap-x":[{"gap-x":C()}],"gap-y":[{"gap-y":C()}],"justify-content":[{justify:[...E(),`normal`]}],"justify-items":[{"justify-items":[...D(),`normal`]}],"justify-self":[{"justify-self":[`auto`,...D()]}],"align-content":[{content:[`normal`,...E()]}],"align-items":[{items:[...D(),{baseline:[``,`last`]}]}],"align-self":[{self:[`auto`,...D(),{baseline:[``,`last`]}]}],"place-content":[{"place-content":E()}],"place-items":[{"place-items":[...D(),`baseline`]}],"place-self":[{"place-self":[`auto`,...D()]}],p:[{p:C()}],px:[{px:C()}],py:[{py:C()}],ps:[{ps:C()}],pe:[{pe:C()}],pbs:[{pbs:C()}],pbe:[{pbe:C()}],pt:[{pt:C()}],pr:[{pr:C()}],pb:[{pb:C()}],pl:[{pl:C()}],m:[{m:O()}],mx:[{mx:O()}],my:[{my:O()}],ms:[{ms:O()}],me:[{me:O()}],mbs:[{mbs:O()}],mbe:[{mbe:O()}],mt:[{mt:O()}],mr:[{mr:O()}],mb:[{mb:O()}],ml:[{ml:O()}],"space-x":[{"space-x":C()}],"space-x-reverse":[`space-x-reverse`],"space-y":[{"space-y":C()}],"space-y-reverse":[`space-y-reverse`],size:[{size:k()}],"inline-size":[{inline:[`auto`,...j()]}],"min-inline-size":[{"min-inline":[`auto`,...j()]}],"max-inline-size":[{"max-inline":[`none`,...j()]}],"block-size":[{block:[`auto`,...M()]}],"min-block-size":[{"min-block":[`auto`,...M()]}],"max-block-size":[{"max-block":[`none`,...M()]}],w:[{w:[s,`screen`,...k()]}],"min-w":[{"min-w":[s,`screen`,`none`,...k()]}],"max-w":[{"max-w":[s,`screen`,`none`,`prose`,{screen:[o]},...k()]}],h:[{h:[`screen`,`lh`,...k()]}],"min-h":[{"min-h":[`screen`,`lh`,`none`,...k()]}],"max-h":[{"max-h":[`screen`,`lh`,...k()]}],"font-size":[{text:[`base`,n,q,W]}],"font-smoothing":[`antialiased`,`subpixel-antialiased`],"font-style":[`italic`,`not-italic`],"font-weight":[{font:[r,xe,pe]}],"font-stretch":[{"font-stretch":[`ultra-condensed`,`extra-condensed`,`condensed`,`semi-condensed`,`normal`,`semi-expanded`,`expanded`,`extra-expanded`,`ultra-expanded`,se,U]}],"font-family":[{font:[_e,me,t]}],"font-features":[{"font-features":[U]}],"fvn-normal":[`normal-nums`],"fvn-ordinal":[`ordinal`],"fvn-slashed-zero":[`slashed-zero`],"fvn-figure":[`lining-nums`,`oldstyle-nums`],"fvn-spacing":[`proportional-nums`,`tabular-nums`],"fvn-fraction":[`diagonal-fractions`,`stacked-fractions`],tracking:[{tracking:[i,K,U]}],"line-clamp":[{"line-clamp":[L,`none`,K,fe]}],leading:[{leading:[a,...C()]}],"list-image":[{"list-image":[`none`,K,U]}],"list-style-position":[{list:[`inside`,`outside`]}],"list-style-type":[{list:[`disc`,`decimal`,`none`,K,U]}],"text-alignment":[{text:[`left`,`center`,`right`,`justify`,`start`,`end`]}],"placeholder-color":[{placeholder:N()}],"text-color":[{text:N()}],"text-decoration":[`underline`,`overline`,`line-through`,`no-underline`],"text-decoration-style":[{decoration:[...V(),`wavy`]}],"text-decoration-thickness":[{decoration:[L,`from-font`,`auto`,K,W]}],"text-decoration-color":[{decoration:N()}],"underline-offset":[{"underline-offset":[L,`auto`,K,U]}],"text-transform":[`uppercase`,`lowercase`,`capitalize`,`normal-case`],"text-overflow":[`truncate`,`text-ellipsis`,`text-clip`],"text-wrap":[{text:[`wrap`,`nowrap`,`balance`,`pretty`]}],indent:[{indent:C()}],"vertical-align":[{align:[`baseline`,`top`,`middle`,`bottom`,`text-top`,`text-bottom`,`sub`,`super`,K,U]}],whitespace:[{whitespace:[`normal`,`nowrap`,`pre`,`pre-line`,`pre-wrap`,`break-spaces`]}],break:[{break:[`normal`,`words`,`all`,`keep`]}],wrap:[{wrap:[`break-word`,`anywhere`,`normal`]}],hyphens:[{hyphens:[`none`,`manual`,`auto`]}],content:[{content:[`none`,K,U]}],"bg-attachment":[{bg:[`fixed`,`local`,`scroll`]}],"bg-clip":[{"bg-clip":[`border`,`padding`,`content`,`text`]}],"bg-origin":[{"bg-origin":[`border`,`padding`,`content`]}],"bg-position":[{bg:ie()}],"bg-repeat":[{bg:ae()}],"bg-size":[{bg:oe()}],"bg-image":[{bg:[`none`,{linear:[{to:[`t`,`tr`,`r`,`br`,`b`,`bl`,`l`,`tl`]},R,K,U],radial:[``,K,U],conic:[R,K,U]},be,ge]}],"bg-color":[{bg:N()}],"gradient-from-pos":[{from:P()}],"gradient-via-pos":[{via:P()}],"gradient-to-pos":[{to:P()}],"gradient-from":[{from:N()}],"gradient-via":[{via:N()}],"gradient-to":[{to:N()}],rounded:[{rounded:F()}],"rounded-s":[{"rounded-s":F()}],"rounded-e":[{"rounded-e":F()}],"rounded-t":[{"rounded-t":F()}],"rounded-r":[{"rounded-r":F()}],"rounded-b":[{"rounded-b":F()}],"rounded-l":[{"rounded-l":F()}],"rounded-ss":[{"rounded-ss":F()}],"rounded-se":[{"rounded-se":F()}],"rounded-ee":[{"rounded-ee":F()}],"rounded-es":[{"rounded-es":F()}],"rounded-tl":[{"rounded-tl":F()}],"rounded-tr":[{"rounded-tr":F()}],"rounded-br":[{"rounded-br":F()}],"rounded-bl":[{"rounded-bl":F()}],"border-w":[{border:B()}],"border-w-x":[{"border-x":B()}],"border-w-y":[{"border-y":B()}],"border-w-s":[{"border-s":B()}],"border-w-e":[{"border-e":B()}],"border-w-bs":[{"border-bs":B()}],"border-w-be":[{"border-be":B()}],"border-w-t":[{"border-t":B()}],"border-w-r":[{"border-r":B()}],"border-w-b":[{"border-b":B()}],"border-w-l":[{"border-l":B()}],"divide-x":[{"divide-x":B()}],"divide-x-reverse":[`divide-x-reverse`],"divide-y":[{"divide-y":B()}],"divide-y-reverse":[`divide-y-reverse`],"border-style":[{border:[...V(),`hidden`,`none`]}],"divide-style":[{divide:[...V(),`hidden`,`none`]}],"border-color":[{border:N()}],"border-color-x":[{"border-x":N()}],"border-color-y":[{"border-y":N()}],"border-color-s":[{"border-s":N()}],"border-color-e":[{"border-e":N()}],"border-color-bs":[{"border-bs":N()}],"border-color-be":[{"border-be":N()}],"border-color-t":[{"border-t":N()}],"border-color-r":[{"border-r":N()}],"border-color-b":[{"border-b":N()}],"border-color-l":[{"border-l":N()}],"divide-color":[{divide:N()}],"outline-style":[{outline:[...V(),`none`,`hidden`]}],"outline-offset":[{"outline-offset":[L,K,U]}],"outline-w":[{outline:[``,L,q,W]}],"outline-color":[{outline:N()}],shadow:[{shadow:[``,`none`,u,J,G]}],"shadow-color":[{shadow:N()}],"inset-shadow":[{"inset-shadow":[`none`,ee,J,G]}],"inset-shadow-color":[{"inset-shadow":N()}],"ring-w":[{ring:B()}],"ring-w-inset":[`ring-inset`],"ring-color":[{ring:N()}],"ring-offset-w":[{"ring-offset":[L,W]}],"ring-offset-color":[{"ring-offset":N()}],"inset-ring-w":[{"inset-ring":B()}],"inset-ring-color":[{"inset-ring":N()}],"text-shadow":[{"text-shadow":[`none`,d,J,G]}],"text-shadow-color":[{"text-shadow":N()}],opacity:[{opacity:[L,K,U]}],"mix-blend":[{"mix-blend":[...le(),`plus-darker`,`plus-lighter`]}],"bg-blend":[{"bg-blend":le()}],"mask-clip":[{"mask-clip":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]},`mask-no-clip`],"mask-composite":[{mask:[`add`,`subtract`,`intersect`,`exclude`]}],"mask-image-linear-pos":[{"mask-linear":[L]}],"mask-image-linear-from-pos":[{"mask-linear-from":H()}],"mask-image-linear-to-pos":[{"mask-linear-to":H()}],"mask-image-linear-from-color":[{"mask-linear-from":N()}],"mask-image-linear-to-color":[{"mask-linear-to":N()}],"mask-image-t-from-pos":[{"mask-t-from":H()}],"mask-image-t-to-pos":[{"mask-t-to":H()}],"mask-image-t-from-color":[{"mask-t-from":N()}],"mask-image-t-to-color":[{"mask-t-to":N()}],"mask-image-r-from-pos":[{"mask-r-from":H()}],"mask-image-r-to-pos":[{"mask-r-to":H()}],"mask-image-r-from-color":[{"mask-r-from":N()}],"mask-image-r-to-color":[{"mask-r-to":N()}],"mask-image-b-from-pos":[{"mask-b-from":H()}],"mask-image-b-to-pos":[{"mask-b-to":H()}],"mask-image-b-from-color":[{"mask-b-from":N()}],"mask-image-b-to-color":[{"mask-b-to":N()}],"mask-image-l-from-pos":[{"mask-l-from":H()}],"mask-image-l-to-pos":[{"mask-l-to":H()}],"mask-image-l-from-color":[{"mask-l-from":N()}],"mask-image-l-to-color":[{"mask-l-to":N()}],"mask-image-x-from-pos":[{"mask-x-from":H()}],"mask-image-x-to-pos":[{"mask-x-to":H()}],"mask-image-x-from-color":[{"mask-x-from":N()}],"mask-image-x-to-color":[{"mask-x-to":N()}],"mask-image-y-from-pos":[{"mask-y-from":H()}],"mask-image-y-to-pos":[{"mask-y-to":H()}],"mask-image-y-from-color":[{"mask-y-from":N()}],"mask-image-y-to-color":[{"mask-y-to":N()}],"mask-image-radial":[{"mask-radial":[K,U]}],"mask-image-radial-from-pos":[{"mask-radial-from":H()}],"mask-image-radial-to-pos":[{"mask-radial-to":H()}],"mask-image-radial-from-color":[{"mask-radial-from":N()}],"mask-image-radial-to-color":[{"mask-radial-to":N()}],"mask-image-radial-shape":[{"mask-radial":[`circle`,`ellipse`]}],"mask-image-radial-size":[{"mask-radial":[{closest:[`side`,`corner`],farthest:[`side`,`corner`]}]}],"mask-image-radial-pos":[{"mask-radial-at":y()}],"mask-image-conic-pos":[{"mask-conic":[L]}],"mask-image-conic-from-pos":[{"mask-conic-from":H()}],"mask-image-conic-to-pos":[{"mask-conic-to":H()}],"mask-image-conic-from-color":[{"mask-conic-from":N()}],"mask-image-conic-to-color":[{"mask-conic-to":N()}],"mask-mode":[{mask:[`alpha`,`luminance`,`match`]}],"mask-origin":[{"mask-origin":[`border`,`padding`,`content`,`fill`,`stroke`,`view`]}],"mask-position":[{mask:ie()}],"mask-repeat":[{mask:ae()}],"mask-size":[{mask:oe()}],"mask-type":[{"mask-type":[`alpha`,`luminance`]}],"mask-image":[{mask:[`none`,K,U]}],filter:[{filter:[``,`none`,K,U]}],blur:[{blur:Y()}],brightness:[{brightness:[L,K,U]}],contrast:[{contrast:[L,K,U]}],"drop-shadow":[{"drop-shadow":[``,`none`,f,J,G]}],"drop-shadow-color":[{"drop-shadow":N()}],grayscale:[{grayscale:[``,L,K,U]}],"hue-rotate":[{"hue-rotate":[L,K,U]}],invert:[{invert:[``,L,K,U]}],saturate:[{saturate:[L,K,U]}],sepia:[{sepia:[``,L,K,U]}],"backdrop-filter":[{"backdrop-filter":[``,`none`,K,U]}],"backdrop-blur":[{"backdrop-blur":Y()}],"backdrop-brightness":[{"backdrop-brightness":[L,K,U]}],"backdrop-contrast":[{"backdrop-contrast":[L,K,U]}],"backdrop-grayscale":[{"backdrop-grayscale":[``,L,K,U]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[L,K,U]}],"backdrop-invert":[{"backdrop-invert":[``,L,K,U]}],"backdrop-opacity":[{"backdrop-opacity":[L,K,U]}],"backdrop-saturate":[{"backdrop-saturate":[L,K,U]}],"backdrop-sepia":[{"backdrop-sepia":[``,L,K,U]}],"border-collapse":[{border:[`collapse`,`separate`]}],"border-spacing":[{"border-spacing":C()}],"border-spacing-x":[{"border-spacing-x":C()}],"border-spacing-y":[{"border-spacing-y":C()}],"table-layout":[{table:[`auto`,`fixed`]}],caption:[{caption:[`top`,`bottom`]}],transition:[{transition:[``,`all`,`colors`,`opacity`,`shadow`,`transform`,`none`,K,U]}],"transition-behavior":[{transition:[`normal`,`discrete`]}],duration:[{duration:[L,`initial`,K,U]}],ease:[{ease:[`linear`,`initial`,g,K,U]}],delay:[{delay:[L,K,U]}],animate:[{animate:[`none`,_,K,U]}],backface:[{backface:[`hidden`,`visible`]}],perspective:[{perspective:[m,K,U]}],"perspective-origin":[{"perspective-origin":b()}],rotate:[{rotate:X()}],"rotate-x":[{"rotate-x":X()}],"rotate-y":[{"rotate-y":X()}],"rotate-z":[{"rotate-z":X()}],scale:[{scale:Z()}],"scale-x":[{"scale-x":Z()}],"scale-y":[{"scale-y":Z()}],"scale-z":[{"scale-z":Z()}],"scale-3d":[`scale-3d`],skew:[{skew:Q()}],"skew-x":[{"skew-x":Q()}],"skew-y":[{"skew-y":Q()}],transform:[{transform:[K,U,``,`none`,`gpu`,`cpu`]}],"transform-origin":[{origin:b()}],"transform-style":[{transform:[`3d`,`flat`]}],translate:[{translate:$()}],"translate-x":[{"translate-x":$()}],"translate-y":[{"translate-y":$()}],"translate-z":[{"translate-z":$()}],"translate-none":[`translate-none`],accent:[{accent:N()}],appearance:[{appearance:[`none`,`auto`]}],"caret-color":[{caret:N()}],"color-scheme":[{scheme:[`normal`,`dark`,`light`,`light-dark`,`only-dark`,`only-light`]}],cursor:[{cursor:[`auto`,`default`,`pointer`,`wait`,`text`,`move`,`help`,`not-allowed`,`none`,`context-menu`,`progress`,`cell`,`crosshair`,`vertical-text`,`alias`,`copy`,`no-drop`,`grab`,`grabbing`,`all-scroll`,`col-resize`,`row-resize`,`n-resize`,`e-resize`,`s-resize`,`w-resize`,`ne-resize`,`nw-resize`,`se-resize`,`sw-resize`,`ew-resize`,`ns-resize`,`nesw-resize`,`nwse-resize`,`zoom-in`,`zoom-out`,K,U]}],"field-sizing":[{"field-sizing":[`fixed`,`content`]}],"pointer-events":[{"pointer-events":[`auto`,`none`]}],resize:[{resize:[`none`,``,`y`,`x`]}],"scroll-behavior":[{scroll:[`auto`,`smooth`]}],"scroll-m":[{"scroll-m":C()}],"scroll-mx":[{"scroll-mx":C()}],"scroll-my":[{"scroll-my":C()}],"scroll-ms":[{"scroll-ms":C()}],"scroll-me":[{"scroll-me":C()}],"scroll-mbs":[{"scroll-mbs":C()}],"scroll-mbe":[{"scroll-mbe":C()}],"scroll-mt":[{"scroll-mt":C()}],"scroll-mr":[{"scroll-mr":C()}],"scroll-mb":[{"scroll-mb":C()}],"scroll-ml":[{"scroll-ml":C()}],"scroll-p":[{"scroll-p":C()}],"scroll-px":[{"scroll-px":C()}],"scroll-py":[{"scroll-py":C()}],"scroll-ps":[{"scroll-ps":C()}],"scroll-pe":[{"scroll-pe":C()}],"scroll-pbs":[{"scroll-pbs":C()}],"scroll-pbe":[{"scroll-pbe":C()}],"scroll-pt":[{"scroll-pt":C()}],"scroll-pr":[{"scroll-pr":C()}],"scroll-pb":[{"scroll-pb":C()}],"scroll-pl":[{"scroll-pl":C()}],"snap-align":[{snap:[`start`,`end`,`center`,`align-none`]}],"snap-stop":[{snap:[`normal`,`always`]}],"snap-type":[{snap:[`none`,`x`,`y`,`both`]}],"snap-strictness":[{snap:[`mandatory`,`proximity`]}],touch:[{touch:[`auto`,`none`,`manipulation`]}],"touch-x":[{"touch-pan":[`x`,`left`,`right`]}],"touch-y":[{"touch-pan":[`y`,`up`,`down`]}],"touch-pz":[`touch-pinch-zoom`],select:[{select:[`none`,`text`,`all`,`auto`]}],"will-change":[{"will-change":[`auto`,`scroll`,`contents`,`transform`,K,U]}],fill:[{fill:[`none`,...N()]}],"stroke-w":[{stroke:[L,q,W,fe]}],stroke:[{stroke:[`none`,...N()]}],"forced-color-adjust":[{"forced-color-adjust":[`auto`,`none`]}]},conflictingClassGroups:{overflow:[`overflow-x`,`overflow-y`],overscroll:[`overscroll-x`,`overscroll-y`],inset:[`inset-x`,`inset-y`,`inset-bs`,`inset-be`,`start`,`end`,`top`,`right`,`bottom`,`left`],"inset-x":[`right`,`left`],"inset-y":[`top`,`bottom`],flex:[`basis`,`grow`,`shrink`],gap:[`gap-x`,`gap-y`],p:[`px`,`py`,`ps`,`pe`,`pbs`,`pbe`,`pt`,`pr`,`pb`,`pl`],px:[`pr`,`pl`],py:[`pt`,`pb`],m:[`mx`,`my`,`ms`,`me`,`mbs`,`mbe`,`mt`,`mr`,`mb`,`ml`],mx:[`mr`,`ml`],my:[`mt`,`mb`],size:[`w`,`h`],"font-size":[`leading`],"fvn-normal":[`fvn-ordinal`,`fvn-slashed-zero`,`fvn-figure`,`fvn-spacing`,`fvn-fraction`],"fvn-ordinal":[`fvn-normal`],"fvn-slashed-zero":[`fvn-normal`],"fvn-figure":[`fvn-normal`],"fvn-spacing":[`fvn-normal`],"fvn-fraction":[`fvn-normal`],"line-clamp":[`display`,`overflow`],rounded:[`rounded-s`,`rounded-e`,`rounded-t`,`rounded-r`,`rounded-b`,`rounded-l`,`rounded-ss`,`rounded-se`,`rounded-ee`,`rounded-es`,`rounded-tl`,`rounded-tr`,`rounded-br`,`rounded-bl`],"rounded-s":[`rounded-ss`,`rounded-es`],"rounded-e":[`rounded-se`,`rounded-ee`],"rounded-t":[`rounded-tl`,`rounded-tr`],"rounded-r":[`rounded-tr`,`rounded-br`],"rounded-b":[`rounded-br`,`rounded-bl`],"rounded-l":[`rounded-tl`,`rounded-bl`],"border-spacing":[`border-spacing-x`,`border-spacing-y`],"border-w":[`border-w-x`,`border-w-y`,`border-w-s`,`border-w-e`,`border-w-bs`,`border-w-be`,`border-w-t`,`border-w-r`,`border-w-b`,`border-w-l`],"border-w-x":[`border-w-r`,`border-w-l`],"border-w-y":[`border-w-t`,`border-w-b`],"border-color":[`border-color-x`,`border-color-y`,`border-color-s`,`border-color-e`,`border-color-bs`,`border-color-be`,`border-color-t`,`border-color-r`,`border-color-b`,`border-color-l`],"border-color-x":[`border-color-r`,`border-color-l`],"border-color-y":[`border-color-t`,`border-color-b`],translate:[`translate-x`,`translate-y`,`translate-none`],"translate-none":[`translate`,`translate-x`,`translate-y`,`translate-z`],"scroll-m":[`scroll-mx`,`scroll-my`,`scroll-ms`,`scroll-me`,`scroll-mbs`,`scroll-mbe`,`scroll-mt`,`scroll-mr`,`scroll-mb`,`scroll-ml`],"scroll-mx":[`scroll-mr`,`scroll-ml`],"scroll-my":[`scroll-mt`,`scroll-mb`],"scroll-p":[`scroll-px`,`scroll-py`,`scroll-ps`,`scroll-pe`,`scroll-pbs`,`scroll-pbe`,`scroll-pt`,`scroll-pr`,`scroll-pb`,`scroll-pl`],"scroll-px":[`scroll-pr`,`scroll-pl`],"scroll-py":[`scroll-pt`,`scroll-pb`],touch:[`touch-x`,`touch-y`,`touch-pz`],"touch-x":[`touch`],"touch-y":[`touch`],"touch-pz":[`touch`]},conflictingClassGroupModifiers:{"font-size":[`leading`]},orderSensitiveModifiers:[`*`,`**`,`after`,`backdrop`,`before`,`details-content`,`file`,`first-letter`,`first-line`,`marker`,`placeholder`,`selection`]}});function Oe(...e){return De(t(e))}export{t as n,Oe as t};
@@ -0,0 +1 @@
1
+ import{t as e}from"./createLucideIcon-Dy1wlrF7.js";var t=e(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{t};
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
2
+ <rect width="192" height="192" rx="24" fill="#0f1419"/>
3
+ <rect x="8" y="8" width="176" height="176" rx="20" fill="#1a1f2e"/>
4
+ <text x="96" y="110" text-anchor="middle" font-family="sans-serif" font-weight="700" font-size="56" fill="#3b82f6">PPM</text>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
2
+ <rect width="192" height="192" rx="24" fill="#0f1419"/>
3
+ <rect x="8" y="8" width="176" height="176" rx="20" fill="#1a1f2e"/>
4
+ <text x="96" y="110" text-anchor="middle" font-family="sans-serif" font-weight="700" font-size="56" fill="#3b82f6">PPM</text>
5
+ </svg>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
+ <meta name="theme-color" content="#0f1419" />
7
+ <title>PPM — Personal Project Manager</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
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-DGSLw2GE.js"></script>
12
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-BnxRlLMJ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/utils-Cgi2TYRi.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/button-BxijdhtM.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Dy1wlrF7.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/x-BISR7bpK.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/dialog-RczsXsmw.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/api-client-Bnf9LAt4.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/react-Uzd0zARU.js">
20
+ <link rel="stylesheet" crossorigin href="/assets/index-DYd_2slk.css">
21
+ <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
22
+ <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
23
+ <div id="root"></div>
24
+ </body>
25
+ </html>
@@ -0,0 +1 @@
1
+ {"name":"PPM — Personal Project Manager","short_name":"PPM","description":"Mobile-first web IDE for managing code projects","start_url":"/","display":"standalone","background_color":"#0f1419","theme_color":"#0f1419","lang":"en","scope":"/","orientation":"any","icons":[{"src":"/icon-192.png","sizes":"192x192","type":"image/png"},{"src":"/icon-512.png","sizes":"512x512","type":"image/png"}]}
@@ -0,0 +1 @@
1
+ if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/sw.js', { scope: '/' })})}
package/dist/web/sw.js ADDED
@@ -0,0 +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,r)=>{const n=s||("document"in self?document.currentScript.src:"")||location.href;if(e[n])return;let t={};const u=s=>i(s,n),o={module:{uri:n},exports:t,require:u};e[n]=Promise.all(l.map(s=>o[s]||u(s))).then(s=>(r(...s),t))}}define(["./workbox-3e722498"],function(s){"use strict";self.skipWaiting(),s.clientsClaim(),s.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"index.html",revision:"5bbe3db98c1f5e52e3f88df7bf70cfa0"},{url:"icon-512.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"icon-192.svg",revision:"a0fb34fc84eb148d51812cd62669f20d"},{url:"assets/x-BISR7bpK.js",revision:null},{url:"assets/utils-Cgi2TYRi.js",revision:null},{url:"assets/trash-2-CHLebaNh.js",revision:null},{url:"assets/terminal-tab-CbwaI-oq.js",revision:null},{url:"assets/terminal-tab-BrP-ENHg.css",revision:null},{url:"assets/settings-tab-DJRzIAuP.js",revision:null},{url:"assets/refresh-cw-DtopuYJf.js",revision:null},{url:"assets/react-Uzd0zARU.js",revision:null},{url:"assets/project-list-DWVXEimw.js",revision:null},{url:"assets/jsx-runtime-BnxRlLMJ.js",revision:null},{url:"assets/index-DYd_2slk.css",revision:null},{url:"assets/index-DGSLw2GE.js",revision:null},{url:"assets/git-status-panel-D8ZUQrRF.js",revision:null},{url:"assets/git-graph-DXMB_DoT.js",revision:null},{url:"assets/external-link-WSiY-639.js",revision:null},{url:"assets/dist-CSp7ir0r.js",revision:null},{url:"assets/diff-viewer-D6ixPlNB.js",revision:null},{url:"assets/dialog-RczsXsmw.js",revision:null},{url:"assets/createLucideIcon-Dy1wlrF7.js",revision:null},{url:"assets/code-editor-hbllHzj7.js",revision:null},{url:"assets/chat-tab-BZopEuub.js",revision:null},{url:"assets/button-BxijdhtM.js",revision:null},{url:"assets/arrow-up-from-line-BXL5dtbG.js",revision:null},{url:"assets/api-client-Bnf9LAt4.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")});
@@ -0,0 +1 @@
1
+ define(["exports"],function(t){"use strict";try{self["workbox:core:7.3.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.3.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=i&&i.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:r})}catch(t){a=Promise.reject(t)}const h=i&&i.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const o=i.match({url:t,sameOrigin:e,request:s,event:n});if(o)return r=o,(Array.isArray(r)&&0===r.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);function h(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new r(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)o=new i(t,e,n);else if("function"==typeof t)o=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return a().registerRoute(o),o}function u(t){return new Promise(e=>setTimeout(e,t))}const l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter(t=>t&&t.length>0).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const g=new Set;try{self["workbox:strategies:7.3.0"]&&_()}catch(t){}function R(t){return"string"==typeof t?new Request(t):t}class m{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.R=[...t.plugins],this.m=new Map;for(const t of this.R)this.m.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=R(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw r&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=R(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),o=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const n=R(t);await u(0);const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(i=r.url,new URL(String(i),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var i;const o=await this.v(e);if(!o)return!1;const{cacheName:c,matchOptions:a}=this.u,h=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,i);for(const e of o)if(r===p(e.url,s))return t.match(e,n)}(h,r.clone(),["__WB_REVISION__"],a):null;try{await h.put(r,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of g)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=R(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.m.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){for(;this.p.length;){const t=this.p.splice(0),e=(await Promise.allSettled(t)).find(t=>"rejected"===t.status);if(e)throw e.reason}}destroy(){this.l.resolve(null)}async v(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class v{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new m(this,{event:e,request:s,params:n}),i=this.q(r,s,e);return[i,this.U(i,r,s,e)]}async q(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.L(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async U(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function q(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.3.0"]&&_()}catch(t){}function U(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class L{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class b{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this._.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this._=t}}let E,C;async function x(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},o=e?e(i):i,c=function(){if(void 0===E){const t=new Response("");if("body"in t)try{new Response(t.body),E=!0}catch(t){E=!1}E=!1}return E}()?r.body:await r.blob();return new Response(c,o)}class O extends v{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(O.copyRedirectedCacheableResponsesPlugin)}async L(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const r=e.params||{};if(!this.C)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,o=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&o&&"no-cors"!==t.mode&&(this.P(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.P();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}P(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==O.copyRedirectedCacheableResponsesPlugin&&(n===O.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(O.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}O.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},O.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await x(t):t};class N{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.k=new Map,this.T=new Map,this.j=new Map,this.u=new O({cacheName:w(t),plugins:[...e,new b({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.K||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.K=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=U(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.k.has(r)&&this.k.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.k.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.j.has(t)&&this.j.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.j.set(t,n.integrity)}if(this.k.set(r,t),this.T.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return q(t,async()=>{const e=new L;this.strategy.plugins.push(e);for(const[e,s]of this.k){const n=this.j.get(s),r=this.T.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}})}activate(t){return q(t,async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.k.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}})}getURLsToCacheKeys(){return this.k}getCachedURLs(){return[...this.k.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.k.get(e.href)}getIntegrityForCacheKey(t){return this.j.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const P=()=>(C||(C=new N),C);class k extends r{constructor(t,e){super(({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(i,e);if(yield o.href,s&&o.pathname.endsWith("/")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}},t.strategy)}}t.NavigationRoute=class extends r{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super(t=>this.W(t),t),this.M=e,this.S=s}W({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.S)if(t.test(s))return!1;return!!this.M.some(t=>t.test(s))}},t.NetworkOnly=class extends v{constructor(t={}){super(t),this.D=t.networkTimeoutSeconds||0}async L(t,e){let n,r;try{const s=[e.fetch(t)];if(this.D){const t=u(1e3*this.D);s.push(t)}if(r=await Promise.race(s),!r)throw new Error(`Timed out the network response after ${this.D} seconds.`)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter(s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t);return await Promise.all(s.map(t=>self.caches.delete(t))),s})(e).then(t=>{}))})},t.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},t.createHandlerBoundToURL=function(t){return P().createHandlerBoundToURL(t)},t.precacheAndRoute=function(t,e){!function(t){P().precache(t)}(t),function(t){const e=P();h(new k(e,t))}(e)},t.registerRoute=h});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -14,6 +14,7 @@
14
14
  "build": "bun run build:web && bun build src/index.ts --compile --outfile dist/ppm",
15
15
  "start": "bun run src/index.ts start",
16
16
  "typecheck": "bunx tsc --noEmit",
17
+ "prepublishOnly": "bun run build:web",
17
18
  "postinstall": "node node_modules/ccburn/scripts/postinstall.js"
18
19
  },
19
20
  "devDependencies": {
@@ -114,11 +114,25 @@ export async function startServer(options: {
114
114
  } as Parameters<typeof Bun.serve>[0] extends { websocket?: infer W } ? W : never,
115
115
  });
116
116
 
117
- console.log(`PPM server running on http://${host}:${server.port}`);
118
- console.log(`Auth: ${configService.get("auth").enabled ? "enabled" : "disabled"}`);
117
+ console.log(`\n PPM v0.1.2 ready\n`);
118
+ console.log(` ➜ Local: http://localhost:${server.port}/`);
119
+
120
+ // List all network interfaces
121
+ const { networkInterfaces } = await import("node:os");
122
+ const nets = networkInterfaces();
123
+ for (const name of Object.keys(nets)) {
124
+ for (const net of nets[name] ?? []) {
125
+ if (net.family === "IPv4" && !net.internal) {
126
+ console.log(` ➜ Network: http://${net.address}:${server.port}/`);
127
+ }
128
+ }
129
+ }
130
+
131
+ console.log(`\n Auth: ${configService.get("auth").enabled ? "enabled" : "disabled"}`);
119
132
  if (configService.get("auth").enabled) {
120
- console.log(`Token: ${configService.get("auth").token}`);
133
+ console.log(` Token: ${configService.get("auth").token}`);
121
134
  }
135
+ console.log();
122
136
  }
123
137
 
124
138
  // Internal entry point for daemon child process
@@ -1,3 +0,0 @@
1
- # Tester Agent Memory Index
2
-
3
- - [project-ppm-test-conventions.md](project-ppm-test-conventions.md) - PPM test setup, gotchas, and conventions
@@ -1,32 +0,0 @@
1
- ---
2
- name: PPM test conventions and gotchas
3
- description: Key patterns, pitfalls, and setup details for writing tests in the PPM project
4
- type: project
5
- ---
6
-
7
- ## Test runner: `bun test` (Jest-compatible API from `bun:test`)
8
-
9
- ## Test structure
10
- - `tests/setup.ts` — shared helpers: `createTempDir`, `cleanupDir`, `createTempGitRepo`, `buildTestApp`
11
- - `tests/unit/services/` — unit tests for ConfigService, ProjectService, FileService, GitService
12
- - `tests/integration/api/` — integration tests using `app.request()` (no real server needed)
13
-
14
- ## Critical gotchas
15
-
16
- ### ppm.yaml in CWD
17
- The project root has a real `ppm.yaml` with `port: 5555`. `ConfigService.load(missingPath)` falls through to `LOCAL_CONFIG = "ppm.yaml"` in CWD when the given path doesn't exist. Always write an actual file before calling `load()` to avoid picking up this real config.
18
-
19
- ### Global configService in git routes
20
- `src/server/routes/git.ts` imports and uses the global `configService` singleton (not injected). Integration tests for git API must mutate `configService.config.projects` directly to register the test repo. Restore to `[]` in `afterEach`.
21
-
22
- ### ConfigService.load() fallback behavior
23
- Candidates checked in order: explicit path → PPM_CONFIG env → LOCAL_CONFIG (ppm.yaml) → HOME_CONFIG (~/.ppm/config.yaml). A missing explicit path does NOT stop the fallback chain.
24
-
25
- ### buildTestApp in setup.ts
26
- Overrides `configService.save = () => {}` (no-op) to prevent tests writing to disk. Injects config directly by mutating private fields via `as unknown as`.
27
-
28
- ### Real git repos for git tests
29
- `createTempGitRepo()` uses `Bun.spawn` with git env vars (author name/email) to create a real repo with an initial commit. No mocks for git operations.
30
-
31
- **Why:** Tests must use real implementations — no fakes/mocks that diverge from production behavior.
32
- **How to apply:** Always use `createTempGitRepo` for anything touching GitService or git API routes.
@@ -1,46 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags: ['v*']
6
-
7
- jobs:
8
- build:
9
- strategy:
10
- matrix:
11
- include:
12
- - os: ubuntu-latest
13
- target: bun-linux-x64
14
- artifact: ppm-linux-x64
15
- - os: macos-latest
16
- target: bun-darwin-arm64
17
- artifact: ppm-darwin-arm64
18
- - os: macos-13
19
- target: bun-darwin-x64
20
- artifact: ppm-darwin-x64
21
-
22
- runs-on: ${{ matrix.os }}
23
- steps:
24
- - uses: actions/checkout@v4
25
- - uses: oven-sh/setup-bun@v2
26
- - run: bun install
27
- - run: bun run build:web
28
- - run: bun build src/index.ts --compile --target=${{ matrix.target }} --outfile=dist/${{ matrix.artifact }}
29
- - uses: actions/upload-artifact@v4
30
- with:
31
- name: ${{ matrix.artifact }}
32
- path: dist/${{ matrix.artifact }}
33
-
34
- release:
35
- needs: build
36
- runs-on: ubuntu-latest
37
- permissions:
38
- contents: write
39
- steps:
40
- - uses: actions/download-artifact@v4
41
- - uses: softprops/action-gh-release@v2
42
- with:
43
- files: |
44
- ppm-linux-x64/ppm-linux-x64
45
- ppm-darwin-arm64/ppm-darwin-arm64
46
- ppm-darwin-x64/ppm-darwin-x64
@@ -1,81 +0,0 @@
1
- # Phase 1: Project Skeleton + Shared Types
2
-
3
- **Owner:** Lead
4
- **Priority:** Critical
5
- **Depends on:** None
6
- **Effort:** Small
7
-
8
- ## Overview
9
-
10
- Initialize monorepo, install dependencies, create shared types, config files. This unblocks all other phases.
11
-
12
- ## Steps
13
-
14
- 1. **Init Bun project**
15
- ```bash
16
- bun init
17
- ```
18
-
19
- 2. **Install backend dependencies**
20
- ```bash
21
- bun add hono commander js-yaml simple-git @anthropic-ai/claude-agent-sdk
22
- bun add -d @types/node typescript
23
- ```
24
-
25
- 3. **Install frontend dependencies**
26
- ```bash
27
- bun add react react-dom zustand @tanstack/react-query
28
- bun add -d vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vite-plugin-pwa
29
- bun add codemirror @codemirror/lang-javascript @codemirror/lang-typescript @codemirror/lang-python @codemirror/lang-html @codemirror/lang-css @codemirror/lang-json @codemirror/lang-markdown @codemirror/autocomplete @codemirror/merge @codemirror/theme-one-dark @uiw/react-codemirror
30
- bun add @xterm/xterm @xterm/addon-fit @xterm/addon-web-links
31
- bun add diff2html react-resizable-panels lucide-react
32
- ```
33
-
34
- 4. **Init shadcn/ui**
35
- ```bash
36
- bunx --bun shadcn@latest init
37
- bunx --bun shadcn@latest add button dialog dropdown-menu context-menu input tabs scroll-area tooltip separator sonner
38
- ```
39
-
40
- 5. **Create config files**
41
- - `tsconfig.json` — strict, paths alias `@/` → `src/web/`
42
- - `vite.config.ts` — React plugin, Tailwind, PWA plugin, build output to `dist/web`
43
- - `tailwind.config.ts` — shadcn/ui preset, mobile-first
44
- - `bunfig.toml` — Bun config
45
- - `ppm.example.yaml` — example config
46
- - `.env.example` — `ANTHROPIC_API_KEY=`
47
- - `.gitignore`
48
-
49
- 6. **Create shared types** (`src/types/`)
50
- - `config.ts` — PpmConfig, AuthConfig, ProjectConfig, AIProviderConfig
51
- - `project.ts` — Project, ProjectInfo
52
- - `git.ts` — GitCommit, GitBranch, GitStatus, GitGraphData, GitDiffResult, GitFileChange
53
- - `chat.ts` — AIProvider interface, ChatEvent, SessionConfig, SessionInfo, ToolApprovalRequest
54
- - `terminal.ts` — TerminalSession, TerminalResize
55
- - `api.ts` — API request/response wrappers
56
-
57
- 7. **Create entry point** `src/index.ts` — minimal Commander.js setup with `start` command placeholder
58
-
59
- 8. **Create README.md** with project description, setup instructions
60
-
61
- 9. **Create CLAUDE.md** with project conventions
62
-
63
- 10. **Verify build**
64
- ```bash
65
- bun run src/index.ts --help
66
- ```
67
-
68
- ## Files to Create
69
-
70
- - `package.json`, `tsconfig.json`, `vite.config.ts`, `tailwind.config.ts`, `bunfig.toml`
71
- - `ppm.example.yaml`, `.env.example`, `.gitignore`
72
- - `src/index.ts`
73
- - `src/types/*.ts` (6 files)
74
- - `README.md`, `CLAUDE.md`, `LICENSE`
75
-
76
- ## Success Criteria
77
-
78
- - [x] `bun run src/index.ts --help` shows CLI help
79
- - [x] `bun run --hot src/web/main.tsx` (via vite) starts dev server
80
- - [x] All shared types compile without errors
81
- - [x] shadcn/ui components installed and importable
@@ -1,148 +0,0 @@
1
- # Phase 2: Backend Core (Server + CLI + Config)
2
-
3
- **Owner:** backend-dev
4
- **Priority:** Critical
5
- **Depends on:** Phase 1
6
- **Effort:** Medium
7
-
8
- ## Overview
9
-
10
- Hono HTTP server, WebSocket upgrade, config loading, auth middleware, static file serving. CLI `init`, `start`, `stop`, `open` commands.
11
-
12
- ## Key Insights
13
-
14
- - Hono on Bun has built-in WebSocket support via `Bun.serve`
15
- - Config loaded from `ppm.yaml` via js-yaml, validated at startup
16
- - Auth = simple Bearer token header check
17
- - Static files = serve Vite build output from `dist/web/`
18
-
19
- ## Files to Create/Modify
20
-
21
- ```
22
- src/
23
- ├── server/
24
- │ ├── index.ts # Hono app, mount routes, WS upgrade
25
- │ ├── middleware/auth.ts # Token auth
26
- │ ├── routes/static.ts # Serve SPA with fallback to index.html
27
- │ └── routes/projects.ts # GET /api/projects, POST /api/projects
28
- ├── services/
29
- │ ├── config.service.ts # Load/save ppm.yaml
30
- │ └── project.service.ts # Project CRUD (add/remove/list)
31
- ├── cli/
32
- │ ├── index.ts # Commander.js program setup
33
- │ ├── commands/init.ts # Scan .git folders, interactive setup
34
- │ ├── commands/start.ts # Start Hono server (+ daemon mode)
35
- │ ├── commands/stop.ts # Kill daemon by PID file
36
- │ ├── commands/open.ts # Open browser to http://localhost:PORT
37
- │ └── utils/project-resolver.ts # CWD detect + -p flag
38
- └── index.ts # Wire CLI → commands
39
- ```
40
-
41
- ## Implementation Steps
42
-
43
- ### 1. Config Service
44
- ```typescript
45
- // config.service.ts
46
- class ConfigService {
47
- private config: PpmConfig;
48
- load(path?: string): PpmConfig // Default: ~/.ppm/config.yaml or ./ppm.yaml
49
- save(): void
50
- get<K extends keyof PpmConfig>(key: K): PpmConfig[K]
51
- set<K extends keyof PpmConfig>(key: K, value: PpmConfig[K]): void
52
- }
53
- ```
54
- - Search order: `--config` flag → `./ppm.yaml` → `~/.ppm/config.yaml`
55
- - Create default config on first run
56
-
57
- ### 2. Project Service
58
- ```typescript
59
- class ProjectService {
60
- constructor(private config: ConfigService)
61
- list(): ProjectInfo[]
62
- add(path: string, name?: string): void
63
- remove(nameOrPath: string): void
64
- resolve(nameOrPath?: string): Project // CWD or -p flag
65
- scanForGitRepos(dir: string): string[] // Find .git folders recursively
66
- }
67
- ```
68
-
69
- ### 3. Hono Server
70
- ```typescript
71
- // server/index.ts
72
- const app = new Hono()
73
- app.use('/api/*', authMiddleware)
74
- app.route('/api/projects', projectRoutes)
75
- // WS upgrade handled at Bun.serve level
76
- // Static files: serve dist/web/ with SPA fallback
77
- ```
78
-
79
- ### 4. Auth Middleware
80
- - Check `Authorization: Bearer <token>` header
81
- - Skip auth if `config.auth.enabled === false`
82
- - Return 401 on failure
83
-
84
- **Auth flow (minimal):**
85
- - Token stored in `ppm.yaml` → `auth.token` field
86
- - Generated on `ppm init` (random 32-char hex)
87
- - Browser: on first visit, if `auth.enabled === true`, show password input screen
88
- - User enters token → stored in localStorage → sent as Bearer header on all API calls
89
- - No user/password DB — single shared token from config
90
- - Config example:
91
- ```yaml
92
- auth:
93
- enabled: true
94
- token: "a1b2c3..." # auto-generated, user can change
95
- ```
96
-
97
- **Frontend auth screen** (`src/web/components/auth/login-screen.tsx`):
98
- - Full-screen centered card: "Enter Access Token" + input + submit
99
- - On submit: store token in localStorage, redirect to app
100
- - On 401 from any API call: clear localStorage, show login screen
101
-
102
- ### 5. CLI Commands
103
- - `ppm init` — interactive: scan parent dir for .git, prompt to add, create config
104
- - `ppm start` — start Hono server, optionally daemonize (`-d` flag → detach process, write PID to `~/.ppm/ppm.pid`)
105
- - `ppm stop` — read PID file, kill process
106
- - `ppm open` — `open http://localhost:${port}` (macOS) / `xdg-open` (Linux)
107
-
108
- ### 6. Project Resolver
109
- ```typescript
110
- function resolveProject(options: { project?: string }): Project {
111
- if (options.project) return projectService.resolve(options.project);
112
- const cwd = process.cwd();
113
- const match = projectService.list().find(p => cwd.startsWith(p.path));
114
- if (match) return match;
115
- throw new Error('Not in a registered project. Use -p <name>');
116
- }
117
- ```
118
-
119
- ### 7. Shared Project Resolver (`src/server/helpers/resolve-project.ts`)
120
-
121
- **[V2 FIX]** All API routes must resolve project by NAME first, fallback to path:
122
- ```typescript
123
- export function resolveProjectPath(nameOrPath: string): string {
124
- const projects = configService.get("projects");
125
- const byName = projects.find(p => p.name === nameOrPath);
126
- if (byName) return resolve(byName.path);
127
- const abs = resolve(nameOrPath);
128
- const allowed = projects.some(p => abs === p.path || abs.startsWith(p.path + "/"));
129
- if (!allowed) throw new Error(`Project not found: ${nameOrPath}`);
130
- return abs;
131
- }
132
- ```
133
- Import this in every route file (files.ts, git.ts, projects.ts).
134
-
135
- ## Success Criteria
136
-
137
- - [ ] `ppm init` scans current dir, creates config file with auto-generated auth token
138
- - [ ] `ppm start` starts HTTP server on configured port
139
- - [ ] `ppm start -d` runs as daemon, writes PID file; `ppm stop` reads PID and kills process
140
- - [ ] `GET /api/projects` with valid Bearer token returns project list as `{ ok: true, data: [...] }`
141
- - [ ] `GET /api/projects` without token returns 401 `{ ok: false, error: "Unauthorized" }`
142
- - [ ] `GET /api/projects` with `auth.enabled: false` in config returns data without token
143
- - [ ] `ppm open` opens `http://localhost:<port>` in default browser
144
- - [ ] `resolveProjectPath("ppm")` returns correct filesystem path from config
145
- - [ ] `resolveProjectPath("nonexistent")` throws descriptive error
146
- - [ ] Config service loads from `./ppm.yaml` → `~/.ppm/config.yaml` fallback chain
147
- - [ ] Config service creates default config if none exists
148
- - [ ] Static route serves `dist/web/index.html` for all non-API paths (SPA fallback)