@hienlh/ppm 0.8.69 → 0.8.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/web/assets/{browser-tab-BhnA1gi8.js → browser-tab-D5GfU4Ja.js} +1 -1
- package/dist/web/assets/chat-tab-BJeNwwUM.js +8 -0
- package/dist/web/assets/code-editor-CTjgdXh2.js +2 -0
- package/dist/web/assets/{database-viewer-ngGR7s3t.js → database-viewer-QzEuetE6.js} +1 -1
- package/dist/web/assets/{diff-viewer-C281t_Li.js → diff-viewer-CvZ06EAH.js} +1 -1
- package/dist/web/assets/{git-graph-DxbU6ljU.js → git-graph-BQqdvSjX.js} +1 -1
- package/dist/web/assets/index-5a-tMkk5.js +37 -0
- package/dist/web/assets/index-CzwYVupc.css +2 -0
- package/dist/web/assets/keybindings-store-zY8zbJ2c.js +1 -0
- package/dist/web/assets/{markdown-renderer-DaS9de_M.js → markdown-renderer-BVxlq4zO.js} +1 -1
- package/dist/web/assets/{postgres-viewer-ck2oHZND.js → postgres-viewer-DP0FOQOa.js} +1 -1
- package/dist/web/assets/{settings-tab-D1LdiVy8.js → settings-tab-CcmhnYpw.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-l4eyjI6C.js → sqlite-viewer-4a4hHLZk.js} +1 -1
- package/dist/web/assets/{terminal-tab-BnA-xDvX.js → terminal-tab-CKsBIgnq.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-DsGrkXnv.js → use-monaco-theme-BwIb9BHq.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/server/routes/chat.ts +33 -3
- package/src/services/db.service.ts +31 -1
- package/src/types/chat.ts +1 -0
- package/src/web/components/chat/chat-history-bar.tsx +35 -3
- package/src/web/components/chat/session-picker.tsx +78 -31
- package/src/web/components/layout/editor-panel.tsx +149 -18
- package/dist/web/assets/chat-tab-DqaEGCJ2.js +0 -8
- package/dist/web/assets/code-editor-MGlcP92U.js +0 -2
- package/dist/web/assets/index-DPI-YVJI.css +0 -2
- package/dist/web/assets/index-xBnF10pS.js +0 -37
- package/dist/web/assets/keybindings-store-lSdxs8NV.js +0 -1
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":"cb207881f2411490f280101b05f4b3cb","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/xychartDiagram-JWTSCODW-z5MVJauZ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-BOSy9ma9.js"},{"revision":null,"url":"assets/utils-BNytJOb1.js"},{"revision":null,"url":"assets/use-monaco-theme-DsGrkXnv.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-B2Xkyv-K.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-58BlOSf9.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/terminal-tab-BnA-xDvX.js"},{"revision":null,"url":"assets/tag-CCtdV063.js"},{"revision":null,"url":"assets/table-C7X5UAEI.js"},{"revision":null,"url":"assets/tab-store-BCfMgMKM.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-DrxVDY9q.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-f8opcZNY.js"},{"revision":null,"url":"assets/src-BqX54PbV.js"},{"revision":null,"url":"assets/sqlite-viewer-l4eyjI6C.js"},{"revision":null,"url":"assets/settings-tab-D1LdiVy8.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-ByxQqGgs.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-ClJuW3Hv.js"},{"revision":null,"url":"assets/rough.esm-JX0wREDd.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-BatTxyWb.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-ER-4DN55.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DH0AOkUy.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-FHMogtsh.js"},{"revision":null,"url":"assets/preload-helper-uTix4PVD.js"},{"revision":null,"url":"assets/postgres-viewer-ck2oHZND.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-WP0XXw51.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BHncZutv.js"},{"revision":null,"url":"assets/path-6uRLdFF7.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-DY5PNnZU.js"},{"revision":null,"url":"assets/ordinal-_K3x1fkz.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BsfWvIoO.js"},{"revision":null,"url":"assets/mermaid-parser.core-DMIWdgEW.js"},{"revision":null,"url":"assets/math-069Z4SuC.js"},{"revision":null,"url":"assets/markdown-renderer-DaS9de_M.js"},{"revision":null,"url":"assets/linear-DP4mkX3m.js"},{"revision":null,"url":"assets/line-B78g-52T.js"},{"revision":null,"url":"assets/lib-BQ34Db2e.js"},{"revision":null,"url":"assets/keybindings-store-lSdxs8NV.js"},{"revision":null,"url":"assets/katex-Bqvo_ZG0.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-Bi0UTUeN.js"},{"revision":null,"url":"assets/jsx-runtime-BRW_vwa9.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-ufoasAy6.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-BOyvKMmB.js"},{"revision":null,"url":"assets/isEmpty-bnrF3Qbc.js"},{"revision":null,"url":"assets/isArrayLikeObject-B_v2FtYn.js"},{"revision":null,"url":"assets/input-BglMT33g.js"},{"revision":null,"url":"assets/init-DlZdxViB.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-B1CX0pbC.js"},{"revision":null,"url":"assets/info-3K5VOQVL-_vRxVNUm.js"},{"revision":null,"url":"assets/index-xBnF10pS.js"},{"revision":null,"url":"assets/index-DPI-YVJI.css"},{"revision":null,"url":"assets/graphlib-BcsNnGcW.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BTXo57mF.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-Bwna3and.js"},{"revision":null,"url":"assets/git-graph-DxbU6ljU.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-D4v7ZbVE.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-DIqcTrDV.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-CKzVujYI.js"},{"revision":null,"url":"assets/dist-lF8CoYII.js"},{"revision":null,"url":"assets/dist-DylI9XxN.js"},{"revision":null,"url":"assets/dist-CSJdAyA9.js"},{"revision":null,"url":"assets/diff-viewer-C281t_Li.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-BkfNRc9U.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-k55eVqVU.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-B1Qz70Do.js"},{"revision":null,"url":"assets/defaultLocale-5eAKkKJC.js"},{"revision":null,"url":"assets/database-viewer-ngGR7s3t.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-BH7aWGRP.js"},{"revision":null,"url":"assets/dagre-Dbb5k38K.js"},{"revision":null,"url":"assets/cytoscape.esm-BW-DbntU.js"},{"revision":null,"url":"assets/csv-preview-DLqYtXxt.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-B_AWZsOP.js"},{"revision":null,"url":"assets/columns-2-DpsNbZOc.js"},{"revision":null,"url":"assets/code-editor-MGlcP92U.js"},{"revision":null,"url":"assets/clone-LRxlvnMj.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-CxkwuInd.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-lse8oZoJ.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-CeU4Q-xC.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-DxAOx4hG.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPQQBakK.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-Djlmrely.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-DfofndiH.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CFwSJijQ.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-CYaTbeZf.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-Dw8ClWch.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-D6BTbCQw.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-BXhYx3nO.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-JC6EGoUz.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-wMgTlP7f.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-BpS_PtKp.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-C7qGJrfV.js"},{"revision":null,"url":"assets/chunk-KYZI473N-BcUZNnwd.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-sQ0o-39C.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-23tyvw8k.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-HRhYy3kG.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-CzYx4w-r.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-BbQkJu8C.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-DXncblvW.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-DzqmU2Z7.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-D21mS_6G.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-BbIFzsIv.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CiyUJxNI.js"},{"revision":null,"url":"assets/chunk-55IACEB6-DJ6BynZ4.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-D4tOov49.js"},{"revision":null,"url":"assets/chevron-right-DeV0ehiG.js"},{"revision":null,"url":"assets/chat-tab-DqaEGCJ2.js"},{"revision":null,"url":"assets/channel-wrd-NHWf.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW-dV22iAsY.js"},{"revision":null,"url":"assets/browser-tab-BhnA1gi8.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-TEF8Ally.js"},{"revision":null,"url":"assets/arrow-up--LjUXLEt.js"},{"revision":null,"url":"assets/array-B9UHiPd-.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DWBCPMLF.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-DEO2f3VD.js"},{"revision":null,"url":"assets/arc-BAOivWpI.js"},{"revision":null,"url":"assets/api-settings-D21InCnR.js"},{"revision":null,"url":"assets/api-client-BfBM3I7n.js"},{"revision":null,"url":"assets/_baseUniq-BT4Ow4Kk.js"},{"revision":null,"url":"assets/_basePickBy-5PGDJbfF.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":"519dc61eb20563fe21c8146628078ccd","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/xychartDiagram-JWTSCODW-z5MVJauZ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-BOSy9ma9.js"},{"revision":null,"url":"assets/utils-BNytJOb1.js"},{"revision":null,"url":"assets/use-monaco-theme-BwIb9BHq.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-B2Xkyv-K.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-58BlOSf9.js"},{"revision":null,"url":"assets/terminal-tab-CKsBIgnq.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-CCtdV063.js"},{"revision":null,"url":"assets/table-C7X5UAEI.js"},{"revision":null,"url":"assets/tab-store-BCfMgMKM.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-DrxVDY9q.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-f8opcZNY.js"},{"revision":null,"url":"assets/src-BqX54PbV.js"},{"revision":null,"url":"assets/sqlite-viewer-4a4hHLZk.js"},{"revision":null,"url":"assets/settings-tab-CcmhnYpw.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-ByxQqGgs.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-ClJuW3Hv.js"},{"revision":null,"url":"assets/rough.esm-JX0wREDd.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-BatTxyWb.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-ER-4DN55.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DH0AOkUy.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-FHMogtsh.js"},{"revision":null,"url":"assets/preload-helper-uTix4PVD.js"},{"revision":null,"url":"assets/postgres-viewer-DP0FOQOa.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-WP0XXw51.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BHncZutv.js"},{"revision":null,"url":"assets/path-6uRLdFF7.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-DY5PNnZU.js"},{"revision":null,"url":"assets/ordinal-_K3x1fkz.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BsfWvIoO.js"},{"revision":null,"url":"assets/mermaid-parser.core-DMIWdgEW.js"},{"revision":null,"url":"assets/math-069Z4SuC.js"},{"revision":null,"url":"assets/markdown-renderer-BVxlq4zO.js"},{"revision":null,"url":"assets/linear-DP4mkX3m.js"},{"revision":null,"url":"assets/line-B78g-52T.js"},{"revision":null,"url":"assets/lib-BQ34Db2e.js"},{"revision":null,"url":"assets/keybindings-store-zY8zbJ2c.js"},{"revision":null,"url":"assets/katex-Bqvo_ZG0.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-Bi0UTUeN.js"},{"revision":null,"url":"assets/jsx-runtime-BRW_vwa9.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-ufoasAy6.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-BOyvKMmB.js"},{"revision":null,"url":"assets/isEmpty-bnrF3Qbc.js"},{"revision":null,"url":"assets/isArrayLikeObject-B_v2FtYn.js"},{"revision":null,"url":"assets/input-BglMT33g.js"},{"revision":null,"url":"assets/init-DlZdxViB.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-B1CX0pbC.js"},{"revision":null,"url":"assets/info-3K5VOQVL-_vRxVNUm.js"},{"revision":null,"url":"assets/index-CzwYVupc.css"},{"revision":null,"url":"assets/index-5a-tMkk5.js"},{"revision":null,"url":"assets/graphlib-BcsNnGcW.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BTXo57mF.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-Bwna3and.js"},{"revision":null,"url":"assets/git-graph-BQqdvSjX.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-D4v7ZbVE.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-DIqcTrDV.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-CKzVujYI.js"},{"revision":null,"url":"assets/dist-lF8CoYII.js"},{"revision":null,"url":"assets/dist-DylI9XxN.js"},{"revision":null,"url":"assets/dist-CSJdAyA9.js"},{"revision":null,"url":"assets/diff-viewer-CvZ06EAH.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-BkfNRc9U.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-k55eVqVU.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-B1Qz70Do.js"},{"revision":null,"url":"assets/defaultLocale-5eAKkKJC.js"},{"revision":null,"url":"assets/database-viewer-QzEuetE6.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-BH7aWGRP.js"},{"revision":null,"url":"assets/dagre-Dbb5k38K.js"},{"revision":null,"url":"assets/cytoscape.esm-BW-DbntU.js"},{"revision":null,"url":"assets/csv-preview-DLqYtXxt.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-B_AWZsOP.js"},{"revision":null,"url":"assets/columns-2-DpsNbZOc.js"},{"revision":null,"url":"assets/code-editor-CTjgdXh2.js"},{"revision":null,"url":"assets/clone-LRxlvnMj.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-CxkwuInd.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-lse8oZoJ.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-CeU4Q-xC.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-DxAOx4hG.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPQQBakK.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-Djlmrely.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-DfofndiH.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CFwSJijQ.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-CYaTbeZf.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-Dw8ClWch.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-D6BTbCQw.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-BXhYx3nO.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-JC6EGoUz.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-wMgTlP7f.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-BpS_PtKp.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-C7qGJrfV.js"},{"revision":null,"url":"assets/chunk-KYZI473N-BcUZNnwd.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-sQ0o-39C.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-23tyvw8k.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-HRhYy3kG.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-CzYx4w-r.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-BbQkJu8C.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-DXncblvW.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-DzqmU2Z7.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-D21mS_6G.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-BbIFzsIv.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CiyUJxNI.js"},{"revision":null,"url":"assets/chunk-55IACEB6-DJ6BynZ4.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-D4tOov49.js"},{"revision":null,"url":"assets/chevron-right-DeV0ehiG.js"},{"revision":null,"url":"assets/chat-tab-BJeNwwUM.js"},{"revision":null,"url":"assets/channel-wrd-NHWf.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW-dV22iAsY.js"},{"revision":null,"url":"assets/browser-tab-D5GfU4Ja.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-TEF8Ally.js"},{"revision":null,"url":"assets/arrow-up--LjUXLEt.js"},{"revision":null,"url":"assets/array-B9UHiPd-.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DWBCPMLF.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-DEO2f3VD.js"},{"revision":null,"url":"assets/arc-BAOivWpI.js"},{"revision":null,"url":"assets/api-settings-D21InCnR.js"},{"revision":null,"url":"assets/api-client-BfBM3I7n.js"},{"revision":null,"url":"assets/_baseUniq-BT4Ow4Kk.js"},{"revision":null,"url":"assets/_basePickBy-5PGDJbfF.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
|
@@ -8,8 +8,7 @@ import { renameSession as sdkRenameSession } from "@anthropic-ai/claude-agent-sd
|
|
|
8
8
|
import { listSlashItems } from "../../services/slash-items.service.ts";
|
|
9
9
|
import { getCachedUsage, refreshUsageNow } from "../../services/claude-usage.service.ts";
|
|
10
10
|
import { getSessionLog } from "../../services/session-log.service.ts";
|
|
11
|
-
import { getSessionMapping } from "../../services/db.service.ts";
|
|
12
|
-
import { getSessionMapping, setSessionTitle } from "../../services/db.service.ts";
|
|
11
|
+
import { getSessionMapping, setSessionTitle, getPinnedSessionIds, pinSession, unpinSession } from "../../services/db.service.ts";
|
|
13
12
|
import { ok, err } from "../../types/api.ts";
|
|
14
13
|
|
|
15
14
|
type Env = { Variables: { projectPath: string; projectName: string } };
|
|
@@ -64,7 +63,16 @@ chatRoutes.get("/sessions", async (c) => {
|
|
|
64
63
|
const projectPath = c.get("projectPath");
|
|
65
64
|
const providerId = c.req.query("providerId");
|
|
66
65
|
const sessions = await chatService.listSessions(providerId, projectPath);
|
|
67
|
-
|
|
66
|
+
// Enrich with pin status
|
|
67
|
+
const pinnedIds = getPinnedSessionIds();
|
|
68
|
+
const enriched = sessions.map((s) => ({ ...s, pinned: pinnedIds.has(s.id) }));
|
|
69
|
+
// Sort: pinned first (by pinned_at implicit via Set order), then unpinned by createdAt
|
|
70
|
+
enriched.sort((a, b) => {
|
|
71
|
+
if (a.pinned && !b.pinned) return -1;
|
|
72
|
+
if (!a.pinned && b.pinned) return 1;
|
|
73
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
74
|
+
});
|
|
75
|
+
return c.json(ok(enriched));
|
|
68
76
|
} catch (e) {
|
|
69
77
|
return c.json(err((e as Error).message), 500);
|
|
70
78
|
}
|
|
@@ -134,6 +142,28 @@ chatRoutes.patch("/sessions/:id", async (c) => {
|
|
|
134
142
|
}
|
|
135
143
|
});
|
|
136
144
|
|
|
145
|
+
/** PUT /chat/sessions/:id/pin — pin a session */
|
|
146
|
+
chatRoutes.put("/sessions/:id/pin", (c) => {
|
|
147
|
+
try {
|
|
148
|
+
const id = c.req.param("id");
|
|
149
|
+
pinSession(id);
|
|
150
|
+
return c.json(ok({ id, pinned: true }));
|
|
151
|
+
} catch (e) {
|
|
152
|
+
return c.json(err((e as Error).message), 500);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/** DELETE /chat/sessions/:id/pin — unpin a session */
|
|
157
|
+
chatRoutes.delete("/sessions/:id/pin", (c) => {
|
|
158
|
+
try {
|
|
159
|
+
const id = c.req.param("id");
|
|
160
|
+
unpinSession(id);
|
|
161
|
+
return c.json(ok({ id, pinned: false }));
|
|
162
|
+
} catch (e) {
|
|
163
|
+
return c.json(err((e as Error).message), 500);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
137
167
|
/** POST /chat/sessions/:id/fork — fork session into a new one (for rewind/branch) */
|
|
138
168
|
chatRoutes.post("/sessions/:id/fork", async (c) => {
|
|
139
169
|
try {
|
|
@@ -4,7 +4,7 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { mkdirSync, existsSync } from "node:fs";
|
|
5
5
|
|
|
6
6
|
const PPM_DIR = process.env.PPM_HOME || resolve(homedir(), ".ppm");
|
|
7
|
-
const CURRENT_SCHEMA_VERSION =
|
|
7
|
+
const CURRENT_SCHEMA_VERSION = 9;
|
|
8
8
|
|
|
9
9
|
let db: Database | null = null;
|
|
10
10
|
let dbProfile: string | null = null;
|
|
@@ -240,6 +240,17 @@ function runMigrations(database: Database): void {
|
|
|
240
240
|
PRAGMA user_version = 8;
|
|
241
241
|
`);
|
|
242
242
|
}
|
|
243
|
+
|
|
244
|
+
if (current < 9) {
|
|
245
|
+
database.exec(`
|
|
246
|
+
CREATE TABLE IF NOT EXISTS session_pins (
|
|
247
|
+
session_id TEXT PRIMARY KEY,
|
|
248
|
+
pinned_at TEXT DEFAULT (datetime('now'))
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
PRAGMA user_version = 9;
|
|
252
|
+
`);
|
|
253
|
+
}
|
|
243
254
|
}
|
|
244
255
|
|
|
245
256
|
// ---------------------------------------------------------------------------
|
|
@@ -350,6 +361,25 @@ export function getSessionTitles(sessionIds: string[]): Record<string, string> {
|
|
|
350
361
|
return result;
|
|
351
362
|
}
|
|
352
363
|
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// Session pin helpers
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
export function pinSession(sessionId: string): void {
|
|
369
|
+
getDb().query(
|
|
370
|
+
"INSERT INTO session_pins (session_id, pinned_at) VALUES (?, datetime('now')) ON CONFLICT(session_id) DO UPDATE SET pinned_at = datetime('now')",
|
|
371
|
+
).run(sessionId);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function unpinSession(sessionId: string): void {
|
|
375
|
+
getDb().query("DELETE FROM session_pins WHERE session_id = ?").run(sessionId);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function getPinnedSessionIds(): Set<string> {
|
|
379
|
+
const rows = getDb().query("SELECT session_id FROM session_pins ORDER BY pinned_at DESC").all() as { session_id: string }[];
|
|
380
|
+
return new Set(rows.map((r) => r.session_id));
|
|
381
|
+
}
|
|
382
|
+
|
|
353
383
|
// ---------------------------------------------------------------------------
|
|
354
384
|
// Push subscription helpers
|
|
355
385
|
// ---------------------------------------------------------------------------
|
package/src/types/chat.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
2
|
-
import { History, Settings2, Loader2, MessageSquare, RefreshCw, Search, Pencil, Check, X, BellOff, Bug, ClipboardCheck } from "lucide-react";
|
|
2
|
+
import { History, Settings2, Loader2, MessageSquare, RefreshCw, Search, Pencil, Check, X, BellOff, Bug, ClipboardCheck, Pin, PinOff } from "lucide-react";
|
|
3
3
|
import { Activity } from "lucide-react";
|
|
4
4
|
import { api, projectUrl } from "@/lib/api-client";
|
|
5
5
|
import { useTabStore } from "@/stores/tab-store";
|
|
@@ -149,6 +149,27 @@ export function ChatHistoryBar({
|
|
|
149
149
|
|
|
150
150
|
const cancelEditing = useCallback(() => setEditingId(null), []);
|
|
151
151
|
|
|
152
|
+
const togglePin = useCallback(async (e: React.MouseEvent, session: SessionInfo) => {
|
|
153
|
+
e.stopPropagation();
|
|
154
|
+
if (!projectName) return;
|
|
155
|
+
const url = `${projectUrl(projectName)}/chat/sessions/${session.id}/pin`;
|
|
156
|
+
try {
|
|
157
|
+
if (session.pinned) {
|
|
158
|
+
await api.del(url);
|
|
159
|
+
} else {
|
|
160
|
+
await api.put(url);
|
|
161
|
+
}
|
|
162
|
+
setSessions((prev) => {
|
|
163
|
+
const updated = prev.map((s) => s.id === session.id ? { ...s, pinned: !s.pinned } : s);
|
|
164
|
+
return updated.sort((a, b) => {
|
|
165
|
+
if (a.pinned && !b.pinned) return -1;
|
|
166
|
+
if (!a.pinned && b.pinned) return 1;
|
|
167
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
} catch { /* silent */ }
|
|
171
|
+
}, [projectName]);
|
|
172
|
+
|
|
152
173
|
// Filter sessions by search query
|
|
153
174
|
const filteredSessions = searchQuery.trim()
|
|
154
175
|
? sessions.filter((s) => (s.title || "").toLowerCase().includes(searchQuery.toLowerCase()))
|
|
@@ -310,9 +331,20 @@ export function ChatHistoryBar({
|
|
|
310
331
|
>
|
|
311
332
|
{session.title || "Untitled"}
|
|
312
333
|
</button>
|
|
334
|
+
<button
|
|
335
|
+
onClick={(e) => togglePin(e, session)}
|
|
336
|
+
className={`p-0.5 rounded transition-all ${
|
|
337
|
+
session.pinned
|
|
338
|
+
? "text-primary hover:text-primary/70"
|
|
339
|
+
: "text-text-subtle hover:text-text-secondary md:opacity-0 md:group-hover:opacity-100"
|
|
340
|
+
}`}
|
|
341
|
+
title={session.pinned ? "Unpin session" : "Pin session"}
|
|
342
|
+
>
|
|
343
|
+
{session.pinned ? <PinOff className="size-3" /> : <Pin className="size-3" />}
|
|
344
|
+
</button>
|
|
313
345
|
<button
|
|
314
346
|
onClick={(e) => startEditing(session, e)}
|
|
315
|
-
className="p-0.5 rounded text-text-subtle hover:text-text-secondary opacity-0 group-hover:opacity-100 transition-opacity"
|
|
347
|
+
className="p-0.5 rounded text-text-subtle hover:text-text-secondary md:opacity-0 md:group-hover:opacity-100 transition-opacity"
|
|
316
348
|
title="Rename session"
|
|
317
349
|
>
|
|
318
350
|
<Pencil className="size-3" />
|
|
@@ -320,7 +352,7 @@ export function ChatHistoryBar({
|
|
|
320
352
|
</>
|
|
321
353
|
)}
|
|
322
354
|
{editingId !== session.id && session.updatedAt && (
|
|
323
|
-
<span className="text-[10px] text-text-subtle shrink-0">{formatDate(session.updatedAt)}</span>
|
|
355
|
+
<span className="text-[10px] text-text-subtle shrink-0 w-10 text-right">{formatDate(session.updatedAt)}</span>
|
|
324
356
|
)}
|
|
325
357
|
</div>
|
|
326
358
|
))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from "react";
|
|
2
2
|
import { api, projectUrl } from "@/lib/api-client";
|
|
3
|
-
import { Plus, Trash2, MessageSquare, ChevronDown } from "lucide-react";
|
|
3
|
+
import { Plus, Trash2, MessageSquare, ChevronDown, Pin, PinOff } from "lucide-react";
|
|
4
4
|
import type { SessionInfo } from "../../../types/chat";
|
|
5
5
|
|
|
6
6
|
interface SessionPickerProps {
|
|
@@ -57,6 +57,75 @@ export function SessionPicker({
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
const handleTogglePin = async (e: React.MouseEvent, session: SessionInfo) => {
|
|
61
|
+
e.stopPropagation();
|
|
62
|
+
if (!projectName) return;
|
|
63
|
+
const url = `${projectUrl(projectName)}/chat/sessions/${session.id}/pin`;
|
|
64
|
+
try {
|
|
65
|
+
if (session.pinned) {
|
|
66
|
+
await api.del(url);
|
|
67
|
+
} else {
|
|
68
|
+
await api.put(url);
|
|
69
|
+
}
|
|
70
|
+
setSessions((prev) => {
|
|
71
|
+
const updated = prev.map((s) => s.id === session.id ? { ...s, pinned: !s.pinned } : s);
|
|
72
|
+
return updated.sort((a, b) => {
|
|
73
|
+
if (a.pinned && !b.pinned) return -1;
|
|
74
|
+
if (!a.pinned && b.pinned) return 1;
|
|
75
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
// Silently fail
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
function renderSessionRow(session: SessionInfo) {
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
key={session.id}
|
|
87
|
+
onClick={() => {
|
|
88
|
+
onSelectSession(session);
|
|
89
|
+
setOpen(false);
|
|
90
|
+
}}
|
|
91
|
+
className={`group flex items-center justify-between px-3 py-2 text-sm cursor-pointer hover:bg-surface-elevated transition-colors ${
|
|
92
|
+
session.id === currentSessionId
|
|
93
|
+
? "bg-surface-elevated text-text-primary"
|
|
94
|
+
: "text-text-secondary"
|
|
95
|
+
}`}
|
|
96
|
+
>
|
|
97
|
+
<div className="flex flex-col min-w-0 flex-1">
|
|
98
|
+
<span className="truncate text-xs font-medium">
|
|
99
|
+
{session.title}
|
|
100
|
+
</span>
|
|
101
|
+
<span className="text-xs text-text-subtle">
|
|
102
|
+
{new Date(session.createdAt).toLocaleDateString()}
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-center gap-0.5 shrink-0">
|
|
106
|
+
<button
|
|
107
|
+
onClick={(e) => handleTogglePin(e, session)}
|
|
108
|
+
className={`p-1 rounded transition-colors ${
|
|
109
|
+
session.pinned
|
|
110
|
+
? "text-primary hover:text-primary/70"
|
|
111
|
+
: "text-text-subtle md:opacity-0 md:group-hover:opacity-100 hover:text-text-primary"
|
|
112
|
+
}`}
|
|
113
|
+
aria-label={session.pinned ? "Unpin session" : "Pin session"}
|
|
114
|
+
>
|
|
115
|
+
{session.pinned ? <PinOff className="size-3" /> : <Pin className="size-3" />}
|
|
116
|
+
</button>
|
|
117
|
+
<button
|
|
118
|
+
onClick={(e) => handleDelete(e, session)}
|
|
119
|
+
className="p-1 rounded hover:bg-red-500/20 text-text-subtle hover:text-red-400 transition-colors md:opacity-0 md:group-hover:opacity-100"
|
|
120
|
+
aria-label="Delete session"
|
|
121
|
+
>
|
|
122
|
+
<Trash2 className="size-3" />
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
60
129
|
return (
|
|
61
130
|
<div className="relative">
|
|
62
131
|
<button
|
|
@@ -100,36 +169,14 @@ export function SessionPicker({
|
|
|
100
169
|
No sessions yet
|
|
101
170
|
</p>
|
|
102
171
|
)}
|
|
103
|
-
{sessions.
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
session.id === currentSessionId
|
|
112
|
-
? "bg-surface-elevated text-text-primary"
|
|
113
|
-
: "text-text-secondary"
|
|
114
|
-
}`}
|
|
115
|
-
>
|
|
116
|
-
<div className="flex flex-col min-w-0 flex-1">
|
|
117
|
-
<span className="truncate text-xs font-medium">
|
|
118
|
-
{session.title}
|
|
119
|
-
</span>
|
|
120
|
-
<span className="text-xs text-text-subtle">
|
|
121
|
-
{new Date(session.createdAt).toLocaleDateString()}
|
|
122
|
-
</span>
|
|
123
|
-
</div>
|
|
124
|
-
<button
|
|
125
|
-
onClick={(e) => handleDelete(e, session)}
|
|
126
|
-
className="p-1 rounded hover:bg-red-500/20 text-text-subtle hover:text-red-400 transition-colors shrink-0"
|
|
127
|
-
aria-label="Delete session"
|
|
128
|
-
>
|
|
129
|
-
<Trash2 className="size-3" />
|
|
130
|
-
</button>
|
|
131
|
-
</div>
|
|
132
|
-
))}
|
|
172
|
+
{sessions.filter((s) => s.pinned).length > 0 && (
|
|
173
|
+
<p className="px-3 py-1 text-[10px] text-text-subtle uppercase tracking-wider bg-surface">Pinned</p>
|
|
174
|
+
)}
|
|
175
|
+
{sessions.filter((s) => s.pinned).map((session) => renderSessionRow(session))}
|
|
176
|
+
{sessions.filter((s) => s.pinned).length > 0 && sessions.filter((s) => !s.pinned).length > 0 && (
|
|
177
|
+
<div className="border-t border-border" />
|
|
178
|
+
)}
|
|
179
|
+
{sessions.filter((s) => !s.pinned).map((session) => renderSessionRow(session))}
|
|
133
180
|
</div>
|
|
134
181
|
</div>
|
|
135
182
|
</>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { Suspense, lazy } from "react";
|
|
2
|
-
import { Loader2, Terminal, MessageSquare, GitBranch } from "lucide-react";
|
|
1
|
+
import { Suspense, lazy, useEffect, useState, useCallback } from "react";
|
|
2
|
+
import { Loader2, Terminal, MessageSquare, GitBranch, Pin, PinOff } from "lucide-react";
|
|
3
3
|
import { usePanelStore } from "@/stores/panel-store";
|
|
4
4
|
import { useProjectStore } from "@/stores/project-store";
|
|
5
5
|
import type { TabType } from "@/stores/tab-store";
|
|
6
|
+
import { api, projectUrl } from "@/lib/api-client";
|
|
7
|
+
import type { SessionInfo } from "../../../types/chat";
|
|
6
8
|
import { TabBar } from "./tab-bar";
|
|
7
9
|
import { SplitDropOverlay } from "./split-drop-overlay";
|
|
8
10
|
import { cn } from "@/lib/utils";
|
|
@@ -74,8 +76,68 @@ export function EditorPanel({ panelId, projectName }: EditorPanelProps) {
|
|
|
74
76
|
);
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
function formatRelativeDate(iso: string): string {
|
|
80
|
+
try {
|
|
81
|
+
const date = new Date(iso);
|
|
82
|
+
const now = new Date();
|
|
83
|
+
const diffMs = now.getTime() - date.getTime();
|
|
84
|
+
const diffMin = Math.floor(diffMs / 60_000);
|
|
85
|
+
if (diffMin < 1) return "Just now";
|
|
86
|
+
if (diffMin < 60) return `${diffMin}m ago`;
|
|
87
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
88
|
+
if (diffHr < 24) return `${diffHr}h ago`;
|
|
89
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
90
|
+
if (diffDay < 7) return `${diffDay}d ago`;
|
|
91
|
+
return date.toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
92
|
+
} catch {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const MAX_RECENT_SESSIONS = 5;
|
|
98
|
+
|
|
77
99
|
function EmptyPanel({ panelId }: { panelId: string }) {
|
|
78
100
|
const activeProject = useProjectStore((s) => s.activeProject);
|
|
101
|
+
const [sessions, setSessions] = useState<SessionInfo[]>([]);
|
|
102
|
+
const [loadingSessions, setLoadingSessions] = useState(false);
|
|
103
|
+
|
|
104
|
+
const loadSessions = useCallback(async () => {
|
|
105
|
+
if (!activeProject?.name) return;
|
|
106
|
+
setLoadingSessions(true);
|
|
107
|
+
try {
|
|
108
|
+
const data = await api.get<SessionInfo[]>(`${projectUrl(activeProject.name)}/chat/sessions`);
|
|
109
|
+
setSessions(data.slice(0, MAX_RECENT_SESSIONS));
|
|
110
|
+
} catch {
|
|
111
|
+
// silently ignore — empty state still functional without sessions
|
|
112
|
+
} finally {
|
|
113
|
+
setLoadingSessions(false);
|
|
114
|
+
}
|
|
115
|
+
}, [activeProject?.name]);
|
|
116
|
+
|
|
117
|
+
useEffect(() => { loadSessions(); }, [loadSessions]);
|
|
118
|
+
|
|
119
|
+
const togglePin = useCallback(async (e: React.MouseEvent, session: SessionInfo) => {
|
|
120
|
+
e.stopPropagation();
|
|
121
|
+
if (!activeProject?.name) return;
|
|
122
|
+
const url = `${projectUrl(activeProject.name)}/chat/sessions/${session.id}/pin`;
|
|
123
|
+
try {
|
|
124
|
+
if (session.pinned) {
|
|
125
|
+
await api.del(url);
|
|
126
|
+
} else {
|
|
127
|
+
await api.put(url);
|
|
128
|
+
}
|
|
129
|
+
setSessions((prev) => {
|
|
130
|
+
const updated = prev.map((s) => s.id === session.id ? { ...s, pinned: !s.pinned } : s);
|
|
131
|
+
return updated.sort((a, b) => {
|
|
132
|
+
if (a.pinned && !b.pinned) return -1;
|
|
133
|
+
if (!a.pinned && b.pinned) return 1;
|
|
134
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
} catch {
|
|
138
|
+
// silently ignore
|
|
139
|
+
}
|
|
140
|
+
}, [activeProject?.name]);
|
|
79
141
|
|
|
80
142
|
function openTab(type: TabType) {
|
|
81
143
|
const needsProject = type !== "settings";
|
|
@@ -86,23 +148,92 @@ function EmptyPanel({ panelId }: { panelId: string }) {
|
|
|
86
148
|
);
|
|
87
149
|
}
|
|
88
150
|
|
|
151
|
+
function openSession(session: SessionInfo) {
|
|
152
|
+
usePanelStore.getState().openTab(
|
|
153
|
+
{
|
|
154
|
+
type: "chat",
|
|
155
|
+
title: session.title || "Chat",
|
|
156
|
+
projectId: activeProject?.name ?? null,
|
|
157
|
+
metadata: { projectName: activeProject?.name, sessionId: session.id, providerId: session.providerId },
|
|
158
|
+
closable: true,
|
|
159
|
+
},
|
|
160
|
+
panelId,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const pinnedSessions = sessions.filter((s) => s.pinned);
|
|
165
|
+
const recentSessions = sessions.filter((s) => !s.pinned).slice(0, MAX_RECENT_SESSIONS);
|
|
166
|
+
|
|
167
|
+
function renderSessionRow(session: SessionInfo) {
|
|
168
|
+
return (
|
|
169
|
+
<button
|
|
170
|
+
key={session.id}
|
|
171
|
+
onClick={() => openSession(session)}
|
|
172
|
+
className="group flex items-center gap-2.5 w-full px-3 py-2.5 text-left hover:bg-surface-elevated active:bg-surface-elevated transition-colors border-b border-border/50 last:border-0"
|
|
173
|
+
>
|
|
174
|
+
<MessageSquare className="size-3.5 shrink-0 text-text-subtle" />
|
|
175
|
+
<span className="flex-1 min-w-0 text-xs font-medium truncate text-text-primary">
|
|
176
|
+
{session.title || "Untitled"}
|
|
177
|
+
</span>
|
|
178
|
+
{session.updatedAt && (
|
|
179
|
+
<span className="text-[10px] text-text-subtle shrink-0">
|
|
180
|
+
{formatRelativeDate(session.updatedAt)}
|
|
181
|
+
</span>
|
|
182
|
+
)}
|
|
183
|
+
<span
|
|
184
|
+
role="button"
|
|
185
|
+
tabIndex={0}
|
|
186
|
+
onClick={(e) => togglePin(e, session)}
|
|
187
|
+
className={`p-1 rounded transition-colors shrink-0 ${
|
|
188
|
+
session.pinned
|
|
189
|
+
? "text-primary hover:text-primary/70"
|
|
190
|
+
: "text-text-subtle md:opacity-0 md:group-hover:opacity-100 hover:text-text-primary"
|
|
191
|
+
}`}
|
|
192
|
+
aria-label={session.pinned ? "Unpin session" : "Pin session"}
|
|
193
|
+
>
|
|
194
|
+
{session.pinned ? <PinOff className="size-3" /> : <Pin className="size-3" />}
|
|
195
|
+
</span>
|
|
196
|
+
</button>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
89
200
|
return (
|
|
90
|
-
<div className="flex flex-col
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
201
|
+
<div className="flex flex-col h-full overflow-y-auto text-text-secondary">
|
|
202
|
+
<div className="flex flex-col items-center justify-center gap-6 px-4 flex-1">
|
|
203
|
+
<p className="text-sm">Open a tab to get started</p>
|
|
204
|
+
<div className="grid grid-cols-3 gap-2 w-full max-w-sm">
|
|
205
|
+
{QUICK_OPEN_TABS.map((opt) => {
|
|
206
|
+
const Icon = opt.icon;
|
|
207
|
+
return (
|
|
208
|
+
<button
|
|
209
|
+
key={opt.type}
|
|
210
|
+
onClick={() => openTab(opt.type)}
|
|
211
|
+
className="flex flex-col items-center justify-center gap-1.5 px-2 py-3 rounded-md border border-border bg-surface hover:bg-surface-elevated active:bg-surface-elevated text-xs text-foreground transition-colors"
|
|
212
|
+
>
|
|
213
|
+
<Icon className="size-5" />
|
|
214
|
+
{opt.label}
|
|
215
|
+
</button>
|
|
216
|
+
);
|
|
217
|
+
})}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{activeProject && !loadingSessions && pinnedSessions.length > 0 && (
|
|
221
|
+
<div className="flex flex-col gap-2 w-full max-w-sm">
|
|
222
|
+
<p className="text-xs text-text-subtle text-center">Pinned</p>
|
|
223
|
+
<div className="w-full rounded-md border border-border bg-surface overflow-hidden">
|
|
224
|
+
{pinnedSessions.map(renderSessionRow)}
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
|
|
229
|
+
{activeProject && !loadingSessions && recentSessions.length > 0 && (
|
|
230
|
+
<div className="flex flex-col gap-2 w-full max-w-sm">
|
|
231
|
+
<p className="text-xs text-text-subtle text-center">Recent chats</p>
|
|
232
|
+
<div className="w-full rounded-md border border-border bg-surface overflow-hidden">
|
|
233
|
+
{recentSessions.map(renderSessionRow)}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
106
237
|
</div>
|
|
107
238
|
</div>
|
|
108
239
|
);
|