@hienlh/ppm 0.13.7 → 0.13.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 (84) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/dist/web/assets/ai-settings-section-CHgpQ_OP.js +1 -0
  5. package/dist/web/assets/{api-settings-t7Leca7J.js → api-settings-D0_eiIYv.js} +1 -1
  6. package/dist/web/assets/architecture-PBZL5I3N-WMbLpD5Y.js +1 -0
  7. package/dist/web/assets/{audio-preview-BR7DoH0l.js → audio-preview-DQbX8gfL.js} +1 -1
  8. package/dist/web/assets/{chat-tab-8Bvn7PHt.js → chat-tab-BJQT9kie.js} +9 -9
  9. package/dist/web/assets/{code-editor-B-F5VdzM.js → code-editor-CeKTvfyz.js} +4 -4
  10. package/dist/web/assets/{conflict-editor-C-lUw4gv.js → conflict-editor-BKwJLX0D.js} +1 -1
  11. package/dist/web/assets/{csv-preview-C9qGhDlb.js → csv-preview-D5lmgVEy.js} +1 -1
  12. package/dist/web/assets/database-viewer-DixWWvjx.js +5 -0
  13. package/dist/web/assets/{diff-viewer-I8qs3Nb-.js → diff-viewer-SAtaBwNI.js} +1 -1
  14. package/dist/web/assets/{esm-B3je8j5P.js → esm-nXReYVnB.js} +1 -1
  15. package/dist/web/assets/{extension-webview-Dt9bKs0C.js → extension-webview-PiV4bKJ1.js} +1 -1
  16. package/dist/web/assets/{file-store-BgZggznw.js → file-store-BrbCNyLm.js} +1 -1
  17. package/dist/web/assets/gitGraph-HDMCJU4V-BdPTuzO3.js +1 -0
  18. package/dist/web/assets/{image-preview-C-w-CK5s.js → image-preview-CbFFD9BS.js} +1 -1
  19. package/dist/web/assets/{index-CEI0tfaL.css → index-C1RBJe0a.css} +1 -1
  20. package/dist/web/assets/index-ZFyltHwi.js +27 -0
  21. package/dist/web/assets/info-3K5VOQVL-MHX_1JfR.js +1 -0
  22. package/dist/web/assets/{input-bGJExpJZ.js → input-BMvRUOr7.js} +1 -1
  23. package/dist/web/assets/keybindings-store-D0C-Pq2o.js +1 -0
  24. package/dist/web/assets/{markdown-renderer-ySnJPmc1.js → markdown-renderer-CHWA0KAo.js} +3 -3
  25. package/dist/web/assets/packet-RMMSAZCW-CreFbf9A.js +1 -0
  26. package/dist/web/assets/{pdf-preview-BoQTv6B2.js → pdf-preview-DQMdjqa2.js} +1 -1
  27. package/dist/web/assets/pie-UPGHQEXC-CnaHXUh8.js +1 -0
  28. package/dist/web/assets/port-forwarding-tab-9BpNC9_7.js +1 -0
  29. package/dist/web/assets/{postgres-viewer-CBntLqXY.js → postgres-viewer-Bm5T51n6.js} +3 -3
  30. package/dist/web/assets/radar-KQ55EAFF-UxsdRHvt.js +1 -0
  31. package/dist/web/assets/{scroll-area-D0EQpAH2.js → scroll-area-BEllam7_.js} +1 -1
  32. package/dist/web/assets/{settings-store-CdcSAgEZ.js → settings-store-BHBb62gq.js} +2 -2
  33. package/dist/web/assets/settings-tab-BUstSDLR.js +1 -0
  34. package/dist/web/assets/sparkles-B0mRBy_j.js +1 -0
  35. package/dist/web/assets/{sql-query-editor-vpD0I0KG.js → sql-query-editor-CMQpaOjA.js} +1 -1
  36. package/dist/web/assets/{sqlite-viewer-CjtNrQ7C.js → sqlite-viewer-C7rhO4bn.js} +1 -1
  37. package/dist/web/assets/tab-store-0rGchMXr.js +1 -0
  38. package/dist/web/assets/terminal-tab-Xtj6RN0d.js +1 -0
  39. package/dist/web/assets/treemap-KZPCXAKY-CBVPi4NV.js +1 -0
  40. package/dist/web/assets/{use-blob-url-BgxxT-n_.js → use-blob-url-Hn6n1730.js} +1 -1
  41. package/dist/web/assets/{use-monaco-theme-dtPsv6sh.js → use-monaco-theme-CP-vyTF8.js} +1 -1
  42. package/dist/web/assets/{vendor-mermaid-DCxaaPi4.js → vendor-mermaid-CMiurk2b.js} +2 -2
  43. package/dist/web/assets/{video-preview-DxTnfuSQ.js → video-preview-BLI_RruT.js} +1 -1
  44. package/dist/web/index.html +14 -14
  45. package/dist/web/sw.js +1 -1
  46. package/package.json +1 -1
  47. package/src/index.ts +0 -0
  48. package/src/server/ws/terminal.ts +4 -0
  49. package/src/services/terminal.service.ts +4 -1
  50. package/src/web/components/database/data-grid.tsx +117 -17
  51. package/src/web/components/editor/code-editor.tsx +2 -2
  52. package/src/web/components/layout/draggable-tab.tsx +1 -0
  53. package/src/web/components/terminal/terminal-tab.tsx +19 -8
  54. package/src/web/hooks/use-terminal.ts +22 -2
  55. package/src/web/stores/panel-utils.ts +1 -0
  56. package/bun.lock +0 -2062
  57. package/bunfig.toml +0 -2
  58. package/dist/web/assets/ai-settings-section-DeW4WN43.js +0 -1
  59. package/dist/web/assets/architecture-PBZL5I3N-Dy3PgD6O.js +0 -1
  60. package/dist/web/assets/database-viewer-mLY7oMC_.js +0 -2
  61. package/dist/web/assets/gitGraph-HDMCJU4V-Bu1SIFFq.js +0 -1
  62. package/dist/web/assets/index-CFz4k7zO.js +0 -27
  63. package/dist/web/assets/info-3K5VOQVL-DzfAxmVd.js +0 -1
  64. package/dist/web/assets/keybindings-store-CWX97luK.js +0 -1
  65. package/dist/web/assets/packet-RMMSAZCW-DpzHf4xp.js +0 -1
  66. package/dist/web/assets/pie-UPGHQEXC-BpzFCKJ8.js +0 -1
  67. package/dist/web/assets/port-forwarding-tab-CjlX-0D2.js +0 -1
  68. package/dist/web/assets/radar-KQ55EAFF-DAxWKxM4.js +0 -1
  69. package/dist/web/assets/settings-tab-Djw-bqxG.js +0 -1
  70. package/dist/web/assets/tab-store-Jvy1eZGM.js +0 -1
  71. package/dist/web/assets/terminal-tab-bKLoy06f.js +0 -1
  72. package/dist/web/assets/treemap-KZPCXAKY-D6dgXbAe.js +0 -1
  73. /package/dist/web/assets/{api-client-r4nyVy7H.js → api-client-Dvzcc_EO.js} +0 -0
  74. /package/dist/web/assets/{csv-parser-DxVplKKB.js → csv-parser-DO0dz4x_.js} +0 -0
  75. /package/dist/web/assets/{dist-BqoEabX7.js → dist-CGvx1c8C.js} +0 -0
  76. /package/dist/web/assets/{katex-bpagxk3Z.js → katex-BFE6i_OH.js} +0 -0
  77. /package/dist/web/assets/{lib-BqkcKGFq.js → lib-D_kRA9p6.js} +0 -0
  78. /package/dist/web/assets/{react-BkWDCPD7.js → react-GqWghJ-L.js} +0 -0
  79. /package/dist/web/assets/{sql-completion-provider-EzHOQLfo.js → sql-completion-provider-tCzZfqWs.js} +0 -0
  80. /package/dist/web/assets/{table-DbSviOmw.js → table-Dq575bPF.js} +0 -0
  81. /package/dist/web/assets/{text-wrap-DzvCTq_i.js → text-wrap-Cn6BNQfq.js} +0 -0
  82. /package/dist/web/assets/{trash-2-BgDIBl6f.js → trash-2-CJYoLw7Q.js} +0 -0
  83. /package/dist/web/assets/{utils-ChWX7pZv.js → utils-CTg5uAYR.js} +0 -0
  84. /package/dist/web/assets/{vendor-xterm-D7SePDJp.js → vendor-xterm-u3AZMvTx.js} +0 -0
