@hienlh/ppm 0.11.6 → 0.11.8

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 (78) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/web/assets/{ai-settings-section-D2vqiydT.js → ai-settings-section-C6FDY8qE.js} +1 -1
  3. package/dist/web/assets/{api-settings-2eTz4SgY.js → api-settings-C__hxGX2.js} +1 -1
  4. package/dist/web/assets/architecture-PBZL5I3N-XX6_EZsC.js +1 -0
  5. package/dist/web/assets/audio-preview--VaB403K.js +1 -0
  6. package/dist/web/assets/{chat-tab-2znvnxzD.js → chat-tab-DS455Mwt.js} +4 -4
  7. package/dist/web/assets/code-editor-BsUsDjJz.js +8 -0
  8. package/dist/web/assets/{conflict-editor-DXnqiS4c.js → conflict-editor-DTMT0ZxI.js} +1 -1
  9. package/dist/web/assets/{csv-preview-D37K2LRd.js → csv-preview-BEBJD4a_.js} +1 -1
  10. package/dist/web/assets/{database-viewer-DK4PfwVJ.js → database-viewer-B7aIs1XX.js} +1 -1
  11. package/dist/web/assets/{diff-viewer-Cy4oX3mb.js → diff-viewer-C9unqCxZ.js} +1 -1
  12. package/dist/web/assets/{esm-B99v94EE.js → esm-K1XIK4vc.js} +1 -1
  13. package/dist/web/assets/{extension-store-CkyOvGbF.js → extension-store-3yZYn07W.js} +1 -1
  14. package/dist/web/assets/{extension-webview-DIVrmdfu.js → extension-webview-DUs8mqO_.js} +1 -1
  15. package/dist/web/assets/file-exclamation-point-BwzaQ50n.js +1 -0
  16. package/dist/web/assets/gitGraph-HDMCJU4V-BhjTKsbg.js +1 -0
  17. package/dist/web/assets/image-preview-CoiyMXuX.js +1 -0
  18. package/dist/web/assets/{index-oqP6ldWs.js → index-B1GZswlR.js} +4 -4
  19. package/dist/web/assets/index-C3ZstZ9f.css +2 -0
  20. package/dist/web/assets/info-3K5VOQVL-CzgVqYTx.js +1 -0
  21. package/dist/web/assets/{input-CHRMley8.js → input-ClhO__YM.js} +1 -1
  22. package/dist/web/assets/keybindings-store-BkZjvU9J.js +1 -0
  23. package/dist/web/assets/{keybindings-store-CpP5_miA.js → keybindings-store-C9KsBH7z.js} +1 -1
  24. package/dist/web/assets/{markdown-renderer-BLhcNL42.js → markdown-renderer-4LYdtPZU.js} +3 -3
  25. package/dist/web/assets/packet-RMMSAZCW-C7agXrtd.js +1 -0
  26. package/dist/web/assets/pdf-preview-CFO6CjHG.js +1 -0
  27. package/dist/web/assets/pie-UPGHQEXC-BRZ7alnf.js +1 -0
  28. package/dist/web/assets/{port-forwarding-tab-iZost0RL.js → port-forwarding-tab-DpoVwtlp.js} +1 -1
  29. package/dist/web/assets/{postgres-viewer-CabnbH06.js → postgres-viewer-Ku3X9rJQ.js} +3 -3
  30. package/dist/web/assets/{project-store-CczGNZyf.js → project-store-BYmQ0fDC.js} +1 -1
  31. package/dist/web/assets/radar-KQ55EAFF-DSn_ekR5.js +1 -0
  32. package/dist/web/assets/{scroll-area-DwWF9FpN.js → scroll-area-DW7L4Gnc.js} +1 -1
  33. package/dist/web/assets/{settings-store-CuYjM0FF.js → settings-store-B9axDbuA.js} +2 -2
  34. package/dist/web/assets/settings-tab-l-SopAs4.js +1 -0
  35. package/dist/web/assets/{sql-query-editor-CVEi0jLM.js → sql-query-editor-BnpKNGG0.js} +1 -1
  36. package/dist/web/assets/{sqlite-viewer-cGe2bGlF.js → sqlite-viewer-DzS8-nbv.js} +1 -1
  37. package/dist/web/assets/{tab-store-Jvy1eZGM.js → tab-store-B3M9hjho.js} +1 -1
  38. package/dist/web/assets/{terminal-tab-CPAMJoNK.js → terminal-tab-ERPwhU5O.js} +1 -1
  39. package/dist/web/assets/treemap-KZPCXAKY-C8puYVyN.js +1 -0
  40. package/dist/web/assets/use-blob-url-BK7zshV7.js +1 -0
  41. package/dist/web/assets/{use-monaco-theme-kjiAwvOp.js → use-monaco-theme-BERjR8IA.js} +1 -1
  42. package/dist/web/assets/{vendor-mermaid-CylkVm4U.js → vendor-mermaid-BlWh9BJO.js} +2 -2
  43. package/dist/web/assets/video-preview-BZenXc_U.js +1 -0
  44. package/dist/web/index.html +17 -17
  45. package/dist/web/sw.js +1 -1
  46. package/package.json +1 -1
  47. package/src/services/autostart-generator.ts +2 -0
  48. package/src/services/supervisor.ts +41 -10
  49. package/src/web/components/editor/audio-preview.tsx +26 -0
  50. package/src/web/components/editor/code-editor.tsx +21 -81
  51. package/src/web/components/editor/image-preview.tsx +23 -0
  52. package/src/web/components/editor/pdf-preview.tsx +32 -0
  53. package/src/web/components/editor/use-blob-url.ts +35 -0
  54. package/src/web/components/editor/video-preview.tsx +23 -0
  55. package/dist/web/assets/architecture-PBZL5I3N-BRW4VwMk.js +0 -1
  56. package/dist/web/assets/code-editor-CUyFYiwJ.js +0 -8
  57. package/dist/web/assets/gitGraph-HDMCJU4V-Bt68dqWT.js +0 -1
  58. package/dist/web/assets/index-iZHWllzQ.css +0 -2
  59. package/dist/web/assets/info-3K5VOQVL-ySD5z855.js +0 -1
  60. package/dist/web/assets/keybindings-store-qfYScgY0.js +0 -1
  61. package/dist/web/assets/packet-RMMSAZCW-CLxaXgIf.js +0 -1
  62. package/dist/web/assets/pie-UPGHQEXC-C9wPZfkn.js +0 -1
  63. package/dist/web/assets/radar-KQ55EAFF-DxEpzVN_.js +0 -1
  64. package/dist/web/assets/settings-tab-D2th4Hfj.js +0 -1
  65. package/dist/web/assets/treemap-KZPCXAKY-yelcZZqO.js +0 -1
  66. /package/dist/web/assets/{api-client-C3tXCh0r.js → api-client-Bn-Pi9k5.js} +0 -0
  67. /package/dist/web/assets/{csv-parser-BAa56Nnn.js → csv-parser--2WJNgS7.js} +0 -0
  68. /package/dist/web/assets/{dist-On3hz9_g.js → dist-im4ynINo.js} +0 -0
  69. /package/dist/web/assets/{katex-Bbu770d9.js → katex-CKoArbIw.js} +0 -0
  70. /package/dist/web/assets/{lib-BqkcKGFq.js → lib-DQHnkzGy.js} +0 -0
  71. /package/dist/web/assets/{react-BkWDCPD7.js → react-GqWghJ-L.js} +0 -0
  72. /package/dist/web/assets/{refresh-cw-CSFrDtiu.js → refresh-cw-LlbZDJpO.js} +0 -0
  73. /package/dist/web/assets/{sql-completion-provider-D3acAhav.js → sql-completion-provider-C3cq9j99.js} +0 -0
  74. /package/dist/web/assets/{table-DbSviOmw.js → table-Dq575bPF.js} +0 -0
  75. /package/dist/web/assets/{text-wrap-DzvCTq_i.js → text-wrap-Cn6BNQfq.js} +0 -0
  76. /package/dist/web/assets/{trash-2-BgDIBl6f.js → trash-2-CJYoLw7Q.js} +0 -0
  77. /package/dist/web/assets/{utils-ChWX7pZv.js → utils-CTg5uAYR.js} +0 -0
  78. /package/dist/web/assets/{vendor-xterm-B9BUAFKA.js → vendor-xterm-CU2c3f0A.js} +0 -0
