@hienlh/ppm 0.11.8 → 0.11.10

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 (32) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/web/assets/{audio-preview--VaB403K.js → audio-preview-1DSokoKb.js} +1 -1
  3. package/dist/web/assets/{chat-tab-DS455Mwt.js → chat-tab-DJRa8l8m.js} +8 -8
  4. package/dist/web/assets/code-editor-CcHvVH2U.js +8 -0
  5. package/dist/web/assets/{conflict-editor-DTMT0ZxI.js → conflict-editor-C7ve0DV-.js} +1 -1
  6. package/dist/web/assets/{database-viewer-B7aIs1XX.js → database-viewer-DIuNqm7A.js} +2 -2
  7. package/dist/web/assets/{diff-viewer-C9unqCxZ.js → diff-viewer-DeitZvBi.js} +1 -1
  8. package/dist/web/assets/{extension-webview-DUs8mqO_.js → extension-webview-Ca4T_biw.js} +1 -1
  9. package/dist/web/assets/{image-preview-CoiyMXuX.js → image-preview-DvcaLB4N.js} +1 -1
  10. package/dist/web/assets/index-BJ5XngyN.js +23 -0
  11. package/dist/web/assets/index-FKwHNxD8.css +2 -0
  12. package/dist/web/assets/{markdown-renderer-4LYdtPZU.js → markdown-renderer-DP-MF3bM.js} +2 -2
  13. package/dist/web/assets/{pdf-preview-CFO6CjHG.js → pdf-preview-CSreqeop.js} +1 -1
  14. package/dist/web/assets/{port-forwarding-tab-DpoVwtlp.js → port-forwarding-tab-pISwYxUi.js} +1 -1
  15. package/dist/web/assets/{postgres-viewer-Ku3X9rJQ.js → postgres-viewer-ZIO1deEA.js} +2 -2
  16. package/dist/web/assets/{settings-tab-l-SopAs4.js → settings-tab-DjTka3Fq.js} +1 -1
  17. package/dist/web/assets/{sqlite-viewer-DzS8-nbv.js → sqlite-viewer-Bxpo_5Ei.js} +1 -1
  18. package/dist/web/assets/{terminal-tab-ERPwhU5O.js → terminal-tab-Df8jrXRQ.js} +1 -1
  19. package/dist/web/assets/{video-preview-BZenXc_U.js → video-preview-DmeBvw9D.js} +1 -1
  20. package/dist/web/index.html +2 -2
  21. package/dist/web/sw.js +1 -1
  22. package/package.json +1 -1
  23. package/src/web/components/explorer/file-tree.tsx +57 -24
  24. package/src/web/components/layout/draggable-tab.tsx +7 -3
  25. package/src/web/components/layout/tab-bar.tsx +4 -0
  26. package/src/web/hooks/use-chat.ts +8 -0
  27. package/src/web/hooks/use-notification-badge.ts +39 -9
  28. package/src/web/lib/favicon.ts +23 -14
  29. package/src/web/stores/streaming-store.ts +23 -0
  30. package/dist/web/assets/code-editor-BsUsDjJz.js +0 -8
  31. package/dist/web/assets/index-B1GZswlR.js +0 -26
  32. package/dist/web/assets/index-C3ZstZ9f.css +0 -2
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":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"ff071aa161c8548ee3f427470d37187e","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/input-ClhO__YM.js"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/diff-viewer-C9unqCxZ.js"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/csv-parser--2WJNgS7.js"},{"revision":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/video-preview-BZenXc_U.js"},{"revision":null,"url":"assets/conflict-editor-DTMT0ZxI.js"},{"revision":null,"url":"assets/csv-preview-BEBJD4a_.js"},{"revision":null,"url":"assets/vendor-xterm-CU2c3f0A.js"},{"revision":null,"url":"assets/keybindings-store-C9KsBH7z.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-BhjTKsbg.js"},{"revision":null,"url":"assets/refresh-cw-LlbZDJpO.js"},{"revision":null,"url":"assets/sql-query-editor-BnpKNGG0.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-XX6_EZsC.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/pdf-preview-CFO6CjHG.js"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/markdown-renderer-4LYdtPZU.js"},{"revision":null,"url":"assets/katex-CKoArbIw.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/chat-tab-DS455Mwt.js"},{"revision":null,"url":"assets/index-B1GZswlR.js"},{"revision":null,"url":"assets/extension-store-3yZYn07W.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/project-store-BYmQ0fDC.js"},{"revision":null,"url":"assets/scroll-area-DW7L4Gnc.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/postgres-viewer-Ku3X9rJQ.js"},{"revision":null,"url":"assets/vendor-mermaid-BlWh9BJO.js"},{"revision":null,"url":"assets/dist-im4ynINo.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-C7agXrtd.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/keybindings-store-BkZjvU9J.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/use-monaco-theme-BERjR8IA.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DSn_ekR5.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/ai-settings-section-C6FDY8qE.js"},{"revision":null,"url":"assets/info-3K5VOQVL-CzgVqYTx.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/trash-2-CJYoLw7Q.js"},{"revision":null,"url":"assets/code-editor-BsUsDjJz.js"},{"revision":null,"url":"assets/extension-webview-DUs8mqO_.js"},{"revision":null,"url":"assets/file-exclamation-point-BwzaQ50n.js"},{"revision":null,"url":"assets/terminal-tab-ERPwhU5O.js"},{"revision":null,"url":"assets/api-settings-C__hxGX2.js"},{"revision":null,"url":"assets/index-C3ZstZ9f.css"},{"revision":null,"url":"assets/utils-CTg5uAYR.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/sqlite-viewer-DzS8-nbv.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/audio-preview--VaB403K.js"},{"revision":null,"url":"assets/image-preview-CoiyMXuX.js"},{"revision":null,"url":"assets/tab-store-B3M9hjho.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C8puYVyN.js"},{"revision":null,"url":"assets/api-client-Bn-Pi9k5.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/sql-completion-provider-C3cq9j99.js"},{"revision":null,"url":"assets/settings-store-B9axDbuA.js"},{"revision":null,"url":"assets/use-blob-url-BK7zshV7.js"},{"revision":null,"url":"assets/esm-K1XIK4vc.js"},{"revision":null,"url":"assets/settings-tab-l-SopAs4.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/port-forwarding-tab-DpoVwtlp.js"},{"revision":null,"url":"assets/lib-DQHnkzGy.js"},{"revision":null,"url":"assets/database-viewer-B7aIs1XX.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BRZ7alnf.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.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":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"d69cc10c06922c4a1def6af7edff78e1","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/input-ClhO__YM.js"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/postgres-viewer-ZIO1deEA.js"},{"revision":null,"url":"assets/markdown-renderer-DP-MF3bM.js"},{"revision":null,"url":"assets/csv-parser--2WJNgS7.js"},{"revision":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/csv-preview-BEBJD4a_.js"},{"revision":null,"url":"assets/audio-preview-1DSokoKb.js"},{"revision":null,"url":"assets/vendor-xterm-CU2c3f0A.js"},{"revision":null,"url":"assets/keybindings-store-C9KsBH7z.js"},{"revision":null,"url":"assets/diff-viewer-DeitZvBi.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-BhjTKsbg.js"},{"revision":null,"url":"assets/refresh-cw-LlbZDJpO.js"},{"revision":null,"url":"assets/sql-query-editor-BnpKNGG0.js"},{"revision":null,"url":"assets/extension-webview-Ca4T_biw.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-XX6_EZsC.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/chat-tab-DJRa8l8m.js"},{"revision":null,"url":"assets/database-viewer-DIuNqm7A.js"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/code-editor-CcHvVH2U.js"},{"revision":null,"url":"assets/pdf-preview-CSreqeop.js"},{"revision":null,"url":"assets/katex-CKoArbIw.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/extension-store-3yZYn07W.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/project-store-BYmQ0fDC.js"},{"revision":null,"url":"assets/scroll-area-DW7L4Gnc.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/vendor-mermaid-BlWh9BJO.js"},{"revision":null,"url":"assets/dist-im4ynINo.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-C7agXrtd.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/keybindings-store-BkZjvU9J.js"},{"revision":null,"url":"assets/image-preview-DvcaLB4N.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/use-monaco-theme-BERjR8IA.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/index-FKwHNxD8.css"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/sqlite-viewer-Bxpo_5Ei.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DSn_ekR5.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/ai-settings-section-C6FDY8qE.js"},{"revision":null,"url":"assets/info-3K5VOQVL-CzgVqYTx.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/trash-2-CJYoLw7Q.js"},{"revision":null,"url":"assets/port-forwarding-tab-pISwYxUi.js"},{"revision":null,"url":"assets/file-exclamation-point-BwzaQ50n.js"},{"revision":null,"url":"assets/api-settings-C__hxGX2.js"},{"revision":null,"url":"assets/utils-CTg5uAYR.js"},{"revision":null,"url":"assets/video-preview-DmeBvw9D.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/conflict-editor-C7ve0DV-.js"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/tab-store-B3M9hjho.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C8puYVyN.js"},{"revision":null,"url":"assets/api-client-Bn-Pi9k5.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/settings-tab-DjTka3Fq.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/sql-completion-provider-C3cq9j99.js"},{"revision":null,"url":"assets/terminal-tab-Df8jrXRQ.js"},{"revision":null,"url":"assets/settings-store-B9axDbuA.js"},{"revision":null,"url":"assets/use-blob-url-BK7zshV7.js"},{"revision":null,"url":"assets/esm-K1XIK4vc.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/index-BJ5XngyN.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/lib-DQHnkzGy.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BRZ7alnf.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.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.11.8",
3
+ "version": "0.11.10",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -7,6 +7,12 @@ import {
7
7
  FileJson,
8
8
  FileText,
9
9
  FileType,
10
+ FileImage,
11
+ FileVideo,
12
+ FileAudio,
13
+ FileSpreadsheet,
14
+ FileArchive,
15
+ Database,
10
16
  ChevronRight,
11
17
  ChevronDown,
12
18
  Download,
@@ -40,27 +46,56 @@ function isExternalFileDrag(e: React.DragEvent): boolean {
40
46
  /** Synthetic root node for creating files/folders at project root */
41
47
  const ROOT_NODE: FileNode = { name: "", path: "", type: "directory" };
42
48
 
43
- const FILE_ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
44
- ts: FileCode,
45
- tsx: FileCode,
46
- js: FileCode,
47
- jsx: FileCode,
48
- py: FileCode,
49
- rs: FileCode,
50
- go: FileCode,
51
- json: FileJson,
52
- md: FileText,
53
- txt: FileText,
54
- yaml: FileType,
55
- yml: FileType,
56
- html: FileCode,
57
- css: FileCode,
58
- scss: FileCode,
49
+ type FileIconInfo = { icon: React.ComponentType<{ className?: string }>; color?: string };
50
+
51
+ const FILE_ICON_MAP: Record<string, FileIconInfo> = {
52
+ // Code
53
+ ts: { icon: FileCode, color: "text-blue-400" }, tsx: { icon: FileCode, color: "text-blue-400" },
54
+ js: { icon: FileCode, color: "text-yellow-400" }, jsx: { icon: FileCode, color: "text-yellow-400" },
55
+ py: { icon: FileCode, color: "text-green-400" }, rs: { icon: FileCode, color: "text-orange-400" },
56
+ go: { icon: FileCode, color: "text-cyan-400" }, c: { icon: FileCode, color: "text-blue-300" },
57
+ cpp: { icon: FileCode, color: "text-blue-300" }, java: { icon: FileCode, color: "text-red-400" },
58
+ rb: { icon: FileCode, color: "text-red-400" }, php: { icon: FileCode, color: "text-purple-400" },
59
+ swift: { icon: FileCode, color: "text-orange-400" }, kt: { icon: FileCode, color: "text-purple-400" },
60
+ dart: { icon: FileCode, color: "text-cyan-400" }, sh: { icon: FileCode, color: "text-green-300" },
61
+ html: { icon: FileCode, color: "text-orange-400" }, css: { icon: FileCode, color: "text-blue-400" },
62
+ scss: { icon: FileCode, color: "text-pink-400" },
63
+ // Data
64
+ json: { icon: FileJson, color: "text-yellow-400" },
65
+ yaml: { icon: FileType, color: "text-orange-300" }, yml: { icon: FileType, color: "text-orange-300" },
66
+ toml: { icon: FileType, color: "text-orange-300" }, ini: { icon: FileType, color: "text-orange-300" },
67
+ env: { icon: FileType, color: "text-yellow-300" },
68
+ csv: { icon: FileSpreadsheet, color: "text-green-400" },
69
+ xls: { icon: FileSpreadsheet, color: "text-green-400" }, xlsx: { icon: FileSpreadsheet, color: "text-green-400" },
70
+ // Text/Docs
71
+ md: { icon: FileText, color: "text-text-secondary" }, txt: { icon: FileText, color: "text-text-secondary" },
72
+ log: { icon: FileText, color: "text-text-subtle" }, pdf: { icon: FileText, color: "text-red-400" },
73
+ // Images
74
+ png: { icon: FileImage, color: "text-green-400" }, jpg: { icon: FileImage, color: "text-green-400" },
75
+ jpeg: { icon: FileImage, color: "text-green-400" }, gif: { icon: FileImage, color: "text-green-400" },
76
+ svg: { icon: FileImage, color: "text-yellow-400" }, webp: { icon: FileImage, color: "text-green-400" },
77
+ ico: { icon: FileImage, color: "text-green-400" }, bmp: { icon: FileImage, color: "text-green-400" },
78
+ // Video
79
+ mp4: { icon: FileVideo, color: "text-purple-400" }, webm: { icon: FileVideo, color: "text-purple-400" },
80
+ mov: { icon: FileVideo, color: "text-purple-400" }, avi: { icon: FileVideo, color: "text-purple-400" },
81
+ mkv: { icon: FileVideo, color: "text-purple-400" },
82
+ // Audio
83
+ mp3: { icon: FileAudio, color: "text-pink-400" }, wav: { icon: FileAudio, color: "text-pink-400" },
84
+ ogg: { icon: FileAudio, color: "text-pink-400" }, flac: { icon: FileAudio, color: "text-pink-400" },
85
+ // Database
86
+ db: { icon: Database, color: "text-amber-400" }, sqlite: { icon: Database, color: "text-amber-400" },
87
+ sqlite3: { icon: Database, color: "text-amber-400" }, sql: { icon: Database, color: "text-amber-400" },
88
+ // Archives
89
+ zip: { icon: FileArchive, color: "text-amber-300" }, tar: { icon: FileArchive, color: "text-amber-300" },
90
+ gz: { icon: FileArchive, color: "text-amber-300" }, rar: { icon: FileArchive, color: "text-amber-300" },
91
+ "7z": { icon: FileArchive, color: "text-amber-300" },
59
92
  };
60
93
 
61
- function getFileIcon(name: string): React.ComponentType<{ className?: string }> {
94
+ const DEFAULT_FILE_ICON: FileIconInfo = { icon: File };
95
+
96
+ function getFileIcon(name: string): FileIconInfo {
62
97
  const ext = name.split(".").pop()?.toLowerCase() ?? "";
63
- return FILE_ICON_MAP[ext] ?? File;
98
+ return FILE_ICON_MAP[ext] ?? DEFAULT_FILE_ICON;
64
99
  }
65
100
 
66
101
  interface TreeNodeProps {
@@ -141,10 +176,8 @@ const TreeNode = memo(function TreeNode({ node, depth, projectName, onAction, on
141
176
  }
142
177
  }
143
178
 
144
- const Icon = isDir
145
- ? isExpanded
146
- ? FolderOpen
147
- : Folder
179
+ const { icon: FileIcon, color: fileIconColor } = isDir
180
+ ? { icon: isExpanded ? FolderOpen : Folder, color: "text-primary" }
148
181
  : getFileIcon(node.name);
149
182
 
150
183
  const sortedChildren = node.children
@@ -186,10 +219,10 @@ const TreeNode = memo(function TreeNode({ node, depth, projectName, onAction, on
186
219
  ) : (
187
220
  <span className="w-3.5 shrink-0" />
188
221
  )}
189
- <Icon
222
+ <FileIcon
190
223
  className={cn(
191
224
  "size-4 shrink-0",
192
- isDir ? "text-primary" : "text-text-secondary",
225
+ fileIconColor ?? "text-text-secondary",
193
226
  )}
194
227
  />
195
228
  <span className="truncate">{node.name}</span>
@@ -19,6 +19,8 @@ interface DraggableTabProps {
19
19
  showDropBefore: boolean;
20
20
  /** Notification type if unread (null = no unread). Controls badge color. */
21
21
  notificationType?: string | null;
22
+ /** True when this chat tab is actively streaming an AI response */
23
+ isStreaming?: boolean;
22
24
  onSelect: () => void;
23
25
  onClose: () => void;
24
26
  onDragStart: (e: React.DragEvent) => void;
@@ -35,7 +37,7 @@ interface DraggableTabProps {
35
37
  }
36
38
 
37
39
  export function DraggableTab({
38
- tab, isActive, icon: Icon, showDropBefore, notificationType, onSelect, onClose,
40
+ tab, isActive, icon: Icon, showDropBefore, notificationType, isStreaming, onSelect, onClose,
39
41
  onDragStart, onDragOver, onDragEnd, onTouchStart, onTouchMove, onTouchEnd, tabRef, onRename, onContextAction,
40
42
  }: DraggableTabProps) {
41
43
  const [editing, setEditing] = useState(false);
@@ -92,9 +94,11 @@ export function DraggableTab({
92
94
  >
93
95
  <span className="relative">
94
96
  <Icon className="size-4" />
95
- {notificationType && !isActive && (
97
+ {isStreaming ? (
98
+ <span className="absolute -top-1 -right-1 size-2 rounded-full bg-emerald-500 animate-pulse motion-reduce:animate-none" />
99
+ ) : notificationType && !isActive ? (
96
100
  <span className={cn("absolute -top-1 -right-1 size-2 rounded-full", notificationColor(notificationType))} />
97
- )}
101
+ ) : null}
98
102
  </span>
99
103
  {editing ? (
100
104
  <input
@@ -21,6 +21,7 @@ import { useTouchTabDrag, wasTouchDragRecent } from "@/hooks/use-touch-tab-drag"
21
21
  import { openCommandPalette } from "@/hooks/use-global-keybindings";
22
22
  import { api, projectUrl } from "@/lib/api-client";
23
23
  import { useNotificationStore, notificationColor } from "@/stores/notification-store";
24
+ import { useStreamingStore } from "@/stores/streaming-store";
24
25
  import { useTabOverflow, getHiddenUnreadDirection } from "@/hooks/use-tab-overflow";
25
26
  import { DraggableTab } from "./draggable-tab";
26
27
  import { cn } from "@/lib/utils";
@@ -64,6 +65,7 @@ export const TabBar = memo(function TabBar({ panelId }: TabBarProps) {
64
65
  const { handleTouchStart, handleTouchMove, handleTouchEnd } = useTouchTabDrag(effectivePanelId);
65
66
 
66
67
  const notifications = useNotificationStore((s) => s.notifications);
68
+ const streamingSessions = useStreamingStore((s) => s.sessions);
67
69
  const { canScrollLeft, canScrollRight, scrollLeft: doScrollLeft, scrollRight: doScrollRight } =
68
70
  useTabOverflow(scrollRef);
69
71
 
@@ -189,6 +191,7 @@ export const TabBar = memo(function TabBar({ panelId }: TabBarProps) {
189
191
  const sessionId = tab.type === "chat" ? (tab.metadata?.sessionId as string) : undefined;
190
192
  const entry = sessionId ? notifications.get(sessionId) : undefined;
191
193
  const notiType = entry && entry.count > 0 ? entry.type : null;
194
+ const isTabStreaming = sessionId ? streamingSessions.has(sessionId) : false;
192
195
  return (
193
196
  <DraggableTab
194
197
  key={tab.id}
@@ -197,6 +200,7 @@ export const TabBar = memo(function TabBar({ panelId }: TabBarProps) {
197
200
  icon={TAB_ICONS[tab.type] || Puzzle}
198
201
  showDropBefore={dropIndex === i}
199
202
  notificationType={notiType}
203
+ isStreaming={isTabStreaming}
200
204
  onSelect={() => {
201
205
  if (wasTouchDragRecent()) return;
202
206
  usePanelStore.getState().setActiveTab(tab.id, effectivePanelId);
@@ -2,6 +2,7 @@ import { useState, useCallback, useRef, useEffect } from "react";
2
2
  import { useWebSocket } from "./use-websocket";
3
3
  import { api, getAuthToken, projectUrl } from "@/lib/api-client";
4
4
  import { useNotificationStore } from "@/stores/notification-store";
5
+ import { useStreamingStore } from "@/stores/streaming-store";
5
6
  import { usePanelStore } from "@/stores/panel-store";
6
7
  import { playNotificationSound } from "@/lib/notification-sounds";
7
8
  import { toast } from "sonner";
@@ -128,6 +129,13 @@ export function useChat(sessionId: string | null, providerId = "claude", project
128
129
  // Derived state
129
130
  const isStreaming = phase !== "idle";
130
131
 
132
+ // Sync streaming state to global store (for favicon + tab icon indicators)
133
+ useEffect(() => {
134
+ if (!sessionId) return;
135
+ useStreamingStore.getState().setStreaming(sessionId, phase !== "idle");
136
+ return () => { useStreamingStore.getState().setStreaming(sessionId, false); };
137
+ }, [sessionId, phase]);
138
+
131
139
  /**
132
140
  * Route a child event to its parent Agent/Task tool_use's children array.
133
141
  * Creates a new parent object to ensure React detects the change on re-render.
@@ -1,7 +1,8 @@
1
- import { useEffect } from "react";
1
+ import { useEffect, useRef } from "react";
2
2
  import { useNotificationStore, selectTotalUnread } from "@/stores/notification-store";
3
3
  import { useProjectStore } from "@/stores/project-store";
4
4
  import { useSettingsStore } from "@/stores/settings-store";
5
+ import { useStreamingStore, selectAnyStreaming } from "@/stores/streaming-store";
5
6
  import { setFavicon } from "@/lib/favicon";
6
7
 
7
8
  function buildTitle(unread: number, projectName?: string, deviceName?: string): string {
@@ -9,21 +10,50 @@ function buildTitle(unread: number, projectName?: string, deviceName?: string):
9
10
  return unread > 0 ? `(${unread}) ${parts}` : parts;
10
11
  }
11
12
 
12
- /** Syncs document.title and favicon with unread notification count.
13
- * Uses direct Zustand subscription to update immediately even in background tabs
14
- * (useEffect is throttled by the browser when the tab is hidden). */
13
+ /** Syncs document.title and favicon with unread notification count + streaming state.
14
+ * When any chat is streaming, favicon alternates between blue and amber every 800ms.
15
+ * Uses direct Zustand subscription to update immediately even in background tabs. */
15
16
  export function useNotificationBadge(): void {
16
17
  const activeProject = useProjectStore((s) => s.activeProject);
17
18
  const deviceName = useSettingsStore((s) => s.deviceName);
19
+ const anyStreaming = useStreamingStore(selectAnyStreaming);
20
+ const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
18
21
 
19
22
  useEffect(() => {
20
- const update = () => {
23
+ const getHasBadge = () => selectTotalUnread(useNotificationStore.getState()) > 0;
24
+
25
+ const updateTitle = () => {
21
26
  const unread = selectTotalUnread(useNotificationStore.getState());
22
27
  document.title = buildTitle(unread, activeProject?.name, deviceName ?? undefined);
23
- setFavicon(unread > 0);
24
28
  };
25
29
 
26
- update(); // apply immediately on mount / when project or device name changes
27
- return useNotificationStore.subscribe(update); // fire on every store change
28
- }, [activeProject?.name, deviceName]);
30
+ updateTitle();
31
+
32
+ if (anyStreaming) {
33
+ // Alternate favicon between primary (blue) and streaming (amber) every 800ms
34
+ let alt = false;
35
+ setFavicon(getHasBadge(), false);
36
+ intervalRef.current = setInterval(() => {
37
+ alt = !alt;
38
+ setFavicon(getHasBadge(), alt);
39
+ }, 800);
40
+ } else {
41
+ setFavicon(getHasBadge());
42
+ }
43
+
44
+ // Keep title in sync with notification changes
45
+ const unsub = useNotificationStore.subscribe(() => {
46
+ updateTitle();
47
+ // Static favicon update only when not streaming (interval handles streaming)
48
+ if (!anyStreaming) setFavicon(getHasBadge());
49
+ });
50
+
51
+ return () => {
52
+ unsub();
53
+ if (intervalRef.current) {
54
+ clearInterval(intervalRef.current);
55
+ intervalRef.current = null;
56
+ }
57
+ };
58
+ }, [activeProject?.name, deviceName, anyStreaming]);
29
59
  }
@@ -1,21 +1,30 @@
1
- // SVG favicon with blue rounded rect + white "PPM" text
2
- const FAVICON_SVG_NORMAL = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
3
- <rect width="32" height="32" rx="6" fill="#3b82f6"/>
4
- <text x="16" y="22" text-anchor="middle" font-family="system-ui,sans-serif" font-weight="700" font-size="11" fill="white">PPM</text>
5
- </svg>`;
1
+ const COLOR_PRIMARY = "#3b82f6";
2
+ const COLOR_STREAMING = "#f59e0b"; // amber-500
6
3
 
7
- // Same + red notification dot (top-right)
8
- const FAVICON_SVG_BADGE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
9
- <rect width="32" height="32" rx="6" fill="#3b82f6"/>
4
+ function buildSvg(bgColor: string, badgeDot: boolean): string {
5
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
6
+ <rect width="32" height="32" rx="6" fill="${bgColor}"/>
10
7
  <text x="16" y="22" text-anchor="middle" font-family="system-ui,sans-serif" font-weight="700" font-size="11" fill="white">PPM</text>
11
- <circle cx="26" cy="6" r="5" fill="#ef4444"/>
8
+ ${badgeDot ? '<circle cx="26" cy="6" r="5" fill="#ef4444"/>' : ""}
12
9
  </svg>`;
10
+ }
13
11
 
14
- export const FAVICON_NORMAL = `data:image/svg+xml,${encodeURIComponent(FAVICON_SVG_NORMAL)}`;
15
- export const FAVICON_BADGE = `data:image/svg+xml,${encodeURIComponent(FAVICON_SVG_BADGE)}`;
12
+ // Pre-encode all 4 variants
13
+ export const FAVICON_NORMAL = `data:image/svg+xml,${encodeURIComponent(buildSvg(COLOR_PRIMARY, false))}`;
14
+ export const FAVICON_BADGE = `data:image/svg+xml,${encodeURIComponent(buildSvg(COLOR_PRIMARY, true))}`;
15
+ const FAVICON_STREAMING = `data:image/svg+xml,${encodeURIComponent(buildSvg(COLOR_STREAMING, false))}`;
16
+ const FAVICON_STREAMING_BADGE = `data:image/svg+xml,${encodeURIComponent(buildSvg(COLOR_STREAMING, true))}`;
16
17
 
17
- /** Swap favicon between normal and badge (red dot) variant */
18
- export function setFavicon(hasBadge: boolean): void {
18
+ /**
19
+ * Swap favicon. When `isStreamingAlt` is true, uses amber color to
20
+ * create an alternation effect (caller toggles this on an interval).
21
+ */
22
+ export function setFavicon(hasBadge: boolean, isStreamingAlt = false): void {
19
23
  const el = document.getElementById("ppm-favicon") as HTMLLinkElement | null;
20
- if (el) el.href = hasBadge ? FAVICON_BADGE : FAVICON_NORMAL;
24
+ if (!el) return;
25
+ if (isStreamingAlt) {
26
+ el.href = hasBadge ? FAVICON_STREAMING_BADGE : FAVICON_STREAMING;
27
+ } else {
28
+ el.href = hasBadge ? FAVICON_BADGE : FAVICON_NORMAL;
29
+ }
21
30
  }
@@ -0,0 +1,23 @@
1
+ import { create } from "zustand";
2
+
3
+ /** Tracks which chat sessions are currently streaming AI responses */
4
+ interface StreamingStore {
5
+ /** Set of sessionIds that are actively streaming */
6
+ sessions: Set<string>;
7
+ /** Mark a session as streaming or idle */
8
+ setStreaming: (sessionId: string, streaming: boolean) => void;
9
+ }
10
+
11
+ export const useStreamingStore = create<StreamingStore>((set) => ({
12
+ sessions: new Set(),
13
+ setStreaming: (sessionId, streaming) =>
14
+ set((state) => {
15
+ const next = new Set(state.sessions);
16
+ if (streaming) next.add(sessionId);
17
+ else next.delete(sessionId);
18
+ return { sessions: next };
19
+ }),
20
+ }));
21
+
22
+ /** Selector: true if any session is streaming */
23
+ export const selectAnyStreaming = (s: StreamingStore) => s.sessions.size > 0;
@@ -1,8 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/markdown-renderer-4LYdtPZU.js","assets/rolldown-runtime-FhOqtrmT.js","assets/index-B1GZswlR.js","assets/vendor-mermaid-BlWh9BJO.js","assets/vendor-ui-B-T_damt.js","assets/vendor-markdown-0Mxgxy0L.js","assets/input-ClhO__YM.js","assets/utils-CTg5uAYR.js","assets/createLucideIcon-BjHrJDVb.js","assets/x-DlFGzN8d.js","assets/scroll-area-DW7L4Gnc.js","assets/ai-settings-section-C6FDY8qE.js","assets/dist-C5IgeqrV.js","assets/plus-51UQ45rf.js","assets/refresh-cw-LlbZDJpO.js","assets/trash-2-CJYoLw7Q.js","assets/api-client-Bn-Pi9k5.js","assets/api-settings-C__hxGX2.js","assets/chevron-right-BzAdxJRG.js","assets/database-D4DIhgi-.js","assets/react-GqWghJ-L.js","assets/extension-store-3yZYn07W.js","assets/keybindings-store-C9KsBH7z.js","assets/tab-store-B3M9hjho.js","assets/project-store-BYmQ0fDC.js","assets/settings-store-B9axDbuA.js","assets/index-C3ZstZ9f.css","assets/csv-preview-BEBJD4a_.js","assets/lib-DQHnkzGy.js","assets/arrow-up-Dtrfv490.js","assets/csv-parser--2WJNgS7.js","assets/image-preview-CoiyMXuX.js","assets/file-exclamation-point-BwzaQ50n.js","assets/use-blob-url-BK7zshV7.js","assets/pdf-preview-CFO6CjHG.js","assets/video-preview-BZenXc_U.js","assets/audio-preview--VaB403K.js"])))=>i.map(i=>d[i]);
2
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-B-T_damt.js";import"./scroll-area-DW7L4Gnc.js";import{t as r}from"./chevron-right-BzAdxJRG.js";import{a as i,l as a,n as o,o as s,r as c,s as l,t as ee}from"./input-ClhO__YM.js";import{t as u}from"./code-CuravVys.js";import{t as d}from"./database-D4DIhgi-.js";import{n as f,r as p}from"./x-DlFGzN8d.js";import{t as te}from"./file-exclamation-point-BwzaQ50n.js";import{t as m}from"./table-Dq575bPF.js";import{t as h}from"./text-wrap-Cn6BNQfq.js";import{i as g,t as _}from"./api-client-Bn-Pi9k5.js";import{G as v}from"./vendor-mermaid-BlWh9BJO.js";import{n as ne}from"./settings-store-B9axDbuA.js";import{t as y}from"./utils-CTg5uAYR.js";import{n as re,t as ie}from"./tab-store-B3M9hjho.js";import{n as b}from"./project-store-BYmQ0fDC.js";import{E as ae,F as x,I as S,L as C,M as w,P as T,R as E,a as oe,c as se,d as D,f as O,g as k,h as A,l as j,m as M,o as N,p as P,u as F,y as ce,z as le}from"./index-B1GZswlR.js";import{n as ue,t as de}from"./use-monaco-theme-BERjR8IA.js";import{n as fe,t as pe}from"./sql-completion-provider-C3cq9j99.js";var I=e(n(),1),L=t(),R={ts:E,tsx:E,js:E,jsx:E,py:E,rs:E,go:E,html:E,css:E,scss:E,json:le,md:C,txt:C,yaml:S,yml:S};function me(e,t){return t?T:R[e.split(`.`).pop()?.toLowerCase()??``]??x}function he(e,t){let n=[],r=e;for(let e=0;e<t.length;e++){let i=t[e],a=t.slice(0,e+1).join(`/`),o=r.find(e=>e.name===i);if(n.push({name:i,fullPath:a,node:o??null,siblings:r}),o?.children)r=o.children;else{for(let r=e+1;r<t.length;r++)n.push({name:t[r],fullPath:t.slice(0,r+1).join(`/`),node:null,siblings:[]});break}}return n}function z(e){return[...e].sort((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1)}function ge({filePath:e,projectName:t,tabId:n,className:i}){let a=ce(e=>e.tree),{updateTab:o,openTab:s}=ie(A(e=>({updateTab:e.updateTab,openTab:e.openTab}))),c=(0,I.useRef)(null),l=(0,I.useMemo)(()=>he(a,e.split(`/`).filter(Boolean)),[a,e]);(0,I.useEffect)(()=>{c.current&&(c.current.scrollLeft=c.current.scrollWidth)},[l]);function ee(e,r){let i=y(e);r.metaKey||r.ctrlKey?s({type:`editor`,title:i,metadata:{filePath:e,projectName:t},projectId:t,closable:!0}):o(n,{title:i,metadata:{filePath:e,projectName:t}})}return(0,L.jsx)(`div`,{ref:c,className:i,children:l.map((e,n)=>(0,L.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[n>0&&(0,L.jsx)(r,{className:`size-3 text-muted-foreground shrink-0 mx-0.5`}),e.siblings.length>0?(0,L.jsx)(B,{segment:e,isLast:n===l.length-1,projectName:t,onFileClick:ee}):(0,L.jsx)(`span`,{className:`text-xs text-muted-foreground px-1 py-0.5`,children:e.name})]},e.fullPath))})}function B({segment:e,isLast:t,projectName:n,onFileClick:r}){let i=(0,I.useMemo)(()=>z(e.siblings),[e.siblings]);return(0,L.jsxs)(se,{children:[(0,L.jsx)(M,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${t?`text-foreground font-medium`:`text-muted-foreground`}`,children:e.name})}),(0,L.jsx)(j,{align:`start`,className:`max-h-[300px] p-1`,children:i.map(t=>(0,L.jsx)(V,{node:t,projectName:n,activePath:e.fullPath,onFileClick:r},t.path))})]})}function V({node:e,projectName:t,activePath:n,onFileClick:r}){let i=me(e.name,e.type===`directory`),a=e.path===n;return e.type===`directory`&&e.children&&e.children.length>0?(0,L.jsxs)(D,{children:[(0,L.jsxs)(P,{className:`text-xs gap-1.5 ${a?`bg-muted`:``}`,children:[(0,L.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,L.jsx)(`span`,{className:`truncate`,children:e.name})]}),(0,L.jsx)(O,{className:`max-h-[300px] overflow-y-auto p-1`,children:z(e.children).map(e=>(0,L.jsx)(V,{node:e,projectName:t,activePath:n,onFileClick:r},e.path))})]}):(0,L.jsxs)(F,{className:`text-xs gap-1.5 cursor-pointer ${a?`bg-muted`:``}`,onSelect:e=>{},onClick:t=>{e.type!==`directory`&&r(e.path,t)},children:[(0,L.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,L.jsx)(`span`,{className:`truncate`,children:e.name})]})}function H({active:e,onClick:t,icon:n,label:r}){return(0,L.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:[(0,L.jsx)(n,{className:`size-3`}),(0,L.jsx)(`span`,{className:`hidden sm:inline`,children:r})]})}function _e({ext:e,mdMode:t,onMdModeChange:n,csvMode:r,onCsvModeChange:i,wordWrap:a,onToggleWordWrap:o,filePath:s,projectName:c,className:l}){return(0,L.jsxs)(`div`,{className:l,children:[(e===`md`||e===`mdx`)&&n&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(H,{active:t===`edit`,onClick:()=>n(`edit`),icon:u,label:`Edit`}),(0,L.jsx)(H,{active:t===`preview`,onClick:()=>n(`preview`),icon:f,label:`Preview`})]}),e===`csv`&&i&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(H,{active:r===`table`,onClick:()=>i(`table`),icon:m,label:`Table`}),(0,L.jsx)(H,{active:r===`raw`,onClick:()=>i(`raw`),icon:u,label:`Raw`})]}),(0,L.jsx)(H,{active:a,onClick:o,icon:h,label:`Wrap`}),s&&c&&(0,L.jsx)(H,{active:!1,onClick:()=>k(c,s),icon:p,label:`Download`})]})}function ve({open:e,defaultName:t,content:n,onSave:r,onCancel:u}){let[d,f]=(0,I.useState)(t),[p,te]=(0,I.useState)(!1),[m,h]=(0,I.useState)(``),g=b(e=>e.activeProject),_=(0,I.useCallback)(()=>{let e=d.trim();if(!e){h(`Filename cannot be empty`);return}if(/[/\\]/.test(e)){h(`Filename cannot contain / or \\`);return}h(``),te(!0)},[d]),v=(0,I.useCallback)(e=>{let t=e.includes(`\\`)?`\\`:`/`;r(e.endsWith(t)?`${e}${d.trim()}`:`${e}${t}${d.trim()}`,n)},[d,n,r]);return p?(0,L.jsx)(N,{open:!0,mode:`folder`,root:g?.path,title:`Save "${d.trim()}" to...`,onSelect:v,onCancel:()=>te(!1)}):(0,L.jsx)(o,{open:e,onOpenChange:e=>{e||u()},children:(0,L.jsxs)(c,{className:`sm:max-w-md`,children:[(0,L.jsx)(s,{children:(0,L.jsx)(l,{children:`Save As`})}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 py-2`,children:[(0,L.jsx)(`label`,{className:`text-sm text-muted-foreground`,children:`Filename`}),(0,L.jsx)(ee,{value:d,onChange:e=>{f(e.target.value),h(``)},onKeyDown:e=>{e.key===`Enter`&&_()},placeholder:`e.g. my-file.ts`,autoFocus:!0}),m&&(0,L.jsx)(`p`,{className:`text-xs text-destructive`,children:m})]}),(0,L.jsxs)(i,{children:[(0,L.jsx)(a,{variant:`outline`,onClick:u,children:`Cancel`}),(0,L.jsx)(a,{onClick:_,children:`Choose Folder...`})]})]})})}var ye=(0,I.lazy)(()=>v(()=>import(`./markdown-renderer-4LYdtPZU.js`).then(e=>({default:e.MarkdownRenderer})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]))),be=(0,I.lazy)(()=>v(()=>import(`./csv-preview-BEBJD4a_.js`).then(e=>({default:e.CsvPreview})),__vite__mapDeps([27,1,4,5,28,8,29,30]))),xe=(0,I.lazy)(()=>v(()=>import(`./image-preview-CoiyMXuX.js`).then(e=>({default:e.ImagePreview})),__vite__mapDeps([31,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,32,33]))),Se=(0,I.lazy)(()=>v(()=>import(`./pdf-preview-CFO6CjHG.js`).then(e=>({default:e.PdfPreview})),__vite__mapDeps([34,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,32,33]))),Ce=(0,I.lazy)(()=>v(()=>import(`./video-preview-BZenXc_U.js`).then(e=>({default:e.VideoPreview})),__vite__mapDeps([35,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,32,33]))),we=(0,I.lazy)(()=>v(()=>import(`./audio-preview--VaB403K.js`).then(e=>({default:e.AudioPreview})),__vite__mapDeps([36,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,32,33]))),Te=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`svg`,`ico`]),Ee=new Set([`mp4`,`webm`,`mov`,`ogg`,`avi`,`mkv`]),De=new Set([`mp3`,`wav`,`flac`,`aac`,`m4a`,`wma`]),Oe=new Set([`db`,`sqlite`,`sqlite3`]);function ke(e){return e.split(`.`).pop()?.toLowerCase()??``}function Ae(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`,sql:`sql`}[ke(e)]??`plaintext`}var U=(0,I.memo)(function({metadata:e,tabId:t}){let n=e?.filePath,r=e?.projectName,i=e?.inlineContent,a=e?.inlineLanguage,[o,s]=(0,I.useState)(i??null),[c,l]=(0,I.useState)(`utf-8`),[ee,u]=(0,I.useState)(!0),[f,p]=(0,I.useState)(null),[m,h]=(0,I.useState)(!1),v=(0,I.useRef)(null),b=(0,I.useRef)(``),x=(0,I.useRef)(null),{tabs:S,updateTab:C}=ie(A(e=>({tabs:e.tabs,updateTab:e.updateTab}))),{wordWrap:T,toggleWordWrap:E}=ne(A(e=>({wordWrap:e.wordWrap,toggleWordWrap:e.toggleWordWrap}))),se=de(),D=e?.isUntitled===!0,O=e?.unsavedContent,[k,j]=(0,I.useState)(!1),M=S.find(e=>e.id===t),N=n?ke(n):``,P=Te.has(N),F=N===`pdf`,ce=Ee.has(N),le=De.has(N),R=Oe.has(N),me=N===`md`||N===`mdx`,he=N===`csv`,z=N===`sql`,[B,V]=(0,I.useState)(`preview`),[H,ye]=(0,I.useState)(`table`),{connections:U,cachedTables:Me,refreshTables:Ne}=oe(),[G,Pe]=(0,I.useState)(()=>{if(!z||!n)return null;let e=localStorage.getItem(`ppm:sql-conn:${n}`);return e?Number(e):null}),K=(0,I.useRef)(null),q=(0,I.useRef)(null),J=(0,I.useMemo)(()=>U.find(e=>e.id===G)??null,[U,G]),Fe=i!=null&&(a===`json`||a===`xml`),[Ie,Le]=(0,I.useState)(!1),Re=(0,I.useCallback)(()=>{if(i)if(Ie)s(i),Le(!1);else{let e=i.trimStart();if(a===`json`)try{s(JSON.stringify(JSON.parse(e),null,2)),Le(!0)}catch{}else if(a===`xml`){let t=0;s(e.replace(/(>)(<)(\/*)/g,`$1
3
- $2$3`).split(`
4
- `).map(e=>{let n=e.trim();n.startsWith(`</`)&&(t=Math.max(0,t-1));let r=` `.repeat(t)+n;return n.startsWith(`<`)&&!n.startsWith(`</`)&&!n.endsWith(`/>`)&&!n.includes(`</`)&&t++,r}).join(`
5
- `)),Le(!0)}}},[i,a,Ie]),ze=(0,I.useCallback)(e=>{Pe(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),Ne(e).catch(()=>{})},[n,Ne]),Y=(0,I.useMemo)(()=>{if(!z||!G)return;let e=(Me.get(G)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>_.get(`/api/db/connections/${G}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[z,G,Me]);(0,I.useEffect)(()=>{if(!(!K.current||!Y))return q.current?.dispose(),pe(),q.current=K.current.languages.registerCompletionItemProvider(`sql`,fe(K.current,Y)),()=>{q.current?.dispose()}},[Y]);let Be=ie(e=>e.openTab),X=(0,I.useCallback)(e=>{J&&Be({type:`database`,title:`${J.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:J.id,connectionName:J.name,dbType:J.type,initialSql:e}})},[J,Be]),Ve=(0,I.useCallback)(()=>{if(!x.current||!J)return;let e=x.current,t=e.getSelection();X(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[J,X]),Z=(0,I.useRef)([]),He=(0,I.useRef)(X);He.current=X,(0,I.useEffect)(()=>()=>{Z.current.forEach(e=>e.dispose()),Z.current=[]},[]),(0,I.useEffect)(()=>{R&&t&&C(t,{type:`sqlite`})},[R,t,C]);let Q=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,I.useEffect)(()=>{if(i!=null){u(!1);return}if(D){s(O??``),b.current=O??``,u(!1),O&&h(!0);return}if(!n||!Q&&!r)return;if(P||F||ce||le){u(!1);return}u(!0),p(null);let e=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${g(r)}/files/read?path=${encodeURIComponent(n)}`;return _.get(e).then(e=>{s(e.content),e.encoding&&l(e.encoding),b.current=e.content,u(!1)}).catch(e=>{p(e instanceof Error?e.message:`Failed to load file`),u(!1)}),()=>{v.current&&clearTimeout(v.current)}},[n,r,P,F,Q,D]);let Ue=(0,I.useRef)(m);Ue.current=m,(0,I.useEffect)(()=>{if(!n||!r||i!=null||D)return;let e=e=>{let t=e.detail;if(t.projectName!==r||t.path!==n||Ue.current)return;let i=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${g(r)}/files/read?path=${encodeURIComponent(n)}`;_.get(i).then(e=>{e.content!==b.current&&(s(e.content),b.current=e.content,e.encoding&&l(e.encoding))}).catch(()=>{})};return window.addEventListener(`file:changed`,e),()=>window.removeEventListener(`file:changed`,e)},[n,r,Q,i,D]),(0,I.useEffect)(()=>{if(!M)return;let t=D?`Untitled-${e?.untitledNumber??1}`:n?y(n):`Untitled`,r=m?`${t} \u25CF`:t;M.title!==r&&C(M.id,{title:r})},[m]);let We=(0,I.useCallback)(async e=>{if(n&&!(!Q&&!r))try{Q?await _.put(`/api/fs/write`,{path:n,content:e}):await _.put(`${g(r)}/files/write`,{path:n,content:e}),h(!1)}catch{}},[n,r,Q]);function Ge(n){let r=n??``;s(r),b.current=r,h(!0),v.current&&clearTimeout(v.current),D?v.current=setTimeout(()=>{t&&C(t,{metadata:{...e,unsavedContent:b.current}})},2e3):v.current=setTimeout(()=>We(b.current),1e3)}let Ke=(0,I.useCallback)(async(e,n)=>{try{if(v.current&&clearTimeout(v.current),await _.put(`/api/fs/write`,{path:e,content:n}),t){let{closeTab:n,openTab:r}=re.getState();n(t),r({type:`editor`,title:y(e),projectId:null,metadata:{filePath:e},closable:!0})}h(!1),j(!1)}catch{}},[t]),$=e?.lineNumber,qe=(0,I.useCallback)((e,t)=>{if(x.current=e,K.current=t,$&&$>0&&setTimeout(()=>{e.revealLineInCenter($),e.setPosition({lineNumber:$,column:1}),e.focus()},100),D&&e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>j(!0)),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>ne.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),Y&&(q.current?.dispose(),q.current=t.languages.registerCompletionItemProvider(`sql`,fe(t,Y))),z){Z.current.forEach(e=>e.dispose()),Z.current=[];let n=e.getModel(),r=e.addCommand(0,(e,t)=>{t&&He.current(t)});if(r&&n){let e=t.languages.registerCodeLensProvider(`sql`,{provideCodeLenses:e=>{if(e!==n)return{lenses:[],dispose:()=>{}};let t=[],i=e.getValue().split(`
6
- `),a=-1,o=[],s=!1,c=(e,n)=>{let i=n.trim();!i||i.startsWith(`--`)||t.push({range:{startLineNumber:e,startColumn:1,endLineNumber:e,endColumn:1},command:{id:r,title:`▷ Run`,arguments:[i]}})};for(let e=0;e<i.length;e++){let t=i[e].trim();if(a===-1){if(!t||t.startsWith(`--`))continue;a=e+1,o=[]}o.push(i[e]),(t.match(/\$\$/g)||[]).length%2==1&&(s=!s),!s&&t.endsWith(`;`)&&(c(a,o.join(`
7
- `)),a=-1,o=[])}return a>0&&o.join(``).trim()&&c(a,o.join(`
8
- `)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[Y]);if(!i&&!D&&(!n||!Q&&!r))return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(ee)return(0,L.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,L.jsx)(w,{className:`size-5 animate-spin`}),(0,L.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(f)return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:f});if(P)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(xe,{filePath:n,projectName:r})});if(F)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(Se,{filePath:n,projectName:r})});if(ce)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(Ce,{filePath:n,projectName:r})});if(le)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(we,{filePath:n,projectName:r})});if(c===`base64`)return(0,L.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,L.jsx)(te,{className:`size-10 text-text-subtle`}),(0,L.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,L.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let Je=z?(0,L.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,L.jsx)(d,{className:`size-3 text-muted-foreground`}),(0,L.jsxs)(`select`,{value:G??``,onChange:e=>{let t=Number(e.target.value);t&&ze(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,L.jsx)(`option`,{value:``,children:`Connection…`}),U.map(e=>(0,L.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,L.jsx)(`button`,{type:`button`,onClick:Ve,disabled:!J,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run all in DB Viewer`,children:(0,L.jsx)(ae,{className:`size-3.5`})})]}):null;return(0,L.jsxs)(`div`,{className:`flex flex-col h-full w-full overflow-hidden`,children:[i!=null&&Fe&&(0,L.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,L.jsx)(`button`,{type:`button`,onClick:Re,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:Ie?`Raw`:`Beautify`})}),n&&r&&t&&(0,L.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,L.jsx)(ge,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),Je,(0,L.jsx)(_e,{ext:N,mdMode:B,onMdModeChange:V,csvMode:H,onCsvModeChange:ye,wordWrap:T,onToggleWordWrap:E,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),z&&(!r||!t)&&(0,L.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,L.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?y(n):`SQL`}),Je]}),he&&H===`table`?(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,L.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,L.jsx)(be,{content:o??``,onContentChange:Ge,wordWrap:T})}):me&&B===`preview`?(0,L.jsx)(je,{content:o??``}):(0,L.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,L.jsx)(ue,{height:`100%`,language:a??Ae(n??``),value:o??``,onChange:i==null?Ge:void 0,onMount:qe,theme:se,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:T?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:i!=null},loading:(0,L.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})})}),k&&(0,L.jsx)(ve,{open:k,defaultName:`Untitled-${e?.untitledNumber??1}`,content:b.current,onSave:Ke,onCancel:()=>j(!1)})]})});function W(){return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,L.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})})}function je({content:e}){return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,L.jsx)(ye,{content:e,className:`flex-1 overflow-auto p-4`})})}export{U as CodeEditor};