@hienlh/ppm 0.13.5 → 0.13.7

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 (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/bun.lock +2062 -0
  5. package/bunfig.toml +2 -0
  6. package/dist/web/assets/{audio-preview-BdRw2cYi.js → audio-preview-BR7DoH0l.js} +1 -1
  7. package/dist/web/assets/{chat-tab-C2NBEXEX.js → chat-tab-8Bvn7PHt.js} +6 -6
  8. package/dist/web/assets/code-editor-B-F5VdzM.js +8 -0
  9. package/dist/web/assets/{conflict-editor-Br_CSQdA.js → conflict-editor-C-lUw4gv.js} +1 -1
  10. package/dist/web/assets/{database-viewer-CbIMjroK.js → database-viewer-mLY7oMC_.js} +2 -2
  11. package/dist/web/assets/diff-viewer-I8qs3Nb-.js +4 -0
  12. package/dist/web/assets/{extension-webview-DxP22X_y.js → extension-webview-Dt9bKs0C.js} +1 -1
  13. package/dist/web/assets/{image-preview-yX0yZtyd.js → image-preview-C-w-CK5s.js} +1 -1
  14. package/dist/web/assets/{index-BJ76xcQz.css → index-CEI0tfaL.css} +1 -1
  15. package/dist/web/assets/index-CFz4k7zO.js +27 -0
  16. package/dist/web/assets/keybindings-store-CWX97luK.js +1 -0
  17. package/dist/web/assets/{markdown-renderer-DHD3HPwK.js → markdown-renderer-ySnJPmc1.js} +1 -1
  18. package/dist/web/assets/{pdf-preview-BlRtar7G.js → pdf-preview-BoQTv6B2.js} +1 -1
  19. package/dist/web/assets/{port-forwarding-tab-DOYZIXHo.js → port-forwarding-tab-CjlX-0D2.js} +1 -1
  20. package/dist/web/assets/{postgres-viewer-DM6b5mZl.js → postgres-viewer-CBntLqXY.js} +2 -2
  21. package/dist/web/assets/{settings-tab-JzeC-QC7.js → settings-tab-Djw-bqxG.js} +1 -1
  22. package/dist/web/assets/{sqlite-viewer-IvosQxK2.js → sqlite-viewer-CjtNrQ7C.js} +1 -1
  23. package/dist/web/assets/{terminal-tab-D4xxia2I.js → terminal-tab-bKLoy06f.js} +1 -1
  24. package/dist/web/assets/{video-preview-ClY8ALGJ.js → video-preview-DxTnfuSQ.js} +1 -1
  25. package/dist/web/index.html +2 -2
  26. package/dist/web/sw.js +1 -1
  27. package/package.json +1 -1
  28. package/src/index.ts +0 -0
  29. package/src/server/routes/chat.ts +25 -16
  30. package/src/web/components/editor/code-editor.tsx +34 -1
  31. package/src/web/components/editor/editor-mobile-toolbar.tsx +144 -0
  32. package/src/web/components/layout/mobile-nav.tsx +39 -4
  33. package/dist/web/assets/code-editor-BhmUC3pD.js +0 -8
  34. package/dist/web/assets/diff-viewer-oq0RiOpV.js +0 -4
  35. package/dist/web/assets/index-DJQJu6Ef.js +0 -27
  36. package/dist/web/assets/keybindings-store-zxSQXdFL.js +0 -1
package/dist/web/sw.js CHANGED
@@ -1 +1 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"68621e26387f540756f76eaa61a6c67f","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/video-preview-ClY8ALGJ.js"},{"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/postgres-viewer-DM6b5mZl.js"},{"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/sql-query-editor-vpD0I0KG.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-DpzHf4xp.js"},{"revision":null,"url":"assets/extension-webview-DxP22X_y.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/image-preview-yX0yZtyd.js"},{"revision":null,"url":"assets/scroll-area-D0EQpAH2.js"},{"revision":null,"url":"assets/vendor-xterm-D7SePDJp.js"},{"revision":null,"url":"assets/file-exclamation-point-Baz81y5z.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/database-viewer-CbIMjroK.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/port-forwarding-tab-DOYZIXHo.js"},{"revision":null,"url":"assets/code-editor-BhmUC3pD.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/input-bGJExpJZ.js"},{"revision":null,"url":"assets/sqlite-viewer-IvosQxK2.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/keybindings-store-zxSQXdFL.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/diff-viewer-oq0RiOpV.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/terminal-tab-D4xxia2I.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-D6dgXbAe.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/chat-tab-C2NBEXEX.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/audio-preview-BdRw2cYi.js"},{"revision":null,"url":"assets/api-client-r4nyVy7H.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.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/settings-tab-JzeC-QC7.js"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/pdf-preview-BlRtar7G.js"},{"revision":null,"url":"assets/conflict-editor-Br_CSQdA.js"},{"revision":null,"url":"assets/katex-bpagxk3Z.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/vendor-ui-B-89Uj8i.js"},{"revision":null,"url":"assets/index-BJ76xcQz.css"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/text-wrap-DzvCTq_i.js"},{"revision":null,"url":"assets/csv-parser-DxVplKKB.js"},{"revision":null,"url":"assets/markdown-renderer-DHD3HPwK.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/use-monaco-theme-dtPsv6sh.js"},{"revision":null,"url":"assets/index-DJQJu6Ef.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":"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||`/`)}))});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.13.5",
3
+ "version": "0.13.7",
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
@@ -307,22 +307,31 @@ chatRoutes.post("/sessions/:id/fork", async (c) => {
307
307
  if (!provider.forkAtMessage) {
308
308
  return c.json(err("Provider does not support forking"), 400);
309
309
  }
310
- const result = await provider.forkAtMessage(sourceId, body.messageId, {
311
- title: "Forked Chat", dir: projectPath,
312
- });
313
- // Register forked session with provider + DB so it's tracked in memory
314
- setSessionMetadata(result.sessionId, projectName, projectPath);
315
- await provider.resumeSession(result.sessionId);
316
- provider.markAsResumed?.(result.sessionId);
317
- const forkedSession = {
318
- id: result.sessionId,
319
- providerId,
320
- title: "Forked Chat",
321
- projectName,
322
- projectPath,
323
- createdAt: new Date().toISOString(),
324
- };
325
- return c.json(ok({ ...forkedSession, forkedFrom: sourceId }), 201);
310
+ try {
311
+ const result = await provider.forkAtMessage(sourceId, body.messageId, {
312
+ title: "Forked Chat", dir: projectPath,
313
+ });
314
+ // Register forked session with provider + DB so it's tracked in memory
315
+ setSessionMetadata(result.sessionId, projectName, projectPath);
316
+ await provider.resumeSession(result.sessionId);
317
+ provider.markAsResumed?.(result.sessionId);
318
+ const forkedSession = {
319
+ id: result.sessionId,
320
+ providerId,
321
+ title: "Forked Chat",
322
+ projectName,
323
+ projectPath,
324
+ createdAt: new Date().toISOString(),
325
+ };
326
+ return c.json(ok({ ...forkedSession, forkedFrom: sourceId }), 201);
327
+ } catch (forkErr) {
328
+ // Message UUID may no longer exist after SDK compaction — fall back to fresh session
329
+ console.warn(`[chat] forkAtMessage failed (message may be compacted): ${(forkErr as Error).message}`);
330
+ const session = await chatService.createSession(providerId, {
331
+ projectName, projectPath, title: "Forked Chat",
332
+ });
333
+ return c.json(ok({ ...session, forkedFrom: sourceId }), 201);
334
+ }
326
335
  } else {
327
336
  // No messageId (fork at first message) — create a fresh empty session
328
337
  const session = await chatService.createSession(providerId, {
@@ -12,6 +12,7 @@ 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";
15
+ import { EditorMobileToolbar } from "./editor-mobile-toolbar";
15
16
  import { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from "../database/sql-completion-provider";
16
17
  import { useConnections, type Connection } from "../database/use-connections";
17
18
 
@@ -192,6 +193,31 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
192
193
  runSqlInViewer(sqlText);
193
194
  }, [selectedSqlConn, runSqlInViewer]);
194
195
 
196
+ // Touch device detection for mobile toolbar
197
+ const isMobile = typeof window !== "undefined" && "ontouchstart" in window;
198
+
199
+ // Track visual viewport so toolbar stays above mobile keyboard
200
+ const containerRef = useRef<HTMLDivElement | null>(null);
201
+ const [mobileHeight, setMobileHeight] = useState<number | null>(null);
202
+ useEffect(() => {
203
+ if (!isMobile) return;
204
+ const vv = window.visualViewport;
205
+ if (!vv) return;
206
+ const handle = () => {
207
+ const el = containerRef.current;
208
+ if (!el) return;
209
+ // Calculate available height = viewport height - element's top offset from viewport
210
+ const top = el.getBoundingClientRect().top;
211
+ setMobileHeight(vv.height - Math.max(0, top));
212
+ };
213
+ vv.addEventListener("resize", handle);
214
+ vv.addEventListener("scroll", handle);
215
+ return () => {
216
+ vv.removeEventListener("resize", handle);
217
+ vv.removeEventListener("scroll", handle);
218
+ };
219
+ }, [isMobile]);
220
+
195
221
  // CodeLens: inline Run buttons between SQL statements
196
222
  const codeLensDisposable = useRef<MonacoType.IDisposable[]>([]);
197
223
  const runSqlRef = useRef(runSqlInViewer);
@@ -502,7 +528,11 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
502
528
  ) : null;
503
529
 
504
530
  return (
505
- <div className="flex flex-col h-full w-full overflow-hidden">
531
+ <div
532
+ ref={containerRef}
533
+ className="flex flex-col h-full w-full overflow-hidden"
534
+ style={mobileHeight ? { height: `${mobileHeight}px`, maxHeight: `${mobileHeight}px` } : undefined}
535
+ >
506
536
  {/* Inline content toolbar (cell viewer mode) */}
507
537
  {inlineContent != null && canBeautifyInline && (
508
538
  <div className="flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2">
@@ -578,6 +608,9 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
578
608
  </div>
579
609
  )}
580
610
 
611
+ {/* Mobile toolbar — bottom, like terminal */}
612
+ {isMobile && <EditorMobileToolbar editorRef={editorRef} readOnly={inlineContent != null} />}
613
+
581
614
  {/* Save As dialog for untitled tabs */}
582
615
  {showSaveAs && (
583
616
  <SaveAsDialog
@@ -0,0 +1,144 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ import { ClipboardPaste, Undo2, Redo2, X } from "lucide-react";
3
+ import type * as MonacoType from "monaco-editor";
4
+
5
+ /** Clipboard API requires secure context (HTTPS / localhost) */
6
+ const isSecureContext = typeof window !== "undefined" && window.isSecureContext;
7
+
8
+ /** Symbols commonly needed when coding on mobile — ordered by frequency */
9
+ const SYMBOL_KEYS = [
10
+ "(", ")", "{", "}", "[", "]",
11
+ "<", ">", ";", ":", "=",
12
+ '"', "'", "`", "/", "\\", "_", "#",
13
+ ];
14
+
15
+ const btnBase =
16
+ "px-2 py-1.5 rounded text-xs min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none";
17
+ const btnSymbol =
18
+ "px-3 py-1.5 rounded text-xs font-mono min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none";
19
+ const divider = "w-px h-5 bg-border mx-0.5 shrink-0";
20
+
21
+ interface EditorMobileToolbarProps {
22
+ editorRef: React.RefObject<MonacoType.editor.IStandaloneCodeEditor | null>;
23
+ readOnly?: boolean;
24
+ }
25
+
26
+ export function EditorMobileToolbar({ editorRef, readOnly }: EditorMobileToolbarProps) {
27
+ const getEditor = useCallback(() => editorRef.current, [editorRef]);
28
+
29
+ /** Insert text at cursor position in Monaco */
30
+ const insertText = useCallback((text: string) => {
31
+ const editor = getEditor();
32
+ if (!editor) return;
33
+ editor.focus();
34
+ const selection = editor.getSelection();
35
+ if (selection) {
36
+ editor.executeEdits("mobile-toolbar", [{ range: selection, text }]);
37
+ }
38
+ }, [getEditor]);
39
+
40
+ // --- Paste: two strategies based on secure context ---
41
+ const [pasteMode, setPasteMode] = useState(false);
42
+ const pasteRef = useRef<HTMLTextAreaElement | null>(null);
43
+
44
+ // HTTPS: use Clipboard API directly (single tap)
45
+ const handleClipboardPaste = useCallback(async () => {
46
+ try {
47
+ const text = await navigator.clipboard.readText();
48
+ if (text) insertText(text);
49
+ } catch { /* permission denied */ }
50
+ }, [insertText]);
51
+
52
+ // HTTP fallback: show textarea for native long-press paste
53
+ const openPasteMode = useCallback(() => {
54
+ setPasteMode(true);
55
+ requestAnimationFrame(() => pasteRef.current?.focus());
56
+ }, []);
57
+
58
+ const handleNativePaste = useCallback((e: React.ClipboardEvent<HTMLTextAreaElement>) => {
59
+ e.preventDefault();
60
+ const text = e.clipboardData.getData("text/plain");
61
+ if (!text) return;
62
+ setPasteMode(false);
63
+ insertText(text);
64
+ }, [insertText]);
65
+
66
+ const handleUndo = useCallback(() => {
67
+ const editor = getEditor();
68
+ if (!editor) return;
69
+ editor.focus();
70
+ editor.trigger("mobile-toolbar", "undo", null);
71
+ }, [getEditor]);
72
+
73
+ const handleRedo = useCallback(() => {
74
+ const editor = getEditor();
75
+ if (!editor) return;
76
+ editor.focus();
77
+ editor.trigger("mobile-toolbar", "redo", null);
78
+ }, [getEditor]);
79
+
80
+ const handleTab = useCallback(() => {
81
+ const editor = getEditor();
82
+ if (!editor) return;
83
+ editor.focus();
84
+ editor.trigger("mobile-toolbar", "tab", null);
85
+ }, [getEditor]);
86
+
87
+ if (readOnly) return null;
88
+
89
+ return (
90
+ <div className="shrink-0 border-t border-border bg-surface">
91
+ {/* HTTP-only: textarea for native paste via long-press */}
92
+ {!isSecureContext && pasteMode && (
93
+ <div className="flex items-center gap-2 px-2 py-1.5 border-b border-border bg-muted/50">
94
+ <textarea
95
+ ref={pasteRef}
96
+ onPaste={handleNativePaste}
97
+ placeholder="Long-press here → Paste"
98
+ className="flex-1 h-8 rounded border border-border bg-background text-foreground text-xs px-2 py-1.5 resize-none focus:outline-none focus:ring-1 focus:ring-primary"
99
+ />
100
+ <button
101
+ type="button"
102
+ onClick={() => setPasteMode(false)}
103
+ className="p-1.5 rounded text-muted-foreground active:bg-muted transition-colors"
104
+ >
105
+ <X size={14} />
106
+ </button>
107
+ </div>
108
+ )}
109
+
110
+ {/* Toolbar buttons */}
111
+ <div className="flex items-center gap-1 px-2 py-1.5 overflow-x-auto">
112
+ {/* Paste: Clipboard API on HTTPS, textarea fallback on HTTP */}
113
+ <button
114
+ type="button"
115
+ onClick={isSecureContext ? handleClipboardPaste : openPasteMode}
116
+ className={btnBase}
117
+ title="Paste"
118
+ >
119
+ <ClipboardPaste size={14} />
120
+ </button>
121
+ <button type="button" onClick={handleUndo} className={btnBase} title="Undo">
122
+ <Undo2 size={14} />
123
+ </button>
124
+ <button type="button" onClick={handleRedo} className={btnBase} title="Redo">
125
+ <Redo2 size={14} />
126
+ </button>
127
+
128
+ <div className={divider} />
129
+
130
+ <button type="button" onClick={handleTab} className={btnSymbol}>
131
+ Tab
132
+ </button>
133
+
134
+ <div className={divider} />
135
+
136
+ {SYMBOL_KEYS.map((key) => (
137
+ <button key={key} type="button" onClick={() => insertText(key)} className={btnSymbol}>
138
+ {key}
139
+ </button>
140
+ ))}
141
+ </div>
142
+ </div>
143
+ );
144
+ }
@@ -15,9 +15,11 @@ import type { Tab, TabType } from "@/stores/tab-store";
15
15
  import { cn } from "@/lib/utils";
16
16
  import { openCommandPalette } from "@/hooks/use-global-keybindings";
17
17
  import { useNotificationStore, notificationColor } from "@/stores/notification-store";
18
+ import { useStreamingStore } from "@/stores/streaming-store";
18
19
  import { useTabOverflow, getHiddenUnreadDirection } from "@/hooks/use-tab-overflow";
19
20
  import { downloadFile } from "@/lib/file-download";
20
21
  import { FileActions } from "@/components/explorer/file-actions";
22
+ import { api, projectUrl } from "@/lib/api-client";
21
23
 
22
24
  const NEW_TAB_OPTIONS: { type: TabType; label: string }[] = [
23
25
  { type: "terminal", label: "Terminal" },
@@ -63,6 +65,9 @@ export function MobileNav({ onMenuPress, onProjectsPress }: MobileNavProps) {
63
65
  const mobileScrollRef = useRef<HTMLDivElement>(null);
64
66
  const prevTabCount = useRef(tabs.length);
65
67
  const notifications = useNotificationStore((s) => s.notifications);
68
+ const streamingSessions = useStreamingStore((s) => s.sessions);
69
+ const [sessionTagMap, setSessionTagMap] = useState<Record<string, { id: number; name: string; color: string }>>({});
70
+
66
71
  const { canScrollLeft, canScrollRight, scrollRight: doScrollRight } =
67
72
  useTabOverflow(mobileScrollRef);
68
73
  const hiddenUnread = getHiddenUnreadDirection(mobileScrollRef.current, tabRefs.current as Map<string, HTMLElement>, tabs, notifications);
@@ -154,6 +159,19 @@ export function MobileNav({ onMenuPress, onProjectsPress }: MobileNavProps) {
154
159
 
155
160
  // Active project avatar for the Projects button
156
161
  const { activeProject, projects, customOrder } = useProjectStore(useShallow((s) => ({ activeProject: s.activeProject, projects: s.projects, customOrder: s.customOrder })));
162
+
163
+ // Session tag map — same fetch pattern as desktop tab-bar so mobile tabs can show tag bar
164
+ const chatSessionIds = tabs.filter((t) => t.type === "chat" && t.metadata?.sessionId).map((t) => t.metadata!.sessionId as string);
165
+ useEffect(() => {
166
+ if (!activeProject?.name || chatSessionIds.length === 0) return;
167
+ api.get<{ sessions: { id: string; tag?: { id: number; name: string; color: string } | null }[] }>(
168
+ `${projectUrl(activeProject.name)}/chat/sessions?limit=50`,
169
+ ).then((data) => {
170
+ const map: Record<string, { id: number; name: string; color: string }> = {};
171
+ for (const s of data.sessions) { if (s.tag) map[s.id] = s.tag; }
172
+ setSessionTagMap(map);
173
+ }).catch(() => {});
174
+ }, [activeProject?.name, chatSessionIds.join(",")]); // eslint-disable-line react-hooks/exhaustive-deps
157
175
  const ordered = resolveOrder(projects, customOrder ?? null);
158
176
  const allNames = ordered.map((p) => p.name);
159
177
  const activeIdx = ordered.findIndex((p) => p.name === activeProject?.name);
@@ -218,6 +236,8 @@ export function MobileNav({ onMenuPress, onProjectsPress }: MobileNavProps) {
218
236
  const sessionId = tab.type === "chat" ? (tab.metadata?.sessionId as string) : undefined;
219
237
  const entry = sessionId ? notifications.get(sessionId) : undefined;
220
238
  const notiType = entry && entry.count > 0 ? entry.type : null;
239
+ const tagColor = sessionId ? sessionTagMap[sessionId]?.color : undefined;
240
+ const isStreaming = sessionId ? streamingSessions.has(sessionId) : false;
221
241
  return (
222
242
  <button
223
243
  key={tab.id}
@@ -231,15 +251,30 @@ export function MobileNav({ onMenuPress, onProjectsPress }: MobileNavProps) {
231
251
  onTouchMove={cancelLongPress}
232
252
  onContextMenu={(e) => e.preventDefault()}
233
253
  className={cn(
234
- "flex items-center gap-1 px-3 h-12 whitespace-nowrap text-xs shrink-0 border-t-2 transition-colors",
254
+ "relative flex items-center gap-1 px-3 h-12 whitespace-nowrap text-xs shrink-0 border-t-2 transition-colors",
235
255
  isActive ? "border-primary bg-surface text-primary" : "border-transparent text-text-secondary",
236
256
  )}
237
257
  >
238
- <span className="relative">
258
+ {tagColor && (
259
+ // Tag identity marker — VS Code-style vertical bar on left edge, matches desktop tab
260
+ <span
261
+ aria-hidden
262
+ className="absolute left-0 top-2 bottom-2 w-[3px] rounded-r-full pointer-events-none"
263
+ style={{ backgroundColor: tagColor }}
264
+ />
265
+ )}
266
+ <span className={cn("relative", isStreaming && "text-amber-500")}>
239
267
  <Icon className="size-4" />
240
- {notiType && !isActive && (
268
+ {isStreaming ? (
269
+ // Messenger-style typing dots inside chat bubble — inherits amber via bg-current
270
+ <span aria-hidden className="absolute inset-0 flex items-center justify-center gap-[1.5px]">
271
+ <span className="tab-typing-dot size-[2px] rounded-full bg-current" />
272
+ <span className="tab-typing-dot size-[2px] rounded-full bg-current" style={{ animationDelay: "0.15s" }} />
273
+ <span className="tab-typing-dot size-[2px] rounded-full bg-current" style={{ animationDelay: "0.3s" }} />
274
+ </span>
275
+ ) : notiType && !isActive ? (
241
276
  <span className={cn("absolute -top-1 -right-1 size-2 rounded-full", notificationColor(notiType))} />
242
- )}
277
+ ) : null}
243
278
  </span>
244
279
  <span className="max-w-[80px] truncate">{tab.title}</span>
245
280
  {tab.closable && (
@@ -1,8 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/markdown-renderer-DHD3HPwK.js","assets/rolldown-runtime-FhOqtrmT.js","assets/index-DJQJu6Ef.js","assets/vendor-mermaid-DCxaaPi4.js","assets/vendor-ui-B-89Uj8i.js","assets/vendor-markdown-0Mxgxy0L.js","assets/input-bGJExpJZ.js","assets/utils-ChWX7pZv.js","assets/createLucideIcon-BjHrJDVb.js","assets/x-BtqbfkR7.js","assets/settings-store-CdcSAgEZ.js","assets/react-BkWDCPD7.js","assets/api-client-r4nyVy7H.js","assets/scroll-area-D0EQpAH2.js","assets/ai-settings-section-DeW4WN43.js","assets/dist-D7KGU7Vl.js","assets/plus-51UQ45rf.js","assets/refresh-cw-CSFrDtiu.js","assets/trash-2-BgDIBl6f.js","assets/api-settings-t7Leca7J.js","assets/chevron-right-BzAdxJRG.js","assets/database-DCT0OjgQ.js","assets/file-store-BgZggznw.js","assets/tab-store-Jvy1eZGM.js","assets/index-BJ76xcQz.css","assets/csv-preview-C9qGhDlb.js","assets/lib-BqkcKGFq.js","assets/arrow-up-Dtrfv490.js","assets/csv-parser-DxVplKKB.js","assets/image-preview-yX0yZtyd.js","assets/file-exclamation-point-Baz81y5z.js","assets/use-blob-url-BgxxT-n_.js","assets/pdf-preview-BlRtar7G.js","assets/video-preview-ClY8ALGJ.js","assets/audio-preview-BdRw2cYi.js"])))=>i.map(i=>d[i]);
2
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-B-89Uj8i.js";import"./scroll-area-D0EQpAH2.js";import{t as r}from"./chevron-right-BzAdxJRG.js";import{a as i,l as a,n as o,o as s,r as c,s as l,t as u}from"./input-bGJExpJZ.js";import{t as d}from"./code-CuravVys.js";import{t as f}from"./database-DCT0OjgQ.js";import{n as p,r as m}from"./x-BtqbfkR7.js";import{t as ee}from"./file-exclamation-point-Baz81y5z.js";import{t as h}from"./table-DbSviOmw.js";import{t as g}from"./text-wrap-DzvCTq_i.js";import{i as _,t as v}from"./api-client-r4nyVy7H.js";import{n as te}from"./settings-store-CdcSAgEZ.js";import{G as y}from"./vendor-mermaid-DCxaaPi4.js";import{t as b}from"./utils-ChWX7pZv.js";import{n as ne,t as re}from"./tab-store-Jvy1eZGM.js";import{r as x,t as S}from"./file-store-BgZggznw.js";import{G as ie,J as C,K as w,L as ae,U as T,X as oe,Y as E,a as se,c as D,d as O,f as k,g as A,h as ce,l as j,m as M,o as N,p as P,q as F,u as le}from"./index-DJQJu6Ef.js";import{n as ue,t as de}from"./use-monaco-theme-dtPsv6sh.js";import{n as fe,t as pe}from"./sql-completion-provider-EzHOQLfo.js";var I=e(n(),1),L=t(),R={ts:E,tsx:E,js:E,jsx:E,py:E,rs:E,go:E,html:E,css:E,scss:E,json:oe,md:C,txt:C,yaml:F,yml:F};function me(e,t){return t?ie:R[e.split(`.`).pop()?.toLowerCase()??``]??w}function he(e,t){let n=[],r=e;for(let e=0;e<t.length;e++){let i=t[e],a=t.slice(0,e+1).join(`/`),o=r.find(e=>e.name===i);if(n.push({name:i,fullPath:a,node:o??null,siblings:r}),o?.children)r=o.children;else{for(let r=e+1;r<t.length;r++)n.push({name:t[r],fullPath:t.slice(0,r+1).join(`/`),node:null,siblings:[]});break}}return n}function z(e){return[...e].sort((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1)}function ge({filePath:e,projectName:t,tabId:n,className:i}){let a=S(e=>e.tree),{updateTab:o,openTab:s}=re(ce(e=>({updateTab:e.updateTab,openTab:e.openTab}))),c=(0,I.useRef)(null),l=(0,I.useMemo)(()=>he(a,e.split(`/`).filter(Boolean)),[a,e]);(0,I.useEffect)(()=>{c.current&&(c.current.scrollLeft=c.current.scrollWidth)},[l]);function u(e,r){let i=b(e);r.metaKey||r.ctrlKey?s({type:`editor`,title:i,metadata:{filePath:e,projectName:t},projectId:t,closable:!0}):o(n,{title:i,metadata:{filePath:e,projectName:t}})}return(0,L.jsx)(`div`,{ref:c,className:i,children:l.map((e,n)=>(0,L.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[n>0&&(0,L.jsx)(r,{className:`size-3 text-muted-foreground shrink-0 mx-0.5`}),e.siblings.length>0?(0,L.jsx)(B,{segment:e,isLast:n===l.length-1,projectName:t,onFileClick:u}):(0,L.jsx)(`span`,{className:`text-xs text-muted-foreground px-1 py-0.5`,children:e.name})]},e.fullPath))})}function B({segment:e,isLast:t,projectName:n,onFileClick:r}){let i=(0,I.useMemo)(()=>z(e.siblings),[e.siblings]);return(0,L.jsxs)(D,{children:[(0,L.jsx)(M,{asChild:!0,children:(0,L.jsx)(`button`,{type:`button`,className:`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${t?`text-foreground font-medium`:`text-muted-foreground`}`,children:e.name})}),(0,L.jsx)(j,{align:`start`,className:`max-h-[300px] p-1`,children:i.map(t=>(0,L.jsx)(V,{node:t,projectName:n,activePath:e.fullPath,onFileClick:r},t.path))})]})}function V({node:e,projectName:t,activePath:n,onFileClick:r}){let i=me(e.name,e.type===`directory`),a=e.path===n;return e.type===`directory`&&e.children&&e.children.length>0?(0,L.jsxs)(O,{children:[(0,L.jsxs)(P,{className:`text-xs gap-1.5 ${a?`bg-muted`:``}`,children:[(0,L.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,L.jsx)(`span`,{className:`truncate`,children:e.name})]}),(0,L.jsx)(k,{className:`max-h-[300px] overflow-y-auto p-1`,children:z(e.children).map(e=>(0,L.jsx)(V,{node:e,projectName:t,activePath:n,onFileClick:r},e.path))})]}):(0,L.jsxs)(le,{className:`text-xs gap-1.5 cursor-pointer ${a?`bg-muted`:``}`,onSelect:e=>{},onClick:t=>{e.type!==`directory`&&r(e.path,t)},children:[(0,L.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,L.jsx)(`span`,{className:`truncate`,children:e.name})]})}function H({active:e,onClick:t,icon:n,label:r}){return(0,L.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:[(0,L.jsx)(n,{className:`size-3`}),(0,L.jsx)(`span`,{className:`hidden sm:inline`,children:r})]})}function _e({ext:e,mdMode:t,onMdModeChange:n,csvMode:r,onCsvModeChange:i,wordWrap:a,onToggleWordWrap:o,filePath:s,projectName:c,className:l}){return(0,L.jsxs)(`div`,{className:l,children:[(e===`md`||e===`mdx`)&&n&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(H,{active:t===`edit`,onClick:()=>n(`edit`),icon:d,label:`Edit`}),(0,L.jsx)(H,{active:t===`preview`,onClick:()=>n(`preview`),icon:p,label:`Preview`})]}),e===`csv`&&i&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(H,{active:r===`table`,onClick:()=>i(`table`),icon:h,label:`Table`}),(0,L.jsx)(H,{active:r===`raw`,onClick:()=>i(`raw`),icon:d,label:`Raw`})]}),(0,L.jsx)(H,{active:a,onClick:o,icon:g,label:`Wrap`}),s&&c&&(0,L.jsx)(H,{active:!1,onClick:()=>A(c,s),icon:m,label:`Download`})]})}function ve({open:e,defaultName:t,content:n,onSave:r,onCancel:d}){let[f,p]=(0,I.useState)(t),[m,ee]=(0,I.useState)(!1),[h,g]=(0,I.useState)(``),_=x(e=>e.activeProject),v=(0,I.useCallback)(()=>{let e=f.trim();if(!e){g(`Filename cannot be empty`);return}if(/[/\\]/.test(e)){g(`Filename cannot contain / or \\`);return}g(``),ee(!0)},[f]),te=(0,I.useCallback)(e=>{let t=e.includes(`\\`)?`\\`:`/`;r(e.endsWith(t)?`${e}${f.trim()}`:`${e}${t}${f.trim()}`,n)},[f,n,r]);return m?(0,L.jsx)(N,{open:!0,mode:`folder`,root:_?.path,title:`Save "${f.trim()}" to...`,onSelect:te,onCancel:()=>ee(!1)}):(0,L.jsx)(o,{open:e,onOpenChange:e=>{e||d()},children:(0,L.jsxs)(c,{className:`sm:max-w-md`,children:[(0,L.jsx)(s,{children:(0,L.jsx)(l,{children:`Save As`})}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 py-2`,children:[(0,L.jsx)(`label`,{className:`text-sm text-muted-foreground`,children:`Filename`}),(0,L.jsx)(u,{value:f,onChange:e=>{p(e.target.value),g(``)},onKeyDown:e=>{e.key===`Enter`&&v()},placeholder:`e.g. my-file.ts`,autoFocus:!0}),h&&(0,L.jsx)(`p`,{className:`text-xs text-destructive`,children:h})]}),(0,L.jsxs)(i,{children:[(0,L.jsx)(a,{variant:`outline`,onClick:d,children:`Cancel`}),(0,L.jsx)(a,{onClick:v,children:`Choose Folder...`})]})]})})}var ye=(0,I.lazy)(()=>y(()=>import(`./markdown-renderer-DHD3HPwK.js`).then(e=>({default:e.MarkdownRenderer})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]))),be=(0,I.lazy)(()=>y(()=>import(`./csv-preview-C9qGhDlb.js`).then(e=>({default:e.CsvPreview})),__vite__mapDeps([25,1,4,5,26,8,27,28]))),xe=(0,I.lazy)(()=>y(()=>import(`./image-preview-yX0yZtyd.js`).then(e=>({default:e.ImagePreview})),__vite__mapDeps([29,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),Se=(0,I.lazy)(()=>y(()=>import(`./pdf-preview-BlRtar7G.js`).then(e=>({default:e.PdfPreview})),__vite__mapDeps([32,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),Ce=(0,I.lazy)(()=>y(()=>import(`./video-preview-ClY8ALGJ.js`).then(e=>({default:e.VideoPreview})),__vite__mapDeps([33,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),we=(0,I.lazy)(()=>y(()=>import(`./audio-preview-BdRw2cYi.js`).then(e=>({default:e.AudioPreview})),__vite__mapDeps([34,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),Te=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`svg`,`ico`]),Ee=new Set([`mp4`,`webm`,`mov`,`ogg`,`avi`,`mkv`]),De=new Set([`mp3`,`wav`,`flac`,`aac`,`m4a`,`wma`]),Oe=new Set([`db`,`sqlite`,`sqlite3`]);function ke(e){return e.split(`.`).pop()?.toLowerCase()??``}function Ae(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`,sql:`sql`}[ke(e)]??`plaintext`}var U=(0,I.memo)(function({metadata:e,tabId:t}){let n=e?.filePath,r=e?.projectName,i=e?.inlineContent,a=e?.inlineLanguage,[o,s]=(0,I.useState)(i??null),[c,l]=(0,I.useState)(`utf-8`),[u,d]=(0,I.useState)(!0),[p,m]=(0,I.useState)(null),[h,g]=(0,I.useState)(!1),y=(0,I.useRef)(null),x=(0,I.useRef)(``),S=(0,I.useRef)(null),{tabs:ie,updateTab:C}=re(ce(e=>({tabs:e.tabs,updateTab:e.updateTab}))),{wordWrap:w,toggleWordWrap:oe}=te(ce(e=>({wordWrap:e.wordWrap,toggleWordWrap:e.toggleWordWrap}))),E=de(),D=e?.isUntitled===!0,O=e?.unsavedContent,[k,A]=(0,I.useState)(!1),j=ie.find(e=>e.id===t),M=n?ke(n):``,N=Te.has(M),P=M===`pdf`,F=Ee.has(M),le=De.has(M),R=Oe.has(M),me=M===`md`||M===`mdx`,he=M===`csv`,z=M===`sql`,[B,V]=(0,I.useState)(`preview`),[H,ye]=(0,I.useState)(`table`),{connections:U,cachedTables:Me,refreshTables:Ne}=se(),[G,Pe]=(0,I.useState)(()=>{if(!z||!n)return null;let e=localStorage.getItem(`ppm:sql-conn:${n}`);return e?Number(e):null}),K=(0,I.useRef)(null),q=(0,I.useRef)(null),J=(0,I.useMemo)(()=>U.find(e=>e.id===G)??null,[U,G]),Fe=i!=null&&(a===`json`||a===`xml`),[Ie,Le]=(0,I.useState)(!1),Re=(0,I.useCallback)(()=>{if(i)if(Ie)s(i),Le(!1);else{let e=i.trimStart();if(a===`json`)try{s(JSON.stringify(JSON.parse(e),null,2)),Le(!0)}catch{}else if(a===`xml`){let t=0;s(e.replace(/(>)(<)(\/*)/g,`$1
3
- $2$3`).split(`
4
- `).map(e=>{let n=e.trim();n.startsWith(`</`)&&(t=Math.max(0,t-1));let r=` `.repeat(t)+n;return n.startsWith(`<`)&&!n.startsWith(`</`)&&!n.endsWith(`/>`)&&!n.includes(`</`)&&t++,r}).join(`
5
- `)),Le(!0)}}},[i,a,Ie]),ze=(0,I.useCallback)(e=>{Pe(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),Ne(e).catch(()=>{})},[n,Ne]),Y=(0,I.useMemo)(()=>{if(!z||!G)return;let e=(Me.get(G)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>v.get(`/api/db/connections/${G}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[z,G,Me]);(0,I.useEffect)(()=>{if(!(!K.current||!Y))return q.current?.dispose(),pe(),q.current=K.current.languages.registerCompletionItemProvider(`sql`,fe(K.current,Y)),()=>{q.current?.dispose()}},[Y]);let Be=re(e=>e.openTab),X=(0,I.useCallback)(e=>{J&&Be({type:`database`,title:`${J.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:J.id,connectionName:J.name,dbType:J.type,initialSql:e}})},[J,Be]),Ve=(0,I.useCallback)(()=>{if(!S.current||!J)return;let e=S.current,t=e.getSelection();X(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[J,X]),Z=(0,I.useRef)([]),He=(0,I.useRef)(X);He.current=X,(0,I.useEffect)(()=>()=>{Z.current.forEach(e=>e.dispose()),Z.current=[]},[]),(0,I.useEffect)(()=>{R&&t&&C(t,{type:`sqlite`})},[R,t,C]);let Q=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,I.useEffect)(()=>{if(i!=null){d(!1);return}if(D){s(O??``),x.current=O??``,d(!1),O&&g(!0);return}if(!n||!Q&&!r)return;if(N||P||F||le){d(!1);return}d(!0),m(null);let e=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${_(r)}/files/read?path=${encodeURIComponent(n)}`;return v.get(e).then(e=>{s(e.content),e.encoding&&l(e.encoding),x.current=e.content,d(!1)}).catch(e=>{m(e instanceof Error?e.message:`Failed to load file`),d(!1)}),()=>{y.current&&clearTimeout(y.current)}},[n,r,N,P,Q,D]);let Ue=(0,I.useRef)(h);Ue.current=h,(0,I.useEffect)(()=>{if(!n||!r||i!=null||D)return;let e=e=>{let t=e.detail;if(t.projectName!==r||t.path!==n||Ue.current)return;let i=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${_(r)}/files/read?path=${encodeURIComponent(n)}`;v.get(i).then(e=>{e.content!==x.current&&(s(e.content),x.current=e.content,e.encoding&&l(e.encoding))}).catch(()=>{})};return window.addEventListener(`file:changed`,e),()=>window.removeEventListener(`file:changed`,e)},[n,r,Q,i,D]),(0,I.useEffect)(()=>{if(!j)return;let t=D?`Untitled-${e?.untitledNumber??1}`:n?b(n):`Untitled`,r=h?`${t} \u25CF`:t;j.title!==r&&C(j.id,{title:r})},[h]);let We=(0,I.useCallback)(async e=>{if(n&&!(!Q&&!r))try{Q?await v.put(`/api/fs/write`,{path:n,content:e}):await v.put(`${_(r)}/files/write`,{path:n,content:e}),g(!1)}catch{}},[n,r,Q]);function Ge(n){let r=n??``;s(r),x.current=r,g(!0),y.current&&clearTimeout(y.current),D?y.current=setTimeout(()=>{t&&C(t,{metadata:{...e,unsavedContent:x.current}})},2e3):y.current=setTimeout(()=>We(x.current),1e3)}let Ke=(0,I.useCallback)(async(e,n)=>{try{if(y.current&&clearTimeout(y.current),await v.put(`/api/fs/write`,{path:e,content:n}),t){let{closeTab:n,openTab:r}=ne.getState();n(t),r({type:`editor`,title:b(e),projectId:null,metadata:{filePath:e},closable:!0})}g(!1),A(!1)}catch{}},[t]),$=e?.lineNumber,qe=(0,I.useCallback)((e,t)=>{if(S.current=e,K.current=t,$&&$>0&&setTimeout(()=>{e.revealLineInCenter($),e.setPosition({lineNumber:$,column:1}),e.focus()},100),D&&e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>A(!0)),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>te.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),Y&&(q.current?.dispose(),q.current=t.languages.registerCompletionItemProvider(`sql`,fe(t,Y))),z){Z.current.forEach(e=>e.dispose()),Z.current=[];let n=e.getModel(),r=e.addCommand(0,(e,t)=>{t&&He.current(t)});if(r&&n){let e=t.languages.registerCodeLensProvider(`sql`,{provideCodeLenses:e=>{if(e!==n)return{lenses:[],dispose:()=>{}};let t=[],i=e.getValue().split(`
6
- `),a=-1,o=[],s=!1,c=(e,n)=>{let i=n.trim();!i||i.startsWith(`--`)||t.push({range:{startLineNumber:e,startColumn:1,endLineNumber:e,endColumn:1},command:{id:r,title:`▷ Run`,arguments:[i]}})};for(let e=0;e<i.length;e++){let t=i[e].trim();if(a===-1){if(!t||t.startsWith(`--`))continue;a=e+1,o=[]}o.push(i[e]),(t.match(/\$\$/g)||[]).length%2==1&&(s=!s),!s&&t.endsWith(`;`)&&(c(a,o.join(`
7
- `)),a=-1,o=[])}return a>0&&o.join(``).trim()&&c(a,o.join(`
8
- `)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[Y]);if(!i&&!D&&(!n||!Q&&!r))return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(u)return(0,L.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,L.jsx)(T,{className:`size-5 animate-spin`}),(0,L.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(p)return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:p});if(N)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(xe,{filePath:n,projectName:r})});if(P)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(Se,{filePath:n,projectName:r})});if(F)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(Ce,{filePath:n,projectName:r})});if(le)return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(W,{}),children:(0,L.jsx)(we,{filePath:n,projectName:r})});if(c===`base64`)return(0,L.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,L.jsx)(ee,{className:`size-10 text-text-subtle`}),(0,L.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,L.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let Je=z?(0,L.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,L.jsx)(f,{className:`size-3 text-muted-foreground`}),(0,L.jsxs)(`select`,{value:G??``,onChange:e=>{let t=Number(e.target.value);t&&ze(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,L.jsx)(`option`,{value:``,children:`Connection…`}),U.map(e=>(0,L.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,L.jsx)(`button`,{type:`button`,onClick:Ve,disabled:!J,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run all in DB Viewer`,children:(0,L.jsx)(ae,{className:`size-3.5`})})]}):null;return(0,L.jsxs)(`div`,{className:`flex flex-col h-full w-full overflow-hidden`,children:[i!=null&&Fe&&(0,L.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,L.jsx)(`button`,{type:`button`,onClick:Re,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:Ie?`Raw`:`Beautify`})}),n&&r&&t&&(0,L.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,L.jsx)(ge,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),Je,(0,L.jsx)(_e,{ext:M,mdMode:B,onMdModeChange:V,csvMode:H,onCsvModeChange:ye,wordWrap:w,onToggleWordWrap:oe,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),z&&(!r||!t)&&(0,L.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,L.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?b(n):`SQL`}),Je]}),he&&H===`table`?(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,L.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,L.jsx)(be,{content:o??``,onContentChange:Ge,wordWrap:w})}):me&&B===`preview`?(0,L.jsx)(je,{content:o??``}):(0,L.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,L.jsx)(ue,{height:`100%`,language:a??Ae(n??``),value:o??``,onChange:i==null?Ge:void 0,onMount:qe,theme:E,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:w?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:i!=null},loading:(0,L.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})})}),k&&(0,L.jsx)(ve,{open:k,defaultName:`Untitled-${e?.untitledNumber??1}`,content:x.current,onSave:Ke,onCancel:()=>A(!1)})]})});function W(){return(0,L.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,L.jsx)(T,{className:`size-5 animate-spin text-text-subtle`})})}function je({content:e}){return(0,L.jsx)(I.Suspense,{fallback:(0,L.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,L.jsx)(ye,{content:e,className:`flex-1 overflow-auto p-4`})})}export{U as CodeEditor};
@@ -1,4 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{t as r}from"./createLucideIcon-BjHrJDVb.js";import{t as i}from"./text-wrap-DzvCTq_i.js";import{i as a,t as o}from"./api-client-r4nyVy7H.js";import{n as s}from"./settings-store-CdcSAgEZ.js";import"./vendor-mermaid-DCxaaPi4.js";import{$ as c,B as l,U as u,Y as d,h as f}from"./index-DJQJu6Ef.js";import{r as p,t as m}from"./use-monaco-theme-dtPsv6sh.js";var h=r(`panel-right-open`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}],[`path`,{d:`m10 15-3-3 3-3`,key:`1pgupc`}]]),g=e(n(),1),_=t();function v(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`}[e.split(`.`).pop()?.toLowerCase()??``]??`plaintext`}function y({metadata:e}){let t=e?.filePath,n=e?.projectName,r=e?.ref1,y=e?.ref2,x=e?.file1,S=e?.file2,C=e?.original,w=e?.modified,T=C!=null||w!=null,E=!!(x&&S),[D,O]=(0,g.useState)(null),[k,A]=(0,g.useState)(null),[j,M]=(0,g.useState)(null),[N,P]=(0,g.useState)(!T),[F,I]=(0,g.useState)(null),[L,R]=(0,g.useState)(`both`),{wordWrap:z,toggleWordWrap:B}=s(f(e=>({wordWrap:e.wordWrap,toggleWordWrap:e.toggleWordWrap}))),V=m(),H=(0,g.useRef)(null),[U,W]=(0,g.useState)();(0,g.useEffect)(()=>{let e=H.current;if(!e)return;let t=new ResizeObserver(([e])=>{e&&W(Math.floor(e.contentRect.height))});return t.observe(e),()=>t.disconnect()},[N,F]),(0,g.useEffect)(()=>{if(T||!n)return;if(P(!0),I(null),M(null),A(null),O(null),x&&S){let e=new URLSearchParams({file1:x,file2:S});o.get(`${a(n)}/files/compare?${e}`).then(e=>{A(e),P(!1)}).catch(e=>{I(e instanceof Error?e.message:`Failed to compare files`),P(!1)});return}if(t){let e=new URLSearchParams({file:t});r&&e.set(`ref`,r),o.get(`${a(n)}/git/file-full-diff?${e}`).then(e=>{M(e),P(!1)}).catch(e=>{I(e instanceof Error?e.message:`Failed to load diff`),P(!1)});return}let e;if(r||y){let t=new URLSearchParams;r&&t.set(`ref1`,r),y&&t.set(`ref2`,y),e=`${a(n)}/git/diff?${t}`}else e=`${a(n)}/git/diff`;o.get(e).then(e=>{O(e.diff),P(!1)}).catch(e=>{I(e instanceof Error?e.message:`Failed to load diff`),P(!1)})},[t,n,r,y,x,S,T]);let{original:G,modified:K}=(0,g.useMemo)(()=>T?{original:C??``,modified:w??``}:E&&k?k:j||(D?b(D):{original:``,modified:``}),[D,T,C,w,E,k,j]),q=(0,g.useMemo)(()=>{let e=t??S??x;return e?v(e):`plaintext`},[t,x,S]),J=typeof window<`u`&&window.innerWidth<768,Y=!J&&L===`both`;return!n&&!T?(0,_.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground text-sm`,children:`No project selected.`}):N?(0,_.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-muted-foreground`,children:[(0,_.jsx)(u,{className:`size-5 animate-spin`}),(0,_.jsx)(`span`,{className:`text-sm`,children:`Loading diff...`})]}):F?(0,_.jsx)(`div`,{className:`flex items-center justify-center h-full text-destructive text-sm`,children:F}):!T&&!E&&!j&&!G&&!K?(0,_.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-2 text-muted-foreground`,children:[(0,_.jsx)(d,{className:`size-8`}),(0,_.jsx)(`p`,{className:`text-sm`,children:`No content changes`}),t&&(0,_.jsx)(`p`,{className:`text-xs font-mono`,children:t})]}):(0,_.jsxs)(`div`,{className:`flex flex-col h-full`,children:[!J&&(0,_.jsxs)(`div`,{className:`flex items-center justify-end gap-0.5 px-2 py-0.5 border-b border-border shrink-0`,children:[(0,_.jsx)(`button`,{type:`button`,onClick:()=>R(L===`left`?`both`:`left`),className:`p-1 rounded hover:bg-muted transition-colors ${L===`left`?`bg-muted text-foreground`:``}`,title:`Expand original`,children:(0,_.jsx)(l,{className:`size-3.5`})}),(0,_.jsx)(`button`,{type:`button`,onClick:()=>R(`both`),className:`p-1 rounded hover:bg-muted transition-colors ${L===`both`?`bg-muted text-foreground`:``}`,title:`Side by side`,children:(0,_.jsx)(c,{className:`size-3.5`})}),(0,_.jsx)(`button`,{type:`button`,onClick:()=>R(L===`right`?`both`:`right`),className:`p-1 rounded hover:bg-muted transition-colors ${L===`right`?`bg-muted text-foreground`:``}`,title:`Expand modified`,children:(0,_.jsx)(h,{className:`size-3.5`})}),(0,_.jsx)(`div`,{className:`w-px h-3.5 bg-border mx-0.5 shrink-0`}),(0,_.jsx)(`button`,{type:`button`,onClick:B,title:`Toggle word wrap`,className:`p-1 rounded hover:bg-muted transition-colors ${z?`bg-muted text-foreground`:``}`,children:(0,_.jsx)(i,{className:`size-3.5`})})]}),(0,_.jsx)(`div`,{ref:H,className:`flex-1 overflow-hidden`,children:U&&U>0?(0,_.jsx)(p,{height:U,language:q,original:G,modified:K,theme:V,options:{fontSize:J?11:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:J||z?`on`:`off`,renderSideBySide:Y,readOnly:!0,automaticLayout:!0,scrollBeyondLastLine:!1},loading:(0,_.jsx)(u,{className:`size-5 animate-spin text-muted-foreground`})}):(0,_.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,_.jsx)(u,{className:`size-5 animate-spin text-muted-foreground`})})})]})}function b(e){let t=e.split(`
2
- `),n=[],r=[],i=!1;for(let e of t)if(!(e.startsWith(`diff --git`)||e.startsWith(`diff --no-index`)||e.startsWith(`index `)||e.startsWith(`new file`)||e.startsWith(`deleted file`)||e.startsWith(`old mode`)||e.startsWith(`new mode`)||e.startsWith(`---`)||e.startsWith(`+++`)||e.startsWith(`Binary files`)||e.startsWith(`\\ No newline`))){if(e.startsWith(`@@`)){i=!0;continue}if(i)if(e.startsWith(`-`))n.push(e.slice(1));else if(e.startsWith(`+`))r.push(e.slice(1));else{let t=e.startsWith(` `)?e.slice(1):e;n.push(t),r.push(t)}}return{original:n.join(`
3
- `),modified:r.join(`
4
- `)}}export{y as DiffViewer};