@@ -0,0 +1 @@
1
+ import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-BwzaQ50n.js";import"./api-client-Bn-Pi9k5.js";import{M as n}from"./index-B1GZswlR.js";import{t as r}from"./use-blob-url-BK7zshV7.js";var i=e();function a({filePath:e,projectName:a}){let{blobUrl:o,error:s}=r(e,a);return s?(0,i.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,i.jsx)(t,{className:`size-10 text-text-subtle`}),(0,i.jsx)(`p`,{className:`text-sm`,children:`Failed to load video.`})]}):o?(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,i.jsx)(`video`,{src:o,controls:!0,className:`max-w-full max-h-full`})}):(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,i.jsx)(n,{className:`size-5 animate-spin text-text-subtle`})})}export{a as VideoPreview};
@@ -39,32 +39,32 @@
39
39
  <link rel="preconnect" href="https://fonts.googleapis.com" />
40
40
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
41
41
  <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
42
- <script type="module" crossorigin src="/assets/index-oqP6ldWs.js"></script>
42
+ <script type="module" crossorigin src="/assets/index-B1GZswlR.js"></script>
43
43
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
44
- <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-CylkVm4U.js">
44
+ <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-BlWh9BJO.js">
45
45
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
46
46
  <link rel="modulepreload" crossorigin href="/assets/vendor-ui-B-T_damt.js">
