@hienlh/ppm 0.7.4 → 0.7.6

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 +13 -0
  2. package/dist/web/assets/{ai-settings-section-CBpXqM-I.js → ai-settings-section-ByRvOONz.js} +1 -1
  3. package/dist/web/assets/{chat-tab-DXtCcWg2.js → chat-tab-ChK0Oj4w.js} +6 -6
  4. package/dist/web/assets/{code-editor-CxMuSJrX.js → code-editor-Bmt1mSYa.js} +1 -1
  5. package/dist/web/assets/columns-2-fz8yNaAo.js +1 -0
  6. package/dist/web/assets/{database-viewer-C6riNG1v.js → database-viewer-Bocsej-g.js} +1 -1
  7. package/dist/web/assets/diff-viewer-BH4xWdR5.js +4 -0
  8. package/dist/web/assets/git-graph-DgzvrrL6.js +1 -0
  9. package/dist/web/assets/{index-B9ZHWVob.js → index-YO_w11fv.js} +4 -4
  10. package/dist/web/assets/{input-BpFvhpT8.js → input-P_K5CUiy.js} +1 -1
  11. package/dist/web/assets/keybindings-store-BydsAw-q.js +1 -0
  12. package/dist/web/assets/{markdown-renderer-DKVNZXEw.js → markdown-renderer-BkXmQhhX.js} +1 -1
  13. package/dist/web/assets/{postgres-viewer-DHvwHGEL.js → postgres-viewer-XQkpsAak.js} +1 -1
  14. package/dist/web/assets/{settings-store-DS-ifJ7c.js → settings-store-2NQzaOVJ.js} +1 -1
  15. package/dist/web/assets/settings-tab-C2zPIPQH.js +1 -0
  16. package/dist/web/assets/{sqlite-viewer-C5Vj_kSU.js → sqlite-viewer-BZroKOjV.js} +1 -1
  17. package/dist/web/assets/{tab-store-D7tRt0VT.js → tab-store-0CKk8cSr.js} +1 -1
  18. package/dist/web/assets/{terminal-tab-B95lVSty.js → terminal-tab-C2IQ6don.js} +2 -2
  19. package/dist/web/assets/{use-monaco-theme-M04jkKDM.js → use-monaco-theme-Bt1Lr3jH.js} +1 -1
  20. package/dist/web/index.html +8 -8
  21. package/dist/web/sw.js +1 -1
  22. package/package.json +1 -1
  23. package/src/server/routes/tunnel.ts +15 -0
  24. package/src/services/claude-usage.service.ts +75 -35
  25. package/src/services/db.service.ts +65 -1
  26. package/src/web/components/editor/diff-viewer.tsx +37 -36
  27. package/dist/web/assets/diff-viewer-CQIockZr.js +0 -4
  28. package/dist/web/assets/git-graph-D2BR2rfP.js +0 -1
  29. package/dist/web/assets/keybindings-store-fcYIcK0C.js +0 -1
  30. package/dist/web/assets/settings-tab-D_quOlcC.js +0 -1
  31. /package/dist/web/assets/{api-client-B0aMOJxF.js → api-client-TUmacMRS.js} +0 -0
  32. /package/dist/web/assets/{dist-QgqOdSYG.js → dist-D9RHR8A4.js} +0 -0
  33. /package/dist/web/assets/{react-Dk7fkoaB.js → react-rgzL83kk.js} +0 -0
  34. /package/dist/web/assets/{table-B6neW6Hr.js → table-C0oSLUYn.js} +0 -0
  35. /package/dist/web/assets/{utils-DBpa1UZX.js → utils-DC-bdPS3.js} +0 -0