@@ -1 +1 @@
1
- import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-Baz81y5z.js";import"./api-client-r4nyVy7H.js";import{W as n}from"./index-CFz4k7zO.js";import{t as r}from"./use-blob-url-BgxxT-n_.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};
1
+ import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-Baz81y5z.js";import"./api-client-Dvzcc_EO.js";import{G as n}from"./index-ZFyltHwi.js";import{t as r}from"./use-blob-url-Hn6n1730.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,30 +39,30 @@
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-CFz4k7zO.js"></script>
42
+ <script type="module" crossorigin src="/assets/index-ZFyltHwi.js"></script>
43
43
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
44
- <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-DCxaaPi4.js">
44
+ <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-CMiurk2b.js">
45
45
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
46
46
  <link rel="modulepreload" crossorigin href="/assets/vendor-ui-B-89Uj8i.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-BtqbfkR7.js">
50
- <link rel="modulepreload" crossorigin href="/assets/input-bGJExpJZ.js">
51
- <link rel="modulepreload" crossorigin href="/assets/react-BkWDCPD7.js">
52
- <link rel="modulepreload" crossorigin href="/assets/api-client-r4nyVy7H.js">
53
- <link rel="modulepreload" crossorigin href="/assets/settings-store-CdcSAgEZ.js">
54
- <link rel="modulepreload" crossorigin href="/assets/scroll-area-D0EQpAH2.js">
50
+ <link rel="modulepreload" crossorigin href="/assets/input-BMvRUOr7.js">
51
+ <link rel="modulepreload" crossorigin href="/assets/react-GqWghJ-L.js">
52
+ <link rel="modulepreload" crossorigin href="/assets/api-client-Dvzcc_EO.js">
53
+ <link rel="modulepreload" crossorigin href="/assets/settings-store-BHBb62gq.js">
54
+ <link rel="modulepreload" crossorigin href="/assets/scroll-area-BEllam7_.js">
55
55
  <link rel="modulepreload" crossorigin href="/assets/dist-D7KGU7Vl.js">