47
- <link rel="modulepreload" crossorigin href="/assets/utils-ChWX7pZv.js">
47
+ <link rel="modulepreload" crossorigin href="/assets/utils-CTg5uAYR.js">
48
48
  <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-BjHrJDVb.js">
49
49
  <link rel="modulepreload" crossorigin href="/assets/x-DlFGzN8d.js">
50
- <link rel="modulepreload" crossorigin href="/assets/input-CHRMley8.js">
51
- <link rel="modulepreload" crossorigin href="/assets/scroll-area-DwWF9FpN.js">
50
+ <link rel="modulepreload" crossorigin href="/assets/input-ClhO__YM.js">
51
+ <link rel="modulepreload" crossorigin href="/assets/scroll-area-DW7L4Gnc.js">
52
52
  <link rel="modulepreload" crossorigin href="/assets/dist-C5IgeqrV.js">
53
53
  <link rel="modulepreload" crossorigin href="/assets/plus-51UQ45rf.js">
54
- <link rel="modulepreload" crossorigin href="/assets/refresh-cw-CSFrDtiu.js">
55
- <link rel="modulepreload" crossorigin href="/assets/trash-2-BgDIBl6f.js">
56
- <link rel="modulepreload" crossorigin href="/assets/api-client-C3tXCh0r.js">
57
- <link rel="modulepreload" crossorigin href="/assets/api-settings-2eTz4SgY.js">
58
- <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-D2vqiydT.js">
54
+ <link rel="modulepreload" crossorigin href="/assets/refresh-cw-LlbZDJpO.js">
55
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-CJYoLw7Q.js">
56
+ <link rel="modulepreload" crossorigin href="/assets/api-client-Bn-Pi9k5.js">
57
+ <link rel="modulepreload" crossorigin href="/assets/api-settings-C__hxGX2.js">
58
+ <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-C6FDY8qE.js">
59
59
  <link rel="modulepreload" crossorigin href="/assets/chevron-right-BzAdxJRG.js">
60
60
  <link rel="modulepreload" crossorigin href="/assets/database-D4DIhgi-.js">
61
- <link rel="modulepreload" crossorigin href="/assets/react-BkWDCPD7.js">
62
- <link rel="modulepreload" crossorigin href="/assets/extension-store-CkyOvGbF.js">
63
- <link rel="modulepreload" crossorigin href="/assets/keybindings-store-CpP5_miA.js">
64
- <link rel="modulepreload" crossorigin href="/assets/tab-store-Jvy1eZGM.js">
65
- <link rel="modulepreload" crossorigin href="/assets/project-store-CczGNZyf.js">
66
- <link rel="modulepreload" crossorigin href="/assets/settings-store-CuYjM0FF.js">
67
- <link rel="stylesheet" crossorigin href="/assets/index-iZHWllzQ.css">
61
+ <link rel="modulepreload" crossorigin href="/assets/react-GqWghJ-L.js">
62
+ <link rel="modulepreload" crossorigin href="/assets/extension-store-3yZYn07W.js">
63
+ <link rel="modulepreload" crossorigin href="/assets/keybindings-store-C9KsBH7z.js">
64
+ <link rel="modulepreload" crossorigin href="/assets/tab-store-B3M9hjho.js">
65
+ <link rel="modulepreload" crossorigin href="/assets/project-store-BYmQ0fDC.js">
66
+ <link rel="modulepreload" crossorigin href="/assets/settings-store-B9axDbuA.js">
67
+ <link rel="stylesheet" crossorigin href="/assets/index-C3ZstZ9f.css">
68
68
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
69
69
  <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