package/dist/web/sw.js CHANGED
@@ -1 +1 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"2d6f942c9da6283860d6fd34d33a4539","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/utils-DBpa1UZX.js"},{"revision":null,"url":"assets/use-monaco-theme-M04jkKDM.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/terminal-tab-B95lVSty.js"},{"revision":null,"url":"assets/table-B6neW6Hr.js"},{"revision":null,"url":"assets/tab-store-D7tRt0VT.js"},{"revision":null,"url":"assets/sqlite-viewer-C5Vj_kSU.js"},{"revision":null,"url":"assets/settings-tab-D_quOlcC.js"},{"revision":null,"url":"assets/settings-store-DS-ifJ7c.js"},{"revision":null,"url":"assets/react-Dk7fkoaB.js"},{"revision":null,"url":"assets/react-CYzKIDNi.js"},{"revision":null,"url":"assets/postgres-viewer-DHvwHGEL.js"},{"revision":null,"url":"assets/markdown-renderer-DKVNZXEw.js"},{"revision":null,"url":"assets/keybindings-store-fcYIcK0C.js"},{"revision":null,"url":"assets/jsx-runtime-wQxeESYQ.js"},{"revision":null,"url":"assets/input-BpFvhpT8.js"},{"revision":null,"url":"assets/index-B9ZHWVob.js"},{"revision":null,"url":"assets/index-4pPCbWJp.css"},{"revision":null,"url":"assets/git-graph-D2BR2rfP.js"},{"revision":null,"url":"assets/dist-QgqOdSYG.js"},{"revision":null,"url":"assets/diff-viewer-CQIockZr.js"},{"revision":null,"url":"assets/database-viewer-C6riNG1v.js"},{"revision":null,"url":"assets/code-editor-CxMuSJrX.js"},{"revision":null,"url":"assets/chat-tab-DXtCcWg2.js"},{"revision":null,"url":"assets/api-client-B0aMOJxF.js"},{"revision":null,"url":"assets/ai-settings-section-CBpXqM-I.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
1
+ try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"ec85dff61cef55e131934cc3405aae11","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/utils-DC-bdPS3.js"},{"revision":null,"url":"assets/use-monaco-theme-Bt1Lr3jH.js"},{"revision":null,"url":"assets/terminal-tab-C2IQ6don.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/table-C0oSLUYn.js"},{"revision":null,"url":"assets/tab-store-0CKk8cSr.js"},{"revision":null,"url":"assets/sqlite-viewer-BZroKOjV.js"},{"revision":null,"url":"assets/settings-tab-C2zPIPQH.js"},{"revision":null,"url":"assets/settings-store-2NQzaOVJ.js"},{"revision":null,"url":"assets/react-rgzL83kk.js"},{"revision":null,"url":"assets/react-CYzKIDNi.js"},{"revision":null,"url":"assets/postgres-viewer-XQkpsAak.js"},{"revision":null,"url":"assets/markdown-renderer-BkXmQhhX.js"},{"revision":null,"url":"assets/keybindings-store-BydsAw-q.js"},{"revision":null,"url":"assets/jsx-runtime-wQxeESYQ.js"},{"revision":null,"url":"assets/input-P_K5CUiy.js"},{"revision":null,"url":"assets/index-YO_w11fv.js"},{"revision":null,"url":"assets/index-4pPCbWJp.css"},{"revision":null,"url":"assets/git-graph-DgzvrrL6.js"},{"revision":null,"url":"assets/dist-D9RHR8A4.js"},{"revision":null,"url":"assets/diff-viewer-BH4xWdR5.js"},{"revision":null,"url":"assets/database-viewer-Bocsej-g.js"},{"revision":null,"url":"assets/columns-2-fz8yNaAo.js"},{"revision":null,"url":"assets/code-editor-Bmt1mSYa.js"},{"revision":null,"url":"assets/chat-tab-ChK0Oj4w.js"},{"revision":null,"url":"assets/api-client-TUmacMRS.js"},{"revision":null,"url":"assets/ai-settings-section-ByRvOONz.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -1,9 +1,22 @@
1
1
  import { Hono } from "hono";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { homedir } from "node:os";
2
5
  import { tunnelService } from "../../services/tunnel.service.ts";
3
6
  import { configService } from "../../services/config.service.ts";
4
7
  import { getLocalIp } from "../../lib/network-utils.ts";
5
8
  import { ok, err } from "../../types/api.ts";
6
9
 
10
+ /** Patch shareUrl in status.json so `ppm status` reflects web-started tunnels */
11
+ function patchStatusFile(shareUrl: string | null): void {
12
+ const path = resolve(homedir(), ".ppm", "status.json");
13
+ if (!existsSync(path)) return;
14
+ try {
15
+ const data = JSON.parse(readFileSync(path, "utf-8"));
16
+ writeFileSync(path, JSON.stringify({ ...data, shareUrl }));
17
+ } catch { /* ignore — status.json may be absent in dev */ }
18
+ }
19
+
7
20
  export const tunnelRoutes = new Hono();
8
21
 
9
22
  /** GET /api/tunnel — current tunnel status + local URL */
@@ -25,6 +38,7 @@ tunnelRoutes.post("/start", async (c) => {
25
38
  try {
26
39
  const port = configService.get("port") ?? 8080;
27
40
  const url = await tunnelService.startTunnel(port);
41
+ patchStatusFile(url);
28
42
  return c.json(ok({ url }));
29
43
  } catch (e) {
30
44
  return c.json(err((e as Error).message), 500);
@@ -34,5 +48,6 @@ tunnelRoutes.post("/start", async (c) => {
34
48
  /** POST /api/tunnel/stop — stop tunnel */
35
49
  tunnelRoutes.post("/stop", (c) => {
36
50
  tunnelService.stopTunnel();
51
+ patchStatusFile(null);
37
52
  return c.json(ok({ stopped: true }));
38
53
  });
@@ -1,6 +1,12 @@
1
1
  import { homedir } from "node:os";
2
2
  import { resolve } from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
+ import {
5
+ insertLimitSnapshot,
6
+ getLatestLimitSnapshot,
7
+ cleanupOldLimitSnapshots,
8
+ type LimitSnapshotRow,
9
+ } from "./db.service.ts";
4
10
 
5
11
  export interface LimitBucket {
6
12
  utilization: number;
@@ -25,12 +31,12 @@ const API_URL = "https://api.anthropic.com/api/oauth/usage";
25
31
  const API_BETA = "oauth-2025-04-20";
26
32
  const USER_AGENT = "claude-code/1.0";
27
33
  const FETCH_TIMEOUT = 10_000; // 10s
28
- const POLL_INTERVAL = 60_000; // auto-fetch every 60s
34
+ const POLL_INTERVAL = 120_000; // auto-fetch every 2min
29
35
  const RETRY_DELAY = 5_000; // 5s between retries
30
36
  const MAX_RETRIES = 3;
31
37
 
32
- /** Cached usage data */
33
- let cache: ClaudeUsage = {};
38
+ /** In-memory accumulator for cost from SDK result events */
39
+ let inMemoryCostUsd = 0;
34
40
 
35
41
  /** Cached OAuth token (read once from Keychain/file) */
36
42
  let tokenCache: { token: string; timestamp: number } | null = null;
@@ -118,12 +124,69 @@ function parseApiBucket(raw: Record<string, any>, windowHours: number): LimitBuc
118
124
  };
119
125
  }
120
126
 
121
- /** Fetch with retry logic */
127
+ /** Convert DB snapshot row fields back to a LimitBucket (recomputes time-relative fields) */
128
+ function dbBucketToLimitBucket(util: number, resetsAt: string, windowHours: number): LimitBucket {
129
+ const diff = resetsAt ? new Date(resetsAt).getTime() - Date.now() : 0;
130
+ const totalMins = diff > 0 ? Math.ceil(diff / 60_000) : 0;
131
+ return {
132
+ utilization: util,
133
+ resetsAt,
134
+ resetsInMinutes: windowHours <= 5 ? totalMins : null,
135
+ resetsInHours: windowHours > 5 ? Math.round((totalMins / 60) * 100) / 100 : null,
136
+ windowHours,
137
+ };
138
+ }
139
+
140
+ /** Return ClaudeUsage from the latest DB snapshot + in-memory cost */
141
+ export function getCachedUsage(): ClaudeUsage {
142
+ const row = getLatestLimitSnapshot();
143
+ const result: ClaudeUsage = {};
144
+ if (inMemoryCostUsd > 0) result.totalCostUsd = inMemoryCostUsd;
145
+ if (!row) return result;
146
+ result.lastFetchedAt = row.recorded_at;
147
+ if (row.five_hour_util != null) result.session = dbBucketToLimitBucket(row.five_hour_util, row.five_hour_resets_at ?? "", 5);
148
+ if (row.weekly_util != null) result.weekly = dbBucketToLimitBucket(row.weekly_util, row.weekly_resets_at ?? "", 168);
149
+ if (row.weekly_opus_util != null) result.weeklyOpus = dbBucketToLimitBucket(row.weekly_opus_util, row.weekly_opus_resets_at ?? "", 168);
150
+ if (row.weekly_sonnet_util != null) result.weeklySonnet = dbBucketToLimitBucket(row.weekly_sonnet_util, row.weekly_sonnet_resets_at ?? "", 168);
151
+ return result;
152
+ }
153
+
154
+ /** Check if new API data differs from the last DB snapshot enough to warrant a new row */
155
+ function hasChanged(data: ClaudeUsage, last: LimitSnapshotRow | null): boolean {
156
+ if (!last) return true;
157
+ const diff = (a: number | null | undefined, b: number | null) =>
158
+ a != null && (b == null || Math.abs(a - b) > 0.001);
159
+ if (diff(data.session?.utilization, last.five_hour_util)) return true;
160
+ if (diff(data.weekly?.utilization, last.weekly_util)) return true;
161
+ // Detect window reset (resetsAt changed)
162
+ if (data.session?.resetsAt && data.session.resetsAt !== (last.five_hour_resets_at ?? "")) return true;
163
+ if (data.weekly?.resetsAt && data.weekly.resetsAt !== (last.weekly_resets_at ?? "")) return true;
164
+ return false;
165
+ }
166
+
167
+ /** Persist API data to DB if changed, then cleanup old rows */
168
+ function persistIfChanged(data: ClaudeUsage): void {
169
+ const last = getLatestLimitSnapshot();
170
+ if (!hasChanged(data, last)) return;
171
+ insertLimitSnapshot({
172
+ five_hour_util: data.session?.utilization ?? null,
173
+ five_hour_resets_at: data.session?.resetsAt ?? null,
174
+ weekly_util: data.weekly?.utilization ?? null,
175
+ weekly_resets_at: data.weekly?.resetsAt ?? null,
176
+ weekly_opus_util: data.weeklyOpus?.utilization ?? null,
177
+ weekly_opus_resets_at: data.weeklyOpus?.resetsAt ?? null,
178
+ weekly_sonnet_util: data.weeklySonnet?.utilization ?? null,
179
+ weekly_sonnet_resets_at: data.weeklySonnet?.resetsAt ?? null,
180
+ });
181
+ cleanupOldLimitSnapshots();
182
+ }
183
+
184
+ /** Fetch with retry logic, persist to DB if changed */
122
185
  async function fetchWithRetry(): Promise<void> {
123
186
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
124
187
  try {
125
188
  const data = await fetchUsageFromApi();
126
- cache = data;
189
+ persistIfChanged(data);
127
190
  return;
128
191
  } catch (e) {
129
192
  const msg = (e as Error).message ?? "";
@@ -150,45 +213,22 @@ export function stopUsagePolling(): void {
150
213
  if (pollTimer) { clearInterval(pollTimer); pollTimer = null; }
151
214
  }
152
215
 
153
- /** Get cached usage (fast, synchronous read — FE just reads this) */
154
- export function getCachedUsage(): ClaudeUsage {
155
- return cache;
156
- }
157
-
158
216
  /**
159
- * Merge SDK rate-limit / cost events into the cache so the REST endpoint
160
- * always returns the freshest data even when the OAuth Usage API is unreachable.
217
+ * Merge SDK result cost events into in-memory accumulator.
218
+ * Rate limit utilization from SDK events is ignored API polling is authoritative.
161
219
  */
162
220
  export function updateFromSdkEvent(
163
- rateLimitType?: string,
164
- utilization?: number,
221
+ _rateLimitType?: string,
222
+ _utilization?: number,
165
223
  costUsd?: number,
166
224
  ): void {
167
- if (rateLimitType && utilization != null) {
168
- if (rateLimitType === "five_hour") {
169
- cache.session = {
170
- ...(cache.session ?? { resetsAt: "", resetsInMinutes: null, resetsInHours: null, windowHours: 5 }),
171
- utilization,
172
- };
173
- } else if (rateLimitType.startsWith("seven_day")) {
174
- const key: keyof ClaudeUsage =
175
- rateLimitType === "seven_day_opus" ? "weeklyOpus"
176
- : rateLimitType === "seven_day_sonnet" ? "weeklySonnet"
177
- : "weekly";
178
- cache[key] = {
179
- ...(cache[key] as LimitBucket ?? { resetsAt: "", resetsInMinutes: null, resetsInHours: null, windowHours: 168 }),
180
- utilization,
181
- };
182
- }
183
- if (!cache.lastFetchedAt) cache.lastFetchedAt = new Date().toISOString();
184
- }
185
225
  if (costUsd != null) {
186
- cache.totalCostUsd = (cache.totalCostUsd ?? 0) + costUsd;
226
+ inMemoryCostUsd += costUsd;
187
227
  }
188
228
  }
189
229
 
190
- /** Force immediate refresh (e.g. after a chat completes) */
230
+ /** Force immediate refresh from Anthropic API, persist to DB, return latest */
191
231
  export async function refreshUsageNow(): Promise<ClaudeUsage> {
192
232
  await fetchWithRetry();
193
- return cache;
233
+ return getCachedUsage();
194
234
  }
@@ -4,7 +4,7 @@ import { homedir } from "node:os";
4
4
  import { mkdirSync, existsSync } from "node:fs";
5
5
 
6
6
  const PPM_DIR = resolve(homedir(), ".ppm");
7
- const CURRENT_SCHEMA_VERSION = 3;
7
+ const CURRENT_SCHEMA_VERSION = 4;
8
8
 
9
9
  let db: Database | null = null;
10
10
  let dbProfile: string | null = null;
@@ -167,6 +167,27 @@ function runMigrations(database: Database): void {
167
167
  PRAGMA user_version = 3;
168
168
  `);
169
169
  }
170
+
171
+ if (current < 4) {
172
+ database.exec(`
173
+ CREATE TABLE IF NOT EXISTS claude_limit_snapshots (
174
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
175
+ five_hour_util REAL,
176
+ five_hour_resets_at TEXT,
177
+ weekly_util REAL,
178
+ weekly_resets_at TEXT,
179
+ weekly_opus_util REAL,
180
+ weekly_opus_resets_at TEXT,
181
+ weekly_sonnet_util REAL,
182
+ weekly_sonnet_resets_at TEXT,
183
+ recorded_at TEXT DEFAULT (datetime('now'))
184
+ );
185
+
186
+ CREATE INDEX IF NOT EXISTS idx_limit_snapshots_recorded ON claude_limit_snapshots(recorded_at);
187
+
188
+ PRAGMA user_version = 4;
189
+ `);
190
+ }
170
191
  }
171
192
 
172
193
  // ---------------------------------------------------------------------------
@@ -345,6 +366,49 @@ export function getDbFilePath(): string {
345
366
  return getDbPath();
346
367
  }
347
368
 
369
+ // ---------------------------------------------------------------------------
370
+ // Claude limit snapshot helpers
371
+ // ---------------------------------------------------------------------------
372
+
373
+ export interface LimitSnapshotRow {
374
+ id: number;
375
+ five_hour_util: number | null;
376
+ five_hour_resets_at: string | null;
377
+ weekly_util: number | null;
378
+ weekly_resets_at: string | null;
379
+ weekly_opus_util: number | null;
380
+ weekly_opus_resets_at: string | null;
381
+ weekly_sonnet_util: number | null;
382
+ weekly_sonnet_resets_at: string | null;
383
+ recorded_at: string;
384
+ }
385
+
386
+ export function insertLimitSnapshot(data: Omit<LimitSnapshotRow, "id" | "recorded_at">): void {
387
+ getDb().query(
388
+ `INSERT INTO claude_limit_snapshots
389
+ (five_hour_util, five_hour_resets_at, weekly_util, weekly_resets_at,
390
+ weekly_opus_util, weekly_opus_resets_at, weekly_sonnet_util, weekly_sonnet_resets_at)
391
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
392
+ ).run(
393
+ data.five_hour_util ?? null, data.five_hour_resets_at ?? null,
394
+ data.weekly_util ?? null, data.weekly_resets_at ?? null,
395
+ data.weekly_opus_util ?? null, data.weekly_opus_resets_at ?? null,
396
+ data.weekly_sonnet_util ?? null, data.weekly_sonnet_resets_at ?? null,
397
+ );
398
+ }
399
+
400
+ export function getLatestLimitSnapshot(): LimitSnapshotRow | null {
401
+ return getDb().query(
402
+ "SELECT * FROM claude_limit_snapshots ORDER BY recorded_at DESC LIMIT 1",
403
+ ).get() as LimitSnapshotRow | null;
404
+ }
405
+
406
+ export function cleanupOldLimitSnapshots(): void {
407
+ getDb().query(
408
+ "DELETE FROM claude_limit_snapshots WHERE recorded_at < datetime('now', '-7 days')",
409
+ ).run();
410
+ }
411
+
348
412
  // ---------------------------------------------------------------------------
349
413
  // Connection helpers
350
414
  // ---------------------------------------------------------------------------
@@ -133,50 +133,51 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
133
133
  );
134
134
  }
135
135
 
136
- if (!isInline && !isFileCompare && (!diffText || diffText.trim() === "") && !original && !modified) {
136
+ // Catch diffs with metadata-only changes (mode, rename) where parseDiff returns empty
137
+ if (!isInline && !isFileCompare && !original && !modified) {
137
138
  return (
138
139
  <div className="flex flex-col items-center justify-center h-full gap-2 text-muted-foreground">
139
140
  <FileCode className="size-8" />
140
- <p className="text-sm">No changes detected</p>
141
+ <p className="text-sm">No content changes</p>
141
142
  {filePath && <p className="text-xs font-mono">{filePath}</p>}
142
143
  </div>
143
144
  );
144
145
  }
145
146
 
146
- const expandToggle = (
147
- <div className="flex items-center gap-0.5 shrink-0">
148
- <button type="button"
149
- onClick={() => setExpandMode(expandMode === "left" ? "both" : "left")}
150
- className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "left" ? "bg-muted text-foreground" : ""}`}
151
- title="Expand original"
152
- >
153
- <PanelLeftOpen className="size-3.5" />
154
- </button>
155
- <button type="button"
156
- onClick={() => setExpandMode("both")}
157
- className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "both" ? "bg-muted text-foreground" : ""}`}
158
- title="Side by side"
159
- >
160
- <Columns2 className="size-3.5" />
161
- </button>
162
- <button type="button"
163
- onClick={() => setExpandMode(expandMode === "right" ? "both" : "right")}
164
- className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "right" ? "bg-muted text-foreground" : ""}`}
165
- title="Expand modified"
166
- >
167
- <PanelRightOpen className="size-3.5" />
168
- </button>
169
- <div className="w-px h-3.5 bg-border mx-0.5 shrink-0" />
170
- <button type="button" onClick={toggleWordWrap} title="Toggle word wrap"
171
- className={`p-1 rounded hover:bg-muted transition-colors ${wordWrap ? "bg-muted text-foreground" : ""}`}
172
- >
173
- <WrapText className="size-3.5" />
174
- </button>
175
- </div>
176
- );
177
-
178
147
  return (
179
148
  <div className="flex flex-col h-full">
149
+ {/* Toolbar */}
150
+ {!isMobile && (
151
+ <div className="flex items-center justify-end gap-0.5 px-2 py-0.5 border-b border-border shrink-0">
152
+ <button type="button"
153
+ onClick={() => setExpandMode(expandMode === "left" ? "both" : "left")}
154
+ className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "left" ? "bg-muted text-foreground" : ""}`}
155
+ title="Expand original"
156
+ >
157
+ <PanelLeftOpen className="size-3.5" />
158
+ </button>
159
+ <button type="button"
160
+ onClick={() => setExpandMode("both")}
161
+ className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "both" ? "bg-muted text-foreground" : ""}`}
162
+ title="Side by side"
163
+ >
164
+ <Columns2 className="size-3.5" />
165
+ </button>
166
+ <button type="button"
167
+ onClick={() => setExpandMode(expandMode === "right" ? "both" : "right")}
168
+ className={`p-1 rounded hover:bg-muted transition-colors ${expandMode === "right" ? "bg-muted text-foreground" : ""}`}
169
+ title="Expand modified"
170
+ >
171
+ <PanelRightOpen className="size-3.5" />
172
+ </button>
173
+ <div className="w-px h-3.5 bg-border mx-0.5 shrink-0" />
174
+ <button type="button" onClick={toggleWordWrap} title="Toggle word wrap"
175
+ className={`p-1 rounded hover:bg-muted transition-colors ${wordWrap ? "bg-muted text-foreground" : ""}`}
176
+ >
177
+ <WrapText className="size-3.5" />
178
+ </button>
179
+ </div>
180
+ )}
180
181
  {/* Monaco DiffEditor */}
181
182
  <div ref={containerRef} className="flex-1 overflow-hidden">
182
183
  {containerHeight && containerHeight > 0 ? (
@@ -195,11 +196,11 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
195
196
  automaticLayout: true,
196
197
  scrollBeyondLastLine: false,
197
198
  }}
198
- loading={<Loader2 className="size-5 animate-spin text-text-subtle" />}
199
+ loading={<Loader2 className="size-5 animate-spin text-muted-foreground" />}
199
200
  />
200
201
  ) : (
201
202
  <div className="flex items-center justify-center h-full">
202
- <Loader2 className="size-5 animate-spin text-text-subtle" />
203
+ <Loader2 className="size-5 animate-spin text-muted-foreground" />
203
204
  </div>
204
205
  )}
205
206
  </div>
@@ -1,4 +0,0 @@
1
- import{i as e,t}from"./react-CYzKIDNi.js";import{t as n}from"./jsx-runtime-wQxeESYQ.js";import{n as r}from"./settings-store-DS-ifJ7c.js";import{i,t as a}from"./api-client-B0aMOJxF.js";import{A as o,I as s}from"./index-B9ZHWVob.js";import{r as c,t as l}from"./use-monaco-theme-M04jkKDM.js";var u=e(t(),1),d=n();function f(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`}[e.split(`.`).pop()?.toLowerCase()??``]??`plaintext`}function p({metadata:e}){let t=e?.filePath,n=e?.projectName,p=e?.ref1,h=e?.ref2,g=e?.file1,_=e?.file2,v=e?.original,y=e?.modified,b=v!=null||y!=null,x=!!(g&&_),[S,C]=(0,u.useState)(null),[w,T]=(0,u.useState)(null),[E,D]=(0,u.useState)(!b),[O,k]=(0,u.useState)(null),[A,j]=(0,u.useState)(`both`),{wordWrap:M,toggleWordWrap:N}=r(),P=l(),F=(0,u.useRef)(null),[I,L]=(0,u.useState)();(0,u.useEffect)(()=>{let e=F.current;if(!e)return;let t=new ResizeObserver(([e])=>{e&&L(Math.floor(e.contentRect.height))});return t.observe(e),()=>t.disconnect()},[]),(0,u.useEffect)(()=>{if(b||!n)return;if(D(!0),k(null),g&&_){let e=new URLSearchParams({file1:g,file2:_});a.get(`${i(n)}/files/compare?${e}`).then(e=>{T(e),D(!1)}).catch(e=>{k(e instanceof Error?e.message:`Failed to compare files`),D(!1)});return}let e;if(t){let r=new URLSearchParams({file:t});p&&r.set(`ref`,p),e=`${i(n)}/git/file-diff?${r}`}else if(p||h){let t=new URLSearchParams;p&&t.set(`ref1`,p),h&&t.set(`ref2`,h),e=`${i(n)}/git/diff?${t}`}else e=`${i(n)}/git/diff`;a.get(e).then(e=>{C(e.diff),D(!1)}).catch(e=>{k(e instanceof Error?e.message:`Failed to load diff`),D(!1)})},[t,n,p,h,g,_,b]);let{original:R,modified:z}=(0,u.useMemo)(()=>b?{original:v??``,modified:y??``}:x&&w?w:S?m(S):{original:``,modified:``},[S,b,v,y,x,w]),B=(0,u.useMemo)(()=>{let e=t??_??g;return e?f(e):`plaintext`},[t,g,_]),V=typeof window<`u`&&window.innerWidth<768,H=!V&&A===`both`;return!n&&!b?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground text-sm`,children:`No project selected.`}):E?(0,d.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-muted-foreground`,children:[(0,d.jsx)(o,{className:`size-5 animate-spin`}),(0,d.jsx)(`span`,{className:`text-sm`,children:`Loading diff...`})]}):O?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full text-destructive text-sm`,children:O}):!b&&!x&&(!S||S.trim()===``)&&!R&&!z?(0,d.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-2 text-muted-foreground`,children:[(0,d.jsx)(s,{className:`size-8`}),(0,d.jsx)(`p`,{className:`text-sm`,children:`No changes detected`}),t&&(0,d.jsx)(`p`,{className:`text-xs font-mono`,children:t})]}):(0,d.jsx)(`div`,{className:`flex flex-col h-full`,children:(0,d.jsx)(`div`,{ref:F,className:`flex-1 overflow-hidden`,children:I&&I>0?(0,d.jsx)(c,{height:I,language:B,original:R,modified:z,theme:P,options:{fontSize:V?11:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:V||M?`on`:`off`,renderSideBySide:H,readOnly:!0,automaticLayout:!0,scrollBeyondLastLine:!1},loading:(0,d.jsx)(o,{className:`size-5 animate-spin text-text-subtle`})}):(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,d.jsx)(o,{className:`size-5 animate-spin text-text-subtle`})})})})}function m(e){let t=e.split(`
2
- `),n=[],r=[],i=!1;for(let e of t)if(!(e.startsWith(`diff --git`)||e.startsWith(`diff --no-index`)||e.startsWith(`index `)||e.startsWith(`new file`)||e.startsWith(`deleted file`)||e.startsWith(`old mode`)||e.startsWith(`new mode`)||e.startsWith(`---`)||e.startsWith(`+++`)||e.startsWith(`Binary files`)||e.startsWith(`\\ No newline`))){if(e.startsWith(`@@`)){i=!0;continue}if(i)if(e.startsWith(`-`))n.push(e.slice(1));else if(e.startsWith(`+`))r.push(e.slice(1));else{let t=e.startsWith(` `)?e.slice(1):e;n.push(t),r.push(t)}}return{original:n.join(`
3
- `),modified:r.join(`
4
- `)}}export{p as DiffViewer};
@@ -1 +0,0 @@
1
- import{i as e,t}from"./react-CYzKIDNi.js";import{A as n,t as r}from"./input-BpFvhpT8.js";import{n as i,t as a}from"./jsx-runtime-wQxeESYQ.js";import{a as o,t as s}from"./tab-store-D7tRt0VT.js";import{t as c}from"./utils-DBpa1UZX.js";import{i as l,t as u}from"./api-client-B0aMOJxF.js";import{A as d,D as f,M as p,R as m,W as h,a as g,c as _,d as v,f as y,i as ee,j as te,l as b,o as ne,p as x,r as re,s as ie,u as S,w as C}from"./index-B9ZHWVob.js";var ae=i(`cherry`,[[`path`,{d:`M2 17a5 5 0 0 0 10 0c0-2.76-2.5-5-5-3-2.5-2-5 .24-5 3Z`,key:`cvxqlc`}],[`path`,{d:`M12 17a5 5 0 0 0 10 0c0-2.76-2.5-5-5-3-2.5-2-5 .24-5 3Z`,key:`1ostrc`}],[`path`,{d:`M7 14c3.22-2.91 4.29-8.75 5-12 1.66 2.38 4.94 9 5 12`,key:`hqx58h`}],[`path`,{d:`M22 9c-4.29 0-7.14-2.33-10-7 5.71 0 10 4.67 10 7Z`,key:`eykp1o`}]]),w=i(`git-merge`,[[`circle`,{cx:`18`,cy:`18`,r:`3`,key:`1xkwt0`}],[`circle`,{cx:`6`,cy:`6`,r:`3`,key:`1lh9wr`}],[`path`,{d:`M6 21V9a9 9 0 0 0 9 9`,key:`7kw0sc`}]]),T=i(`tag`,[[`path`,{d:`M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z`,key:`vktsd0`}],[`circle`,{cx:`7.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`kqv944`}]]),E=e(t(),1),D=a(),O=[`#4fc3f7`,`#81c784`,`#ffb74d`,`#e57373`,`#ba68c8`,`#4dd0e1`,`#aed581`,`#ff8a65`,`#f06292`,`#7986cb`],k=32,A=20,oe=5;function j({metadata:e}){let t=e?.projectName,[i,a]=(0,E.useState)(null),[o,h]=(0,E.useState)(!0),[C,w]=(0,E.useState)(null),[j,M]=(0,E.useState)(!1),[N,P]=(0,E.useState)({type:null}),[F,I]=(0,E.useState)(``),[L,R]=(0,E.useState)(null),[z,B]=(0,E.useState)([]),[ce,V]=(0,E.useState)(!1),{openTab:H}=s(),U=(0,E.useCallback)(async()=>{if(t)try{h(!0),a(await u.get(`${l(t)}/git/graph?max=200`)),w(null)}catch(e){w(e instanceof Error?e.message:`Failed to fetch graph`)}finally{h(!1)}},[t]);(0,E.useEffect)(()=>{U();let e=setInterval(U,1e4);return()=>clearInterval(e)},[U]);let W=async(e,n)=>{if(t){M(!0);try{await u.post(`${l(t)}${e}`,n),await U()}catch(e){w(e instanceof Error?e.message:`Action failed`)}finally{M(!1)}}},G=e=>W(`/git/checkout`,{ref:e}),le=e=>W(`/git/cherry-pick`,{hash:e}),ue=e=>W(`/git/revert`,{hash:e}),de=e=>W(`/git/merge`,{source:e}),fe=e=>W(`/git/branch/delete`,{name:e}),pe=e=>W(`/git/push`,{branch:e}),K=async(e,t)=>{if(i?.branches.some(t=>t.name===e||t.name.endsWith(`/${e}`))){if(!window.confirm(`Branch "${e}" already exists.\nDelete it and recreate from this commit?`))return;await W(`/git/branch/delete`,{name:e})}await W(`/git/branch/create`,{name:e,from:t})},q=(e,t)=>W(`/git/tag`,{name:e,hash:t}),me=async e=>{if(t)try{let n=await u.get(`${l(t)}/git/pr-url?branch=${encodeURIComponent(e)}`);n.url&&window.open(n.url,`_blank`)}catch{}},J=e=>{navigator.clipboard.writeText(e)},he=async e=>{if(L?.hash===e.hash){R(null);return}R(e),V(!0);try{let n=e.parents[0]??``,r=n?`ref1=${encodeURIComponent(n)}&`:``,i=await u.get(`${l(t)}/git/diff-stat?${r}ref2=${encodeURIComponent(e.hash)}`);B(Array.isArray(i)?i:[])}catch(e){console.error(`diff-stat error:`,e),B([])}finally{V(!1)}},ge=e=>{let n=e.parents[0];H({type:`git-diff`,title:`Diff ${e.abbreviatedHash}`,closable:!0,metadata:{projectName:t,ref1:n??void 0,ref2:e.hash},projectId:t??null})},{laneMap:Y,maxLane:_e}=(0,E.useMemo)(()=>{let e=new Map;if(!i)return{laneMap:e,maxLane:0};let t=0,n=new Map;for(let r of i.commits){let i=n.get(r.hash);i===void 0&&(i=t++),e.set(r.hash,i),n.delete(r.hash);for(let e=0;e<r.parents.length;e++){let a=r.parents[e];n.has(a)||n.set(a,e===0?i:t++)}}return{laneMap:e,maxLane:Math.max(t-1,0)}},[i]),X=i?.branches.find(e=>e.current),ve=(0,E.useMemo)(()=>{let e=new Map;if(!i)return e;for(let t of i.branches){let n=e.get(t.commitHash)??[];n.push({name:t.name,type:`branch`}),e.set(t.commitHash,n)}for(let t of i.commits)for(let n of t.refs)if(n.startsWith(`tag: `)){let r=n.replace(`tag: `,``),i=e.get(t.hash)??[];i.push({name:r,type:`tag`}),e.set(t.hash,i)}return e},[i]),ye=(0,E.useMemo)(()=>{if(!i)return[];let e=[];for(let t=0;t<i.commits.length;t++){let n=i.commits[t],r=Y.get(n.hash)??0,a=O[r%O.length];for(let o of n.parents){let s=i.commits.findIndex(e=>e.hash===o);if(s<0)continue;let c=Y.get(o)??0,l=O[c%O.length],u=r*A+A/2,d=t*k+k/2,f=c*A+A/2,p=s*k+k/2,m,h=n.parents.indexOf(o)>0;if(u===f)m=`M ${u} ${d} L ${f} ${p}`;else if(h){let e=d+k;m=`M ${u} ${d} C ${u} ${e} ${f} ${d} ${f} ${e} L ${f} ${p}`}else{let e=p-k;m=`M ${u} ${d} L ${u} ${e} C ${u} ${p} ${f} ${e} ${f} ${p}`}let g=n.parents.indexOf(o)===0?a:l;e.push({d:m,color:g})}}return e},[i,Y]);(_e+1)*A+A;let be=(i?.commits.length??0)*k,[Z,xe]=(0,E.useState)((typeof window<`u`&&window.innerWidth<768?6:10)*A+A),Q=(0,E.useRef)(!1),$=(0,E.useCallback)(e=>{Q.current=!0;let t=Z,n=n=>{if(!Q.current)return;let r=`touches`in n?n.touches[0].clientX:n.clientX;xe(Math.max(40,t+r-e))},r=()=>{Q.current=!1,window.removeEventListener(`mousemove`,n),window.removeEventListener(`mouseup`,r),window.removeEventListener(`touchmove`,n),window.removeEventListener(`touchend`,r)};window.addEventListener(`mousemove`,n),window.addEventListener(`mouseup`,r),window.addEventListener(`touchmove`,n,{passive:!1}),window.addEventListener(`touchend`,r)},[Z]),Se=(0,E.useCallback)(e=>{e.preventDefault(),$(e.clientX)},[$]),Ce=(0,E.useCallback)(e=>{$(e.touches[0].clientX)},[$]);if(!t)return(0,D.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground text-sm`,children:`No project selected.`});if(o&&!i)return(0,D.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-muted-foreground`,children:[(0,D.jsx)(d,{className:`size-5 animate-spin`}),(0,D.jsx)(`span`,{className:`text-sm`,children:`Loading git graph...`})]});if(C&&!i)return(0,D.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-2 text-destructive text-sm`,children:[(0,D.jsx)(`p`,{children:C}),(0,D.jsx)(_,{variant:`outline`,size:`sm`,onClick:U,children:`Retry`})]});function we(e){let t=new Date(e),n=new Date().getTime()-t.getTime(),r=Math.floor(n/6e4);if(r<1)return`just now`;if(r<60)return`${r}m ago`;let i=Math.floor(r/60);if(i<24)return`${i}h ago`;let a=Math.floor(i/24);if(a<30)return`${a}d ago`;let o=Math.floor(a/30);return o<12?`${o}mo ago`:`${Math.floor(o/12)}y ago`}return(0,D.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,D.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-2 border-b`,children:[(0,D.jsxs)(`span`,{className:`text-sm font-medium`,children:[`Git Graph`,X?` - ${X.name}`:``]}),(0,D.jsx)(_,{variant:`ghost`,size:`icon-xs`,onClick:U,disabled:j,children:(0,D.jsx)(f,{className:o?`animate-spin`:``})})]}),C&&(0,D.jsx)(`div`,{className:`px-3 py-1.5 text-xs text-destructive bg-destructive/10`,children:C}),(0,D.jsx)(`div`,{className:`flex-1 overflow-y-auto overflow-x-auto md:overflow-x-hidden`,children:(0,D.jsxs)(`div`,{className:`flex min-w-max md:min-w-0`,style:{height:`${be}px`},children:[(0,D.jsxs)(`div`,{className:`sticky left-0 z-10 shrink-0 bg-background`,style:{width:`${Z}px`},children:[(0,D.jsxs)(`svg`,{width:Z,height:be,children:[ye.map((e,t)=>(0,D.jsx)(`path`,{d:e.d,stroke:e.color,strokeWidth:2,fill:`none`},t)),i?.commits.map((e,t)=>{let n=Y.get(e.hash)??0,r=n*A+A/2,i=t*k+k/2,a=O[n%O.length];return(0,D.jsx)(`circle`,{cx:r,cy:i,r:oe,fill:a,stroke:`#0f1419`,strokeWidth:2},e.hash)})]}),(0,D.jsx)(`div`,{className:`absolute top-0 right-0 w-3 md:w-2 h-full cursor-col-resize hover:bg-primary/20 flex items-center justify-center bg-primary/10 md:bg-transparent`,onMouseDown:Se,onTouchStart:Ce,children:(0,D.jsx)(te,{className:`size-3 text-muted-foreground md:opacity-0 md:hover:opacity-100`})})]}),(0,D.jsx)(`div`,{className:`flex-1 min-w-[400px]`,children:i?.commits.map((e,t)=>{let r=O[(Y.get(e.hash)??0)%O.length],i=ve.get(e.hash)??[],a=i.filter(e=>e.type===`branch`),o=i.filter(e=>e.type===`tag`);return(0,D.jsxs)(b,{children:[(0,D.jsx)(x,{asChild:!0,children:(0,D.jsx)(`div`,{className:`flex items-center hover:bg-muted/50 cursor-pointer text-sm border-b border-border/30 ${L?.hash===e.hash?`bg-primary/10`:``}`,style:{height:`${k}px`},onClick:()=>he(e),children:(0,D.jsxs)(`div`,{className:`flex items-center gap-2 flex-1 min-w-0 px-2`,children:[(0,D.jsx)(`span`,{className:`font-mono text-xs text-muted-foreground w-14 shrink-0`,children:e.abbreviatedHash}),a.map(e=>(0,D.jsx)(se,{label:e,color:r,currentBranch:X,onCheckout:G,onMerge:de,onPush:pe,onCreatePr:me,onDelete:fe},`branch-${e.name}`)),o.map(e=>(0,D.jsxs)(`span`,{className:`inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0 bg-amber-500/20 text-amber-500 border border-amber-500/30`,children:[(0,D.jsx)(T,{className:`size-2.5`}),e.name]},`tag-${e.name}`)),(0,D.jsx)(`span`,{className:`flex-1 truncate`,children:e.subject}),(0,D.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0 hidden sm:inline`,children:e.authorName}),(0,D.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0 w-14 text-right`,children:we(e.authorDate)})]})})}),(0,D.jsxs)(S,{children:[(0,D.jsx)(v,{onClick:()=>G(e.hash),children:`Checkout`}),(0,D.jsxs)(v,{onClick:()=>{P({type:`branch`,hash:e.hash}),I(``)},children:[(0,D.jsx)(p,{className:`size-3`}),`Create Branch...`]}),(0,D.jsx)(y,{}),(0,D.jsxs)(v,{onClick:()=>le(e.hash),children:[(0,D.jsx)(ae,{className:`size-3`}),`Cherry Pick`]}),(0,D.jsxs)(v,{onClick:()=>ue(e.hash),children:[(0,D.jsx)(n,{className:`size-3`}),`Revert`]}),(0,D.jsxs)(v,{onClick:()=>{P({type:`tag`,hash:e.hash}),I(``)},children:[(0,D.jsx)(T,{className:`size-3`}),`Create Tag...`]}),(0,D.jsx)(y,{}),(0,D.jsx)(v,{onClick:()=>ge(e),children:`View Diff`}),(0,D.jsxs)(v,{onClick:()=>J(e.hash),children:[(0,D.jsx)(m,{className:`size-3`}),`Copy Hash`]})]})]},e.hash)})})]})}),L&&(0,D.jsxs)(`div`,{className:`border-t bg-muted/30 max-h-[40%] overflow-auto`,children:[(0,D.jsxs)(`div`,{className:`px-3 py-2 border-b flex items-center justify-between`,children:[(0,D.jsxs)(`span`,{className:`text-sm font-medium truncate`,children:[L.abbreviatedHash,` — `,L.subject]}),(0,D.jsx)(_,{variant:`ghost`,size:`icon-xs`,onClick:()=>R(null),children:`✕`})]}),(0,D.jsxs)(`div`,{className:`px-3 py-2 text-xs space-y-1`,children:[(0,D.jsxs)(`div`,{className:`flex gap-4`,children:[(0,D.jsx)(`span`,{className:`text-muted-foreground`,children:`Author`}),(0,D.jsxs)(`span`,{children:[L.authorName,` <`,L.authorEmail,`>`]})]}),(0,D.jsxs)(`div`,{className:`flex gap-4`,children:[(0,D.jsx)(`span`,{className:`text-muted-foreground`,children:`Date`}),(0,D.jsx)(`span`,{children:new Date(L.authorDate).toLocaleString()})]}),(0,D.jsxs)(`div`,{className:`flex gap-4`,children:[(0,D.jsx)(`span`,{className:`text-muted-foreground`,children:`Hash`}),(0,D.jsx)(`span`,{className:`font-mono cursor-pointer hover:text-primary`,onClick:()=>J(L.hash),children:L.hash})]}),L.parents.length>0&&(0,D.jsxs)(`div`,{className:`flex gap-4`,children:[(0,D.jsx)(`span`,{className:`text-muted-foreground`,children:`Parents`}),(0,D.jsx)(`span`,{className:`font-mono`,children:L.parents.map(e=>e.slice(0,7)).join(`, `)})]}),L.body&&(0,D.jsx)(`div`,{className:`mt-2 p-2 bg-background rounded text-xs whitespace-pre-wrap`,children:L.body})]}),(0,D.jsxs)(`div`,{className:`px-3 py-1 border-t`,children:[(0,D.jsx)(`div`,{className:`text-xs text-muted-foreground py-1`,children:ce?`Loading files...`:`${z.length} file${z.length===1?``:`s`} changed`}),z.map(e=>(0,D.jsxs)(`div`,{className:`flex items-center gap-2 py-0.5 text-xs hover:bg-muted/50 rounded px-1 cursor-pointer`,onClick:()=>H({type:`git-diff`,title:`Diff ${c(e.path)}`,closable:!0,metadata:{projectName:t,ref1:L.parents[0]??void 0,ref2:L.hash,filePath:e.path},projectId:t??null}),children:[(0,D.jsx)(`span`,{className:`flex-1 truncate font-mono`,children:e.path}),e.additions>0&&(0,D.jsxs)(`span`,{className:`text-green-500`,children:[`+`,e.additions]}),e.deletions>0&&(0,D.jsxs)(`span`,{className:`text-red-500`,children:[`-`,e.deletions]})]},e.path))]})]}),(0,D.jsx)(re,{open:N.type!==null,onOpenChange:e=>{e||P({type:null})},children:(0,D.jsxs)(ee,{children:[(0,D.jsx)(ne,{children:(0,D.jsx)(ie,{children:N.type===`branch`?`Create Branch`:`Create Tag`})}),(0,D.jsx)(r,{placeholder:N.type===`branch`?`Branch name`:`Tag name`,value:F,onChange:e=>I(e.target.value),onKeyDown:e=>{e.key===`Enter`&&F.trim()&&(N.type===`branch`?K(F.trim(),N.hash):q(F.trim(),N.hash),P({type:null}))},autoFocus:!0}),(0,D.jsxs)(g,{children:[(0,D.jsx)(_,{variant:`outline`,onClick:()=>P({type:null}),children:`Cancel`}),(0,D.jsx)(_,{disabled:!F.trim(),onClick:()=>{N.type===`branch`?K(F.trim(),N.hash):q(F.trim(),N.hash),P({type:null})},children:`Create`})]})]})})]})}function se({label:e,color:t,currentBranch:n,onCheckout:r,onMerge:i,onPush:a,onCreatePr:s,onDelete:c}){return(0,D.jsxs)(b,{children:[(0,D.jsx)(x,{asChild:!0,children:(0,D.jsxs)(`span`,{className:`inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0 cursor-context-menu`,style:{backgroundColor:`${t}30`,color:t,border:`1px solid ${t}50`},children:[(0,D.jsx)(p,{className:`size-2.5`}),e.name]})}),(0,D.jsxs)(S,{children:[(0,D.jsx)(v,{onClick:()=>r(e.name),children:`Checkout`}),(0,D.jsxs)(v,{onClick:()=>i(e.name),disabled:e.name===n?.name,children:[(0,D.jsx)(w,{className:`size-3`}),`Merge into current`]}),(0,D.jsx)(y,{}),(0,D.jsxs)(v,{onClick:()=>a(e.name),children:[(0,D.jsx)(h,{className:`size-3`}),`Push`]}),(0,D.jsxs)(v,{onClick:()=>s(e.name),children:[(0,D.jsx)(o,{className:`size-3`}),`Create PR`]}),(0,D.jsx)(y,{}),(0,D.jsxs)(v,{variant:`destructive`,onClick:()=>c(e.name),disabled:e.name===n?.name,children:[(0,D.jsx)(C,{className:`size-3`}),`Delete`]})]})]})}export{j as GitGraph};
@@ -1 +0,0 @@
1
- import"./react-CYzKIDNi.js";import"./api-client-B0aMOJxF.js";import{x as e}from"./index-B9ZHWVob.js";export{e as useKeybindingsStore};
@@ -1 +0,0 @@
1
- import"./react-CYzKIDNi.js";import"./input-BpFvhpT8.js";import"./jsx-runtime-wQxeESYQ.js";import"./ai-settings-section-CBpXqM-I.js";import"./settings-store-DS-ifJ7c.js";import"./api-client-B0aMOJxF.js";import{n as e}from"./index-B9ZHWVob.js";export{e as SettingsTab};