56
56
  <link rel="modulepreload" crossorigin href="/assets/plus-51UQ45rf.js">
57
57
  <link rel="modulepreload" crossorigin href="/assets/refresh-cw-CSFrDtiu.js">
58
- <link rel="modulepreload" crossorigin href="/assets/trash-2-BgDIBl6f.js">
59
- <link rel="modulepreload" crossorigin href="/assets/api-settings-t7Leca7J.js">
60
- <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-DeW4WN43.js">
58
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-CJYoLw7Q.js">
59
+ <link rel="modulepreload" crossorigin href="/assets/api-settings-D0_eiIYv.js">
60
+ <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-CHgpQ_OP.js">
61
61
  <link rel="modulepreload" crossorigin href="/assets/chevron-right-BzAdxJRG.js">
62
62
  <link rel="modulepreload" crossorigin href="/assets/database-DCT0OjgQ.js">
63
- <link rel="modulepreload" crossorigin href="/assets/file-store-BgZggznw.js">
64
- <link rel="modulepreload" crossorigin href="/assets/tab-store-Jvy1eZGM.js">
65
- <link rel="stylesheet" crossorigin href="/assets/index-CEI0tfaL.css">
63
+ <link rel="modulepreload" crossorigin href="/assets/file-store-BrbCNyLm.js">
64
+ <link rel="modulepreload" crossorigin href="/assets/tab-store-0rGchMXr.js">
65
+ <link rel="stylesheet" crossorigin href="/assets/index-C1RBJe0a.css">
66
66
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
67
67
  <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