70
70
  <div id="root"></div>
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":"3e294bb82e02c2ea8cb5efe532598c1c","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/lib-BqkcKGFq.js"},{"revision":null,"url":"assets/input-CHRMley8.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/tab-store-Jvy1eZGM.js"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-Bt68dqWT.js"},{"revision":null,"url":"assets/terminal-tab-CPAMJoNK.js"},{"revision":null,"url":"assets/port-forwarding-tab-iZost0RL.js"},{"revision":null,"url":"assets/postgres-viewer-CabnbH06.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/ai-settings-section-D2vqiydT.js"},{"revision":null,"url":"assets/database-viewer-DK4PfwVJ.js"},{"revision":null,"url":"assets/index-oqP6ldWs.js"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/settings-store-CuYjM0FF.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-C9wPZfkn.js"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/sql-query-editor-CVEi0jLM.js"},{"revision":null,"url":"assets/settings-tab-D2th4Hfj.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-CLxaXgIf.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/trash-2-BgDIBl6f.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/esm-B99v94EE.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/api-settings-2eTz4SgY.js"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DxEpzVN_.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/vendor-xterm-B9BUAFKA.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/api-client-C3tXCh0r.js"},{"revision":null,"url":"assets/code-editor-CUyFYiwJ.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/use-monaco-theme-kjiAwvOp.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/extension-store-CkyOvGbF.js"},{"revision":null,"url":"assets/conflict-editor-DXnqiS4c.js"},{"revision":null,"url":"assets/info-3K5VOQVL-ySD5z855.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/sql-completion-provider-D3acAhav.js"},{"revision":null,"url":"assets/react-BkWDCPD7.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/markdown-renderer-BLhcNL42.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-BRW4VwMk.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-yelcZZqO.js"},{"revision":null,"url":"assets/sqlite-viewer-cGe2bGlF.js"},{"revision":null,"url":"assets/chat-tab-2znvnxzD.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/csv-parser-BAa56Nnn.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/project-store-CczGNZyf.js"},{"revision":null,"url":"assets/katex-Bbu770d9.js"},{"revision":null,"url":"assets/keybindings-store-qfYScgY0.js"},{"revision":null,"url":"assets/extension-webview-DIVrmdfu.js"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/text-wrap-DzvCTq_i.js"},{"revision":null,"url":"assets/diff-viewer-Cy4oX3mb.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/scroll-area-DwWF9FpN.js"},{"revision":null,"url":"assets/index-iZHWllzQ.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/keybindings-store-CpP5_miA.js"},{"revision":null,"url":"assets/csv-preview-D37K2LRd.js"},{"revision":null,"url":"assets/vendor-mermaid-CylkVm4U.js"},{"revision":null,"url":"assets/table-DbSviOmw.js"},{"revision":null,"url":"assets/dist-On3hz9_g.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":"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||`/`)}))});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.11.6",
3
+ "version": "0.11.8",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -134,6 +134,8 @@ Type=simple
134
134
  ExecStart=${execStart}
135
135
  Restart=on-failure
136
136
  RestartSec=5
137
+ TimeoutStopSec=10
138
+ KillMode=mixed
137
139
  ${envPath}
138
140
  WorkingDirectory=${homedir()}/.ppm
139
141
 
@@ -75,9 +75,8 @@ function log(level: string, msg: string) {
75
75
  const ts = new Date().toISOString();
76
76
  const line = `[${ts}] [${level}] [supervisor] ${msg}\n`;
77
77
  try { appendFileSync(logFile(), line); } catch {}
78
- if (level === "ERROR" || level === "FATAL") {
79
- process.stderr.write(line);
80
- }
78
+ // Always write supervisor logs to stderr so journalctl captures them
79
+ try { process.stderr.write(line); } catch {}
81
80
  }
82
81
 
83
82
  // ─── Backoff calc ──────────────────────────────────────────────────────
@@ -243,7 +242,18 @@ export async function spawnTunnel(port: number): Promise<void> {
243
242
  tunnelChild = null;
244
243
 
245
244
  if (shuttingDown) return;
245
+
246
+ const now = Date.now();
247
+ if (now - lastTunnelCrash > STABLE_WINDOW_MS) tunnelRestarts = 0;
248
+ lastTunnelCrash = now;
246
249
  tunnelRestarts++;
250
+
251
+ if (tunnelRestarts > MAX_RESTARTS) {
252
+ log("ERROR", `Tunnel exceeded ${MAX_RESTARTS} URL extraction failures, disabling tunnel`);
253
+ updateStatus({ shareUrl: null, tunnelPid: null });
254
+ return;
255
+ }
256
+
247
257
  const delay = backoffDelay(tunnelRestarts);
248
258
  log("WARN", `Tunnel failed, retry in ${delay}ms (#${tunnelRestarts})`);
249
259
  await Bun.sleep(delay);
@@ -276,7 +286,7 @@ export async function spawnTunnel(port: number): Promise<void> {
276
286
  }
277
287
 
278
288
  const delay = backoffDelay(tunnelRestarts);
279
- log("WARN", `Tunnel died (exit ${exitCode}, was ${deadUrl}), restart in ${delay}ms (#${tunnelRestarts})`);
289
+ log("WARN", `Tunnel process exited (code=${exitCode}, signal=${tunnelChild === null ? "killed" : "self"}, url=${deadUrl}), restart in ${delay}ms (#${tunnelRestarts})`);
280
290
  await Bun.sleep(delay);
281
291
 
282
292
  if (!shuttingDown) return spawnTunnel(port);
@@ -734,9 +744,20 @@ export function shutdown() {
734
744
  if (upgradeDelayTimer) clearTimeout(upgradeDelayTimer);
735
745
  if (cloudMonitorTimer) clearInterval(cloudMonitorTimer);
736
746
 
737
- if (serverChild) { try { serverChild.kill(); } catch {} }
738
- if (tunnelChild) { try { tunnelChild.kill(); } catch {} }
739
- if (adoptedTunnelPid) { try { process.kill(adoptedTunnelPid, "SIGTERM"); } catch {} }
747
+ // Use SIGKILL for children SIGTERM leaves grandchildren (Claude SDK, etc.)
748
+ // alive, causing systemd to wait 90s then SIGKILL the entire cgroup
749
+ if (serverChild) {
750
+ log("INFO", `Killing server child (PID: ${serverChild.pid})`);
751
+ try { serverChild.kill("SIGKILL"); } catch {}
752
+ }
753
+ if (tunnelChild) {
754
+ log("INFO", `Killing tunnel child (PID: ${tunnelChild.pid})`);
755
+ try { tunnelChild.kill("SIGKILL"); } catch {}
756
+ }
757
+ if (adoptedTunnelPid) {
758
+ log("INFO", `Killing adopted tunnel (PID: ${adoptedTunnelPid})`);
759
+ try { process.kill(adoptedTunnelPid, "SIGKILL"); } catch {}
760
+ }
740
761
  }
741
762
 
742
763
  // ─── Main entry ────────────────────────────────────────────────────────
@@ -788,9 +809,19 @@ export async function runSupervisor(opts: {
788
809
  _logFd = logFd;
789
810
  _opts = { port: opts.port, host: opts.host, share: opts.share };
790
811
 
791
- // Signal handlers
792
- process.on("SIGTERM", () => { shutdown(); process.exit(0); });
793
- process.on("SIGINT", () => { shutdown(); process.exit(0); });
812
+ // Signal handlers — force exit after 5s if process.exit doesn't work
813
+ const forceShutdown = (signal: string) => {
814
+ log("INFO", `${signal} received`);
815
+ shutdown();
816
+ // Safety net: force kill self if process.exit(0) doesn't terminate
817
+ setTimeout(() => {
818
+ log("WARN", `Force exit after ${signal} — process.exit(0) did not terminate`);
819
+ try { process.kill(process.pid, "SIGKILL"); } catch {}
820
+ }, 5000).unref();
821
+ process.exit(0);
822
+ };
823
+ process.on("SIGTERM", () => forceShutdown("SIGTERM"));
824
+ process.on("SIGINT", () => forceShutdown("SIGINT"));
794
825
 
795
826
  // SIGUSR2 = command file dispatch OR graceful server restart
796
827
  process.on("SIGUSR2", () => {
@@ -0,0 +1,26 @@
1
+ import { Loader2, FileWarning, Music } from "lucide-react";
2
+ import { useBlobUrl } from "./use-blob-url";
3
+ import { basename } from "@/lib/utils";
4
+
5
+ export function AudioPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
6
+ const { blobUrl, error } = useBlobUrl(filePath, projectName);
7
+
8
+ if (error) {
9
+ return (
10
+ <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
11
+ <FileWarning className="size-10 text-text-subtle" />
12
+ <p className="text-sm">Failed to load audio.</p>
13
+ </div>
14
+ );
15
+ }
16
+ if (!blobUrl) {
17
+ return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
18
+ }
19
+ return (
20
+ <div className="flex flex-col items-center justify-center h-full gap-4 p-4 bg-surface">
21
+ <Music className="size-16 text-text-subtle" />
22
+ <p className="text-sm text-text-secondary truncate max-w-xs">{basename(filePath)}</p>
23
+ <audio src={blobUrl} controls className="w-full max-w-md" />
24
+ </div>
25
+ );
26
+ }
@@ -1,14 +1,14 @@
1
1
  import { useEffect, useState, useCallback, useRef, useMemo, memo, lazy, Suspense } from "react";
2
2
  import Editor, { type OnMount } from "@monaco-editor/react";
3
3
  import type * as MonacoType from "monaco-editor";
4
- import { api, projectUrl, getAuthToken } from "@/lib/api-client";
4
+ import { api, projectUrl } from "@/lib/api-client";
5
5
  import { useShallow } from "zustand/react/shallow";
6
6
  import { useTabStore } from "@/stores/tab-store";
7
7
  import { usePanelStore } from "@/stores/panel-store";
8
8
  import { useSettingsStore } from "@/stores/settings-store";
9
9
  import { basename } from "@/lib/utils";
10
10
  import { useMonacoTheme } from "@/lib/use-monaco-theme";
11
- import { Loader2, FileWarning, ExternalLink, Play, Database } from "lucide-react";
11
+ import { Loader2, FileWarning, Play, Database } from "lucide-react";
12
12
  import { EditorBreadcrumb } from "./editor-breadcrumb";
13
13
  import { EditorToolbar } from "./editor-toolbar";
14
14
  import { SaveAsDialog } from "./save-as-dialog";
@@ -19,9 +19,17 @@ const MarkdownRenderer = lazy(() =>
19
19
  import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
20
20
  );
21
21
  const CsvPreview = lazy(() => import("./csv-preview").then((m) => ({ default: m.CsvPreview })));
22
+ const ImagePreview = lazy(() => import("./image-preview").then((m) => ({ default: m.ImagePreview })));
23
+ const PdfPreview = lazy(() => import("./pdf-preview").then((m) => ({ default: m.PdfPreview })));
24
+ const VideoPreview = lazy(() => import("./video-preview").then((m) => ({ default: m.VideoPreview })));
25
+ const AudioPreview = lazy(() => import("./audio-preview").then((m) => ({ default: m.AudioPreview })));
22
26
 
23
27
  /** Image extensions renderable inline */
24
28
  const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "ico"]);
29
+ /** Video extensions playable inline */
30
+ const VIDEO_EXTS = new Set(["mp4", "webm", "mov", "ogg", "avi", "mkv"]);
31
+ /** Audio extensions playable inline */
32
+ const AUDIO_EXTS = new Set(["mp3", "wav", "flac", "aac", "m4a", "wma"]);
25
33
  /** SQLite extensions — redirect to sqlite viewer */
26
34
  const SQLITE_EXTS = new Set(["db", "sqlite", "sqlite3"]);
27
35
 
@@ -75,6 +83,8 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
75
83
  const ext = filePath ? getFileExt(filePath) : "";
76
84
  const isImage = IMAGE_EXTS.has(ext);
77
85
  const isPdf = ext === "pdf";
86
+ const isVideo = VIDEO_EXTS.has(ext);
87
+ const isAudio = AUDIO_EXTS.has(ext);
78
88
  const isSqlite = SQLITE_EXTS.has(ext);
79
89
  const isMarkdown = ext === "md" || ext === "mdx";
80
90
  const isCsv = ext === "csv";
@@ -215,7 +225,7 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
215
225
  }
216
226
  if (!filePath) return;
217
227
  if (!isExternalFile && !projectName) return;
218
- if (isImage || isPdf) { setLoading(false); return; }
228
+ if (isImage || isPdf || isVideo || isAudio) { setLoading(false); return; }
219
229
 
220
230
  setLoading(true);
221
231
  setError(null);
@@ -451,8 +461,10 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
451
461
  );
452
462
  }
453
463
 
454
- if (isImage) return <ImagePreview filePath={filePath!} projectName={projectName!} />;
455
- if (isPdf) return <PdfPreview filePath={filePath!} projectName={projectName!} />;
464
+ if (isImage) return <Suspense fallback={<LoadingSpinner />}><ImagePreview filePath={filePath!} projectName={projectName!} /></Suspense>;
465
+ if (isPdf) return <Suspense fallback={<LoadingSpinner />}><PdfPreview filePath={filePath!} projectName={projectName!} /></Suspense>;
466
+ if (isVideo) return <Suspense fallback={<LoadingSpinner />}><VideoPreview filePath={filePath!} projectName={projectName!} /></Suspense>;
467
+ if (isAudio) return <Suspense fallback={<LoadingSpinner />}><AudioPreview filePath={filePath!} projectName={projectName!} /></Suspense>;
456
468
 
457
469
  if (encoding === "base64") {
458
470
  return (
@@ -580,6 +592,10 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
580
592
  );
581
593
  });
582
594
 
595
+ function LoadingSpinner() {
596
+ return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
597
+ }
598
+
583
599
  function MarkdownPreview({ content }: { content: string }) {
584
600
  return (
585
601
  <Suspense fallback={<div className="animate-pulse h-4 bg-muted rounded m-4" />}>
@@ -588,79 +604,3 @@ function MarkdownPreview({ content }: { content: string }) {
588
604
  );
589
605
  }
590
606
 
591
- function ImagePreview({ filePath, projectName }: { filePath: string; projectName: string }) {
592
- const [blobUrl, setBlobUrl] = useState<string | null>(null);
593
- const [error, setError] = useState(false);
594
-
595
- useEffect(() => {
596
- let revoke: string | undefined;
597
- const url = `${projectUrl(projectName)}/files/raw?path=${encodeURIComponent(filePath)}`;
598
- const token = getAuthToken();
599
- fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
600
- .then((r) => { if (!r.ok) throw new Error("Failed"); return r.blob(); })
601
- .then((blob) => { const u = URL.createObjectURL(blob); revoke = u; setBlobUrl(u); })
602
- .catch(() => setError(true));
603
- return () => { if (revoke) URL.revokeObjectURL(revoke); };
604
- }, [filePath, projectName]);
605
-
606
- if (error) {
607
- return (
608
- <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
609
- <FileWarning className="size-10 text-text-subtle" />
610
- <p className="text-sm">Failed to load image.</p>
611
- </div>
612
- );
613
- }
614
- if (!blobUrl) {
615
- return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
616
- }
617
- return (
618
- <div className="flex items-center justify-center h-full p-4 bg-surface overflow-auto">
619
- <img src={blobUrl} alt={filePath} className="max-w-full max-h-full object-contain" />
620
- </div>
621
- );
622
- }
623
-
624
- function PdfPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
625
- const [blobUrl, setBlobUrl] = useState<string | null>(null);
626
- const [error, setError] = useState(false);
627
-
628
- useEffect(() => {
629
- let revoke: string | undefined;
630
- const url = `${projectUrl(projectName)}/files/raw?path=${encodeURIComponent(filePath)}`;
631
- const token = getAuthToken();
632
- fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
633
- .then((r) => { if (!r.ok) throw new Error("Failed"); return r.blob(); })
634
- .then((blob) => {
635
- const u = URL.createObjectURL(new Blob([blob], { type: "application/pdf" }));
636
- revoke = u; setBlobUrl(u);
637
- })
638
- .catch(() => setError(true));
639
- return () => { if (revoke) URL.revokeObjectURL(revoke); };
640
- }, [filePath, projectName]);
641
-
642
- const openInNewTab = useCallback(() => { if (blobUrl) window.open(blobUrl, "_blank"); }, [blobUrl]);
643
-
644
- if (error) {
645
- return (
646
- <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
647
- <FileWarning className="size-10 text-text-subtle" />
648
- <p className="text-sm">Failed to load PDF.</p>
649
- </div>
650
- );
651
- }
652
- if (!blobUrl) {
653
- return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
654
- }
655
- return (
656
- <div className="flex flex-col h-full">
657
- <div className="flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0">
658
- <span className="text-xs text-text-secondary truncate">{filePath}</span>
659
- <button onClick={openInNewTab} className="flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors">
660
- <ExternalLink className="size-3" /> Open in new tab
661
- </button>
662
- </div>
663
- <iframe src={blobUrl} title={filePath} className="flex-1 w-full border-none" />
664
- </div>
665
- );
666
- }
@@ -0,0 +1,23 @@
1
+ import { Loader2, FileWarning } from "lucide-react";
2
+ import { useBlobUrl } from "./use-blob-url";
3
+
4
+ export function ImagePreview({ filePath, projectName }: { filePath: string; projectName: string }) {
5
+ const { blobUrl, error } = useBlobUrl(filePath, projectName);
6
+
7
+ if (error) {
8
+ return (
9
+ <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
10
+ <FileWarning className="size-10 text-text-subtle" />
11
+ <p className="text-sm">Failed to load image.</p>
12
+ </div>
13
+ );
14
+ }
15
+ if (!blobUrl) {
16
+ return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
17
+ }
18
+ return (
19
+ <div className="flex items-center justify-center h-full p-4 bg-surface overflow-auto">
20
+ <img src={blobUrl} alt={filePath} className="max-w-full max-h-full object-contain" />
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,32 @@
1
+ import { useCallback } from "react";
2
+ import { Loader2, FileWarning, ExternalLink } from "lucide-react";
3
+ import { useBlobUrl } from "./use-blob-url";
4
+
5
+ export function PdfPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
6
+ const { blobUrl, error } = useBlobUrl(filePath, projectName, "application/pdf");
7
+
8
+ const openInNewTab = useCallback(() => { if (blobUrl) window.open(blobUrl, "_blank"); }, [blobUrl]);
9
+
10
+ if (error) {
11
+ return (
12
+ <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
13
+ <FileWarning className="size-10 text-text-subtle" />
14
+ <p className="text-sm">Failed to load PDF.</p>
15
+ </div>
16
+ );
17
+ }
18
+ if (!blobUrl) {
19
+ return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
20
+ }
21
+ return (
22
+ <div className="flex flex-col h-full">
23
+ <div className="flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0">
24
+ <span className="text-xs text-text-secondary truncate">{filePath}</span>
25
+ <button onClick={openInNewTab} className="flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors">
26
+ <ExternalLink className="size-3" /> Open in new tab
27
+ </button>
28
+ </div>
29
+ <iframe src={blobUrl} title={filePath} className="flex-1 w-full border-none" />
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,35 @@
1
+ import { useEffect, useState } from "react";
2
+ import { projectUrl, getAuthToken } from "@/lib/api-client";
3
+
4
+ /** Shared hook: fetch a project file as a blob URL via /files/raw endpoint. */
5
+ export function useBlobUrl(
6
+ filePath: string,
7
+ projectName: string,
8
+ mimeOverride?: string,
9
+ ) {
10
+ const [blobUrl, setBlobUrl] = useState<string | null>(null);
11
+ const [error, setError] = useState(false);
12
+
13
+ useEffect(() => {
14
+ let revoke: string | undefined;
15
+ const url = `${projectUrl(projectName)}/files/raw?path=${encodeURIComponent(filePath)}`;
16
+ const token = getAuthToken();
17
+ fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
18
+ .then((r) => {
19
+ if (!r.ok) throw new Error("Failed");
20
+ return r.blob();
21
+ })
22
+ .then((blob) => {
23
+ const final = mimeOverride ? new Blob([blob], { type: mimeOverride }) : blob;
24
+ const u = URL.createObjectURL(final);
25
+ revoke = u;
26
+ setBlobUrl(u);
27
+ })
28
+ .catch(() => setError(true));
29
+ return () => {
30
+ if (revoke) URL.revokeObjectURL(revoke);
31
+ };
32
+ }, [filePath, projectName, mimeOverride]);
33
+
34
+ return { blobUrl, error };
35
+ }
@@ -0,0 +1,23 @@
1
+ import { Loader2, FileWarning } from "lucide-react";
2
+ import { useBlobUrl } from "./use-blob-url";
3
+
4
+ export function VideoPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
5
+ const { blobUrl, error } = useBlobUrl(filePath, projectName);
6
+
7
+ if (error) {
8
+ return (
9
+ <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
10
+ <FileWarning className="size-10 text-text-subtle" />
11
+ <p className="text-sm">Failed to load video.</p>
12
+ </div>
13
+ );
14
+ }
15
+ if (!blobUrl) {
16
+ return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
17
+ }
18
+ return (
19
+ <div className="flex items-center justify-center h-full p-4 bg-surface overflow-auto">
20
+ <video src={blobUrl} controls className="max-w-full max-h-full" />
21
+ </div>
22
+ );
23
+ }
@@ -1 +0,0 @@
1
- import{W as e}from"./vendor-mermaid-CylkVm4U.js";export{e as createArchitectureServices};