68
68
  <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":"799a9d7b8dd64119616c0b2d70cd7a95","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/lib-BqkcKGFq.js"},{"revision":null,"url":"assets/dist-D7KGU7Vl.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/architecture-PBZL5I3N-Dy3PgD6O.js"},{"revision":null,"url":"assets/pdf-preview-BoQTv6B2.js"},{"revision":null,"url":"assets/sql-query-editor-vpD0I0KG.js"},{"revision":null,"url":"assets/conflict-editor-C-lUw4gv.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-DpzHf4xp.js"},{"revision":null,"url":"assets/code-editor-B-F5VdzM.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/scroll-area-D0EQpAH2.js"},{"revision":null,"url":"assets/vendor-xterm-D7SePDJp.js"},{"revision":null,"url":"assets/settings-tab-Djw-bqxG.js"},{"revision":null,"url":"assets/file-exclamation-point-Baz81y5z.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/sqlite-viewer-CjtNrQ7C.js"},{"revision":null,"url":"assets/csv-preview-C9qGhDlb.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/file-store-BgZggznw.js"},{"revision":null,"url":"assets/x-BtqbfkR7.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/ai-settings-section-DeW4WN43.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BpzFCKJ8.js"},{"revision":null,"url":"assets/vendor-mermaid-DCxaaPi4.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/database-viewer-mLY7oMC_.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/trash-2-BgDIBl6f.js"},{"revision":null,"url":"assets/use-blob-url-BgxxT-n_.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/dist-BqoEabX7.js"},{"revision":null,"url":"assets/port-forwarding-tab-CjlX-0D2.js"},{"revision":null,"url":"assets/input-bGJExpJZ.js"},{"revision":null,"url":"assets/diff-viewer-I8qs3Nb-.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/api-settings-t7Leca7J.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/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DAxWKxM4.js"},{"revision":null,"url":"assets/markdown-renderer-ySnJPmc1.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/github-dark-dimmed.min-BrpRStFV.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/sql-completion-provider-EzHOQLfo.js"},{"revision":null,"url":"assets/esm-B3je8j5P.js"},{"revision":null,"url":"assets/image-preview-C-w-CK5s.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-D6dgXbAe.js"},{"revision":null,"url":"assets/extension-webview-Dt9bKs0C.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/chat-tab-8Bvn7PHt.js"},{"revision":null,"url":"assets/react-BkWDCPD7.js"},{"revision":null,"url":"assets/database-DCT0OjgQ.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-Bu1SIFFq.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/api-client-r4nyVy7H.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/keybindings-store-CWX97luK.js"},{"revision":null,"url":"assets/info-3K5VOQVL-DzfAxmVd.js"},{"revision":null,"url":"assets/settings-store-CdcSAgEZ.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/terminal-tab-bKLoy06f.js"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/index-CEI0tfaL.css"},{"revision":null,"url":"assets/katex-bpagxk3Z.js"},{"revision":null,"url":"assets/index-CFz4k7zO.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/video-preview-DxTnfuSQ.js"},{"revision":null,"url":"assets/vendor-ui-B-89Uj8i.js"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/audio-preview-BR7DoH0l.js"},{"revision":null,"url":"assets/text-wrap-DzvCTq_i.js"},{"revision":null,"url":"assets/csv-parser-DxVplKKB.js"},{"revision":null,"url":"assets/postgres-viewer-CBntLqXY.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/use-monaco-theme-dtPsv6sh.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/table-DbSviOmw.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":"0243ed2ed58acd13176b487d17e042d9","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/dist-D7KGU7Vl.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/scroll-area-BEllam7_.js"},{"revision":null,"url":"assets/extension-webview-PiV4bKJ1.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-BdPTuzO3.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CnaHXUh8.js"},{"revision":null,"url":"assets/audio-preview-DQbX8gfL.js"},{"revision":null,"url":"assets/chat-tab-BJQT9kie.js"},{"revision":null,"url":"assets/ai-settings-section-CHgpQ_OP.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/code-editor-CeKTvfyz.js"},{"revision":null,"url":"assets/settings-store-BHBb62gq.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/file-exclamation-point-Baz81y5z.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/diff-viewer-SAtaBwNI.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/info-3K5VOQVL-MHX_1JfR.js"},{"revision":null,"url":"assets/conflict-editor-BKwJLX0D.js"},{"revision":null,"url":"assets/lib-D_kRA9p6.js"},{"revision":null,"url":"assets/x-BtqbfkR7.js"},{"revision":null,"url":"assets/dist-CGvx1c8C.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/postgres-viewer-Bm5T51n6.js"},{"revision":null,"url":"assets/api-settings-D0_eiIYv.js"},{"revision":null,"url":"assets/port-forwarding-tab-9BpNC9_7.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-CBVPi4NV.js"},{"revision":null,"url":"assets/csv-preview-D5lmgVEy.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/index-C1RBJe0a.css"},{"revision":null,"url":"assets/radar-KQ55EAFF-UxsdRHvt.js"},{"revision":null,"url":"assets/api-client-Dvzcc_EO.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-WMbLpD5Y.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/tab-store-0rGchMXr.js"},{"revision":null,"url":"assets/settings-tab-BUstSDLR.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/database-viewer-DixWWvjx.js"},{"revision":null,"url":"assets/pdf-preview-DQMdjqa2.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/sql-query-editor-CMQpaOjA.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/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/markdown-renderer-CHWA0KAo.js"},{"revision":null,"url":"assets/sqlite-viewer-C7rhO4bn.js"},{"revision":null,"url":"assets/input-BMvRUOr7.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/esm-nXReYVnB.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/github-dark-dimmed.min-BrpRStFV.css"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/katex-BFE6i_OH.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/keybindings-store-D0C-Pq2o.js"},{"revision":null,"url":"assets/use-monaco-theme-CP-vyTF8.js"},{"revision":null,"url":"assets/file-store-BrbCNyLm.js"},{"revision":null,"url":"assets/use-blob-url-Hn6n1730.js"},{"revision":null,"url":"assets/utils-CTg5uAYR.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/database-DCT0OjgQ.js"},{"revision":null,"url":"assets/vendor-mermaid-CMiurk2b.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/csv-parser-DO0dz4x_.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/terminal-tab-Xtj6RN0d.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/sparkles-B0mRBy_j.js"},{"revision":null,"url":"assets/vendor-ui-B-89Uj8i.js"},{"revision":null,"url":"assets/index-ZFyltHwi.js"},{"revision":null,"url":"assets/vendor-xterm-u3AZMvTx.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-CreFbf9A.js"},{"revision":null,"url":"assets/sql-completion-provider-tCzZfqWs.js"},{"revision":null,"url":"assets/image-preview-CbFFD9BS.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/video-preview-BLI_RruT.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"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.13.7",
3
+ "version": "0.13.8",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
File without changes
@@ -83,6 +83,10 @@ export const terminalWebSocket = {
83
83
 
84
84
  close(ws: { data: { type: string; id: string } }) {
85
85
  const sessionId = ws.data.id;
86
+ const session = terminalService.get(sessionId);
87
+ // Only clean up if this WS is still the active connection.
88
+ // Prevents race: stale WS close fires after new WS open, removing the new listener.
89
+ if (!session || session.ws !== ws) return;
86
90
  terminalService.removeOutputListener(sessionId);
87
91
  terminalService.setDisconnected(sessionId);
88
92
  },
@@ -144,7 +144,10 @@ class TerminalService {
144
144
  };
145
145
  const onExit = () => {
146
146
  const listener = this.outputListeners.get(id);
147
- if (listener) listener(id, "\r\n[Process exited]\r\n");
147
+ if (listener) {
148
+ listener(id, "\r\n[Process exited]\r\n");
149
+ listener(id, JSON.stringify({ type: "exited" }));
150
+ }
148
151
  };
149
152
 
150
153
  const pty = isWindows
@@ -1,7 +1,9 @@
1
1
  import { useState, useCallback, useMemo, useRef, memo, useEffect } from "react";
2
- import { Loader2, ChevronLeft, ChevronRight, ChevronUp, ChevronDown, Trash2, Plus, Search, X, Eye, Filter, Pin, PinOff, Columns3 } from "lucide-react";
2
+ import Editor from "@monaco-editor/react";
3
+ import { Loader2, ChevronLeft, ChevronRight, ChevronUp, ChevronDown, Trash2, Plus, Search, X, Eye, ExternalLink, WrapText, Sparkles, Filter, Pin, PinOff, Columns3 } from "lucide-react";
3
4
  import { useShallow } from "zustand/react/shallow";
4
5
  import { useTabStore } from "@/stores/tab-store";
6
+ import { useMonacoTheme } from "@/lib/use-monaco-theme";
5
7
  import type { DbColumnInfo } from "./use-database";
6
8
  import { ExportButton } from "./export-button";
7
9
 
@@ -44,15 +46,14 @@ export function DataGrid({
44
46
  const [insertError, setInsertError] = useState<string | null>(null);
45
47
  const [confirmBulkDelete, setConfirmBulkDelete] = useState(false);
46
48
  const { openTab } = useTabStore(useShallow((s) => ({ openTab: s.openTab })));
47
- const openCellViewer = useCallback((cell: { col: string; value: string }) => {
48
- openTab({
49
- type: "editor",
50
- title: cell.col,
51
- projectId: null,
52
- closable: true,
53
- metadata: { inlineContent: cell.value, inlineLanguage: detectLang(cell.value) },
54
- });
55
- }, [openTab]);
49
+ const [previewData, setPreviewData] = useState<{ title: string; content: string; language: string; viewerKey: string } | null>(null);
50
+ const openCellViewer = useCallback((cell: { col: string; value: string; pkVal: string }) => {
51
+ const table = selectedTable ?? "";
52
+ const lang = detectLang(cell.value);
53
+ const key = `${connectionId ?? "local"}:${table}:${cell.col}:${cell.pkVal}`;
54
+ const title = table ? `${cell.col} #${cell.pkVal} — ${table}` : `${cell.col} #${cell.pkVal}`;
55
+ setPreviewData({ title, content: cell.value, language: lang, viewerKey: key });
56
+ }, [connectionId, selectedTable]);
56
57
  const [pinnedCols, setPinnedCols] = useState<Set<string>>(new Set());
57
58
  const [pinnedRows, setPinnedRows] = useState<Set<number>>(new Set());
58
59
  const [filterOpenCol, setFilterOpenCol] = useState<string | null>(null);
@@ -69,14 +70,21 @@ export function DataGrid({
69
70
  const openRowViewer = useCallback((row: Record<string, unknown>) => {
70
71
  const json = JSON.stringify(row, null, 2);
71
72
  const pk = pkCol ? String(row[pkCol] ?? "") : "";
73
+ const table = selectedTable ?? "";
74
+ const key = `${connectionId ?? "local"}:${table}:row:${pk || "unknown"}`;
75
+ const title = pk ? `Row #${pk}${table ? ` — ${table}` : ""}` : `Row${table ? ` — ${table}` : ""}`;
76
+ setPreviewData({ title, content: json, language: "json", viewerKey: key });
77
+ }, [pkCol, connectionId, selectedTable]);
78
+ const openPreviewInTab = useCallback(() => {
79
+ if (!previewData) return;
72
80
  openTab({
73
81
  type: "editor",
74
- title: pk ? `Row ${pk}` : "Row",
82
+ title: previewData.title,
75
83
  projectId: null,
76
84
  closable: true,
77
- metadata: { inlineContent: json, inlineLanguage: "json" },
85
+ metadata: { inlineContent: previewData.content, inlineLanguage: previewData.language, viewerKey: previewData.viewerKey },
78
86
  });
79
- }, [openTab, pkCol]);
87
+ }, [openTab, previewData]);
80
88
 
81
89
  // Refs for cell renderers — avoid column memo rebuild on every state change
82
90
  const editingRef = useRef(editingCell);
@@ -196,7 +204,7 @@ export function DataGrid({
196
204
  if (!el) return;
197
205
  const handler = (e: KeyboardEvent) => {
198
206
  // Escape closes column search from anywhere
199
- if (e.key === "Escape") { setColSearchOpen(false); return; }
207
+ if (e.key === "Escape") { setColSearchOpen(false); setPreviewData(null); return; }
200
208
 
201
209
  // Skip if focus is in an input/textarea
202
210
  const tag = (e.target as HTMLElement)?.tagName;
@@ -433,7 +441,7 @@ export function DataGrid({
433
441
  )}
434
442
 
435
443
  {/* Table */}
436
- <div ref={scrollRef} className="flex-1 overflow-auto relative">
444
+ <div ref={scrollRef} className="flex-1 overflow-auto relative min-h-0">
437
445
  {loading && (
438
446
  <div className="absolute inset-0 z-30 flex items-center justify-center bg-background/60">
439
447
  <Loader2 className="size-5 animate-spin text-primary" />
@@ -540,6 +548,11 @@ export function DataGrid({
540
548
  </table>
541
549
  </div>
542
550
 
551
+ {/* Inline preview panel */}
552
+ {previewData && (
553
+ <DataPreviewPanel data={previewData} onClose={() => setPreviewData(null)} onOpenInTab={openPreviewInTab} />
554
+ )}
555
+
543
556
  {/* Footer: row count + pagination */}
544
557
  <div className="flex items-center justify-between px-3 py-1.5 border-t border-border bg-background shrink-0 text-xs text-muted-foreground">
545
558
  <span>{tableData.total.toLocaleString()} rows</span>
@@ -596,6 +609,93 @@ function detectLang(text: string): string {
596
609
  return "plaintext";
597
610
  }
598
611
 
612
+ /** Inline preview panel for cell/row content with Monaco editor */
613
+ function DataPreviewPanel({ data, onClose, onOpenInTab }: {
614
+ data: { title: string; content: string; language: string };
615
+ onClose: () => void;
616
+ onOpenInTab: () => void;
617
+ }) {
618
+ const monacoTheme = useMonacoTheme();
619
+ const [wordWrap, setWordWrap] = useState(true);
620
+ const [displayContent, setDisplayContent] = useState(data.content);
621
+ const [beautified, setBeautified] = useState(false);
622
+ const canBeautify = data.language === "json" || data.language === "xml";
623
+
624
+ // Reset state when data changes
625
+ const prevKey = useRef(data.title);
626
+ if (prevKey.current !== data.title) {
627
+ prevKey.current = data.title;
628
+ setDisplayContent(data.content);
629
+ setBeautified(false);
630
+ }
631
+
632
+ const toggleBeautify = useCallback(() => {
633
+ if (beautified) {
634
+ setDisplayContent(data.content);
635
+ setBeautified(false);
636
+ } else {
637
+ if (data.language === "json") {
638
+ try { setDisplayContent(JSON.stringify(JSON.parse(data.content.trim()), null, 2)); setBeautified(true); } catch { /* invalid */ }
639
+ } else if (data.language === "xml") {
640
+ // Simple XML indent
641
+ let depth = 0;
642
+ const formatted = data.content.trim().replace(/>\s*</g, ">\n<").split("\n").map((line) => {
643
+ const trimmed = line.trim();
644
+ if (trimmed.startsWith("</")) depth = Math.max(0, depth - 1);
645
+ const indented = " ".repeat(depth) + trimmed;
646
+ if (trimmed.startsWith("<") && !trimmed.startsWith("</") && !trimmed.endsWith("/>") && !trimmed.startsWith("<?")) depth++;
647
+ return indented;
648
+ }).join("\n");
649
+ setDisplayContent(formatted);
650
+ setBeautified(true);
651
+ }
652
+ }
653
+ }, [beautified, data.content, data.language]);
654
+
655
+ return (
656
+ <div className="shrink-0 border-t border-border flex flex-col" style={{ height: "40%" }}>
657
+ <div className="flex items-center gap-1 px-2 py-1 bg-muted/50 border-b border-border shrink-0">
658
+ <Eye className="size-3 text-muted-foreground" />
659
+ <span className="text-xs font-medium text-foreground truncate flex-1">{data.title}</span>
660
+ {canBeautify && (
661
+ <button type="button" onClick={toggleBeautify} title={beautified ? "Raw" : "Beautify"}
662
+ className={`p-0.5 rounded transition-colors ${beautified ? "text-primary" : "text-muted-foreground hover:text-foreground"}`}>
663
+ <Sparkles className="size-3" />
664
+ </button>
665
+ )}
666
+ <button type="button" onClick={() => setWordWrap(!wordWrap)} title={wordWrap ? "No wrap" : "Word wrap"}
667
+ className={`p-0.5 rounded transition-colors ${wordWrap ? "text-primary" : "text-muted-foreground hover:text-foreground"}`}>
668
+ <WrapText className="size-3" />
669
+ </button>
670
+ <button type="button" onClick={onOpenInTab} title="Open in new tab"
671
+ className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors">
672
+ <ExternalLink className="size-3" />
673
+ <span className="hidden sm:inline">Open in Tab</span>
674
+ </button>
675
+ <button type="button" onClick={onClose} title="Close preview (Esc)"
676
+ className="p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors">
677
+ <X className="size-3" />
678
+ </button>
679
+ </div>
680
+ <div className="flex-1 min-h-0">
681
+ <Editor
682
+ height="100%"
683
+ language={data.language === "plaintext" ? undefined : data.language}
684
+ value={displayContent}
685
+ theme={monacoTheme}
686
+ options={{
687
+ readOnly: true, minimap: { enabled: false }, scrollBeyondLastLine: false,
688
+ wordWrap: wordWrap ? "on" : "off", lineNumbers: "on", fontSize: 12,
689
+ folding: true, bracketPairColorization: { enabled: true },
690
+ domReadOnly: true, contextmenu: false, overviewRulerLanes: 0,
691
+ }}
692
+ loading={<Loader2 className="size-4 animate-spin text-muted-foreground" />}
693
+ />
694
+ </div>
695
+ </div>
696
+ );
697
+ }
698
+
599
699
  /** Column search dropdown — owns query/index state internally to avoid re-rendering DataGrid */
600
700
  function ColumnSearchDropdown({ columns, onSelect, onClose }: {
601
701
  columns: string[]; onSelect: (col: string) => void; onClose: () => void;
@@ -651,7 +751,7 @@ const DataRow = memo(function DataRow({ row, rowIdx, columns, selected, onToggle
651
751
  onSetEditValue: (v: string) => void;
652
752
  showDelete: boolean; confirmingDelete: boolean;
653
753
  onDelete: (i: number) => void; onConfirmDelete: (i: number | null) => void;
654
- onViewCell: (cell: { col: string; value: string }) => void;
754
+ onViewCell: (cell: { col: string; value: string; pkVal: string }) => void;
655
755
  onViewRow: (row: Record<string, unknown>) => void;
656
756
  pinned: boolean; onTogglePin: (i: number) => void;
657
757
  pinnedCols: Set<string>; pinnedColOffsets: Map<string, number>;
@@ -702,7 +802,7 @@ const DataRow = memo(function DataRow({ row, rowIdx, columns, selected, onToggle
702
802
  </span>
703
803
  {showEye && (
704
804
  <button type="button" title="View full content"
705
- onClick={() => onViewCell({ col, value: formatCellValue(val) })}
805
+ onClick={() => onViewCell({ col, value: formatCellValue(val), pkVal: pkCol ? String(row[pkCol] ?? rowIdx) : String(rowIdx) })}
706
806
  className="shrink-0 p-0.5 rounded text-muted-foreground/50 hover:text-foreground transition-colors">
707
807
  <Eye className="size-3" />
708
808
  </button>
@@ -299,9 +299,9 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
299
299
  return () => window.removeEventListener("file:changed", handler);
300
300
  }, [filePath, projectName, isExternalFile, inlineContent, isUntitled]);
301
301
 
302
- // Update tab title unsaved indicator
302
+ // Update tab title unsaved indicator (skip for inline content — title set by caller)
303
303
  useEffect(() => {
304
- if (!ownTab) return;
304
+ if (!ownTab || inlineContent != null) return;
305
305
  const baseName = isUntitled
306
306
  ? `Untitled-${metadata?.untitledNumber ?? 1}`
307
307
  : (filePath ? basename(filePath) : "Untitled");
@@ -136,6 +136,7 @@ export function DraggableTab({
136
136
  ) : (
137
137
  <span
138
138
  className="max-w-[120px] truncate"
139
+ title={tab.title}
139
140
  onDoubleClick={(e) => {
140
141
  if (onRename) { e.stopPropagation(); setEditing(true); }
141
142
  }}
@@ -1,7 +1,7 @@
1
1
  import { useRef, useEffect, useState, useCallback, memo } from "react";
2
2
  import { useTerminal } from "@/hooks/use-terminal";
3
3
  import { cn } from "@/lib/utils";
4
- import { Copy, ClipboardPaste } from "lucide-react";
4
+ import { Copy, ClipboardPaste, RotateCcw } from "lucide-react";
5
5
  import "@xterm/xterm/css/xterm.css";
6
6
 
7
7
  interface TerminalTabProps {
@@ -22,7 +22,7 @@ export const TerminalTab = memo(function TerminalTab({ metadata }: TerminalTabPr
22
22
  const sessionId = (metadata?.sessionId as string) ?? "new";
23
23
  const projectName = metadata?.projectName as string | undefined;
24
24
  const containerRef = useRef<HTMLDivElement>(null);
25
- const { connected, reconnecting, sendData, getSelection } = useTerminal({ sessionId, projectName, containerRef });
25
+ const { connected, reconnecting, exited, sendData, getSelection, restart } = useTerminal({ sessionId, projectName, containerRef });
26
26
  const [ctrlMode, setCtrlMode] = useState(false);
27
27
  const [viewportHeight, setViewportHeight] = useState<number | null>(null);
28
28
 
@@ -97,16 +97,27 @@ export const TerminalTab = memo(function TerminalTab({ metadata }: TerminalTabPr
97
97
  <span
98
98
  className={cn(
99
99
  "size-2 rounded-full",
100
- connected ? "bg-success" : reconnecting ? "bg-warning" : "bg-error",
100
+ exited ? "bg-error" : connected ? "bg-success" : reconnecting ? "bg-warning" : "bg-error",
101
101
  )}
102
102
  />
103
103
  <span className="text-text-secondary">
104
- {connected
105
- ? "Connected"
106
- : reconnecting
107
- ? "Reconnecting..."
108
- : "Disconnected"}
104
+ {exited
105
+ ? "Process exited"
106
+ : connected
107
+ ? "Connected"
108
+ : reconnecting
109
+ ? "Reconnecting..."
110
+ : "Disconnected"}
109
111
  </span>
112
+ {exited && (
113
+ <button
114
+ onClick={restart}
115
+ className="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors"
116
+ >
117
+ <RotateCcw size={10} />
118
+ Restart
119
+ </button>
120
+ )}
110
121
  <span className="text-text-subtle ml-auto font-mono">{sessionId}</span>
111
122
  </div>
112
123
 
@@ -66,8 +66,10 @@ interface UseTerminalOptions {
66
66
  interface UseTerminalReturn {
67
67
  connected: boolean;
68
68
  reconnecting: boolean;
69
+ exited: boolean;
69
70
  sendData: (data: string) => void;
70
71
  getSelection: () => string;
72
+ restart: () => void;
71
73
  }
72
74
 
73
75
  const RESIZE_PREFIX = "\x01RESIZE:";
@@ -83,6 +85,7 @@ export function useTerminal(
83
85
  const reconnectAttempts = useRef(0);
84
86
  const [connected, setConnected] = useState(false);
85
87
  const [reconnecting, setReconnecting] = useState(false);
88
+ const [exited, setExited] = useState(false);
86
89
  const actualSessionId = useRef(sessionId); // Track server-assigned session ID
87
90
 
88
91
  const sendData = useCallback((data: string) => {
@@ -104,6 +107,20 @@ export function useTerminal(
104
107
  }
105
108
  }, []);
106
109
 
110
+ const restart = useCallback(() => {
111
+ // Close existing WS, reset to "new" session, reconnect
112
+ if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
113
+ wsRef.current?.close();
114
+ wsRef.current = null;
115
+ actualSessionId.current = "new";
116
+ reconnectAttempts.current = 0;
117
+ setExited(false);
118
+ setConnected(false);
119
+ setReconnecting(false);
120
+ // connectWs will be called after this via setTimeout to allow state to settle
121
+ setTimeout(() => connectWs(), 0);
122
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
123
+
107
124
  const connectWs = useCallback(() => {
108
125
  const term = termRef.current;
109
126
  if (!term) return;
@@ -130,13 +147,16 @@ export function useTerminal(
130
147
  if (event.data.startsWith("{")) {
131
148
  try {
132
149
  const msg = JSON.parse(event.data);
133
- if (msg.type === "session" || msg.type === "error") {
150
+ if (msg.type === "session" || msg.type === "error" || msg.type === "exited") {
134
151
  if (msg.type === "session" && msg.id) {
135
152
  actualSessionId.current = msg.id; // Save for reconnect
136
153
  }
137
154
  if (msg.type === "error") {
138
155
  term.write(`\r\n\x1b[31mError: ${msg.message}\x1b[0m\r\n`);
139
156
  }
157
+ if (msg.type === "exited") {
158
+ setExited(true);
159
+ }
140
160
  return; // Don't write raw JSON to terminal
141
161
  }
142
162
  } catch {
@@ -234,5 +254,5 @@ export function useTerminal(
234
254
  };
235
255
  }, [sessionId]); // eslint-disable-line react-hooks/exhaustive-deps
236
256
 
237
- return { connected, reconnecting, sendData, getSelection };
257
+ return { connected, reconnecting, exited, sendData, getSelection, restart };
238
258
  }
@@ -89,6 +89,7 @@ export function getNextUntitledNumber(panels: Record<string, Panel>): number {
89
89
  export function deriveTabId(type: TabType, metadata?: Record<string, unknown>): string {
90
90
  switch (type) {
91
91
  case "editor":
92
+ if (metadata?.viewerKey) return `editor:viewer:${metadata.viewerKey}`;
92
93
  if (metadata?.isUntitled) return `editor:untitled-${metadata.untitledNumber ?? 1}`;
93
94
  return `editor:${metadata?.filePath ?? "untitled"}`;
94
95
  case "chat": {