@hienlh/ppm 0.13.8 → 0.13.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/ai-settings-section-CLNBWLS4.js +1 -0
- package/dist/web/assets/{audio-preview-DQbX8gfL.js → audio-preview-C1p-Q5XZ.js} +1 -1
- package/dist/web/assets/{chat-tab-BJQT9kie.js → chat-tab-BSJUkgxB.js} +8 -8
- package/dist/web/assets/code-editor-rNw5_pXh.js +8 -0
- package/dist/web/assets/{conflict-editor-BKwJLX0D.js → conflict-editor-Dcn3HuLD.js} +1 -1
- package/dist/web/assets/data-grid-nZfSIop5.js +5 -0
- package/dist/web/assets/database-DOWH9-Vv.js +1 -0
- package/dist/web/assets/database-viewer-CNoq5Uxp.js +1 -0
- package/dist/web/assets/{diff-viewer-SAtaBwNI.js → diff-viewer-NMLD4V8q.js} +1 -1
- package/dist/web/assets/{extension-webview-PiV4bKJ1.js → extension-webview-DW2dBswj.js} +1 -1
- package/dist/web/assets/{image-preview-CbFFD9BS.js → image-preview-Dqp1KSus.js} +1 -1
- package/dist/web/assets/index-CoMWx5VS.js +27 -0
- package/dist/web/assets/index-Dzb3OtrX.css +2 -0
- package/dist/web/assets/{input-BMvRUOr7.js → input-DYWhyaze.js} +1 -1
- package/dist/web/assets/keybindings-store-B7nlHmDh.js +1 -0
- package/dist/web/assets/{markdown-renderer-CHWA0KAo.js → markdown-renderer-DNIXdY0d.js} +1 -1
- package/dist/web/assets/{pdf-preview-DQMdjqa2.js → pdf-preview-ChC1gaaZ.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-9BpNC9_7.js → port-forwarding-tab-dLhH_g2l.js} +1 -1
- package/dist/web/assets/{postgres-viewer-Bm5T51n6.js → postgres-viewer-De0pzd1C.js} +2 -2
- package/dist/web/assets/{settings-tab-BUstSDLR.js → settings-tab-Mrs9uzCZ.js} +1 -1
- package/dist/web/assets/sqlite-viewer-BqtIjvil.js +1 -0
- package/dist/web/assets/{terminal-tab-Xtj6RN0d.js → terminal-tab-CeHEtoE2.js} +1 -1
- package/dist/web/assets/{video-preview-BLI_RruT.js → video-preview-CHPVrMtx.js} +1 -1
- package/dist/web/assets/x-OGGXhtlb.js +1 -0
- package/dist/web/index.html +8 -8
- package/dist/web/sw.js +1 -1
- package/package.json +2 -1
- package/src/web/components/database/data-grid.tsx +18 -2
- package/src/web/components/database/glide-grid-theme.ts +82 -0
- package/src/web/components/database/glide-grid-types.ts +79 -0
- package/src/web/components/database/use-glide-cell-content.ts +124 -0
- package/src/web/components/database/use-glide-columns.ts +61 -0
- package/src/web/components/database/use-glide-selection.ts +48 -0
- package/src/web/components/editor/code-editor.tsx +124 -7
- package/src/web/index.html +1 -0
- package/test.sql +1 -0
- package/dist/web/assets/ai-settings-section-CHgpQ_OP.js +0 -1
- package/dist/web/assets/code-editor-CeKTvfyz.js +0 -8
- package/dist/web/assets/database-DCT0OjgQ.js +0 -1
- package/dist/web/assets/database-viewer-DixWWvjx.js +0 -5
- package/dist/web/assets/index-C1RBJe0a.css +0 -2
- package/dist/web/assets/index-ZFyltHwi.js +0 -27
- package/dist/web/assets/keybindings-store-D0C-Pq2o.js +0 -1
- package/dist/web/assets/plus-51UQ45rf.js +0 -1
- package/dist/web/assets/sqlite-viewer-C7rhO4bn.js +0 -1
- package/dist/web/assets/x-BtqbfkR7.js +0 -1
- /package/dist/web/assets/{chevron-right-BzAdxJRG.js → chevron-right-DnHIvvcy.js} +0 -0
- /package/dist/web/assets/{code-CuravVys.js → code-DGBecc50.js} +0 -0
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":"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||`/`)}))});
|
|
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":"b828c433c952d5869f7f8c6f4f241f09","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/diff-viewer-NMLD4V8q.js"},{"revision":null,"url":"assets/dist-D7KGU7Vl.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/audio-preview-C1p-Q5XZ.js"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/scroll-area-BEllam7_.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-BdPTuzO3.js"},{"revision":null,"url":"assets/terminal-tab-CeHEtoE2.js"},{"revision":null,"url":"assets/chat-tab-BSJUkgxB.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CnaHXUh8.js"},{"revision":null,"url":"assets/port-forwarding-tab-dLhH_g2l.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/settings-tab-Mrs9uzCZ.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/database-DOWH9-Vv.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/info-3K5VOQVL-MHX_1JfR.js"},{"revision":null,"url":"assets/lib-D_kRA9p6.js"},{"revision":null,"url":"assets/dist-CGvx1c8C.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/api-settings-D0_eiIYv.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/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/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/index-Dzb3OtrX.css"},{"revision":null,"url":"assets/code-editor-rNw5_pXh.js"},{"revision":null,"url":"assets/sql-query-editor-CMQpaOjA.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/image-preview-Dqp1KSus.js"},{"revision":null,"url":"assets/data-grid-nZfSIop5.js"},{"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/postgres-viewer-De0pzd1C.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/esm-nXReYVnB.js"},{"revision":null,"url":"assets/code-DGBecc50.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/extension-webview-DW2dBswj.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/pdf-preview-ChC1gaaZ.js"},{"revision":null,"url":"assets/utils-CTg5uAYR.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/conflict-editor-Dcn3HuLD.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/database-viewer-CNoq5Uxp.js"},{"revision":null,"url":"assets/ai-settings-section-CLNBWLS4.js"},{"revision":null,"url":"assets/csv-parser-DO0dz4x_.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/input-DYWhyaze.js"},{"revision":null,"url":"assets/chevron-right-DnHIvvcy.js"},{"revision":null,"url":"assets/video-preview-CHPVrMtx.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/markdown-renderer-DNIXdY0d.js"},{"revision":null,"url":"assets/index-CoMWx5VS.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/x-OGGXhtlb.js"},{"revision":null,"url":"assets/sparkles-B0mRBy_j.js"},{"revision":null,"url":"assets/vendor-ui-B-89Uj8i.js"},{"revision":null,"url":"assets/vendor-xterm-u3AZMvTx.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-CreFbf9A.js"},{"revision":null,"url":"assets/keybindings-store-B7nlHmDh.js"},{"revision":null,"url":"assets/sql-completion-provider-tCzZfqWs.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/sqlite-viewer-BqtIjvil.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.
|
|
3
|
+
"version": "0.13.9",
|
|
4
4
|
"description": "Personal Project Manager — mobile-first web IDE with AI assistance",
|
|
5
5
|
"author": "hienlh",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
|
47
47
|
"@codemirror/lang-sql": "^6.10.0",
|
|
48
|
+
"@glideapps/glide-data-grid": "^6.0.3",
|
|
48
49
|
"@inquirer/prompts": "^8.3.0",
|
|
49
50
|
"@monaco-editor/react": "^4.7.0",
|
|
50
51
|
"@radix-ui/react-switch": "^1.2.6",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useCallback, useMemo, useRef, memo, useEffect } from "react";
|
|
2
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
|
+
import { Loader2, ChevronLeft, ChevronRight, ChevronUp, ChevronDown, Trash2, Plus, Search, X, Eye, ExternalLink, WrapText, Sparkles, GripHorizontal, Filter, Pin, PinOff, Columns3 } from "lucide-react";
|
|
4
4
|
import { useShallow } from "zustand/react/shallow";
|
|
5
5
|
import { useTabStore } from "@/stores/tab-store";
|
|
6
6
|
import { useMonacoTheme } from "@/lib/use-monaco-theme";
|
|
@@ -652,8 +652,24 @@ function DataPreviewPanel({ data, onClose, onOpenInTab }: {
|
|
|
652
652
|
}
|
|
653
653
|
}, [beautified, data.content, data.language]);
|
|
654
654
|
|
|
655
|
+
const [panelHeight, setPanelHeight] = useState(200);
|
|
656
|
+
const handleDrag = useCallback((e: React.MouseEvent) => {
|
|
657
|
+
e.preventDefault();
|
|
658
|
+
const startY = e.clientY;
|
|
659
|
+
const startH = panelHeight;
|
|
660
|
+
const onMove = (ev: MouseEvent) => setPanelHeight(Math.max(80, startH + (startY - ev.clientY)));
|
|
661
|
+
const onUp = () => { document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
|
|
662
|
+
document.addEventListener("mousemove", onMove);
|
|
663
|
+
document.addEventListener("mouseup", onUp);
|
|
664
|
+
}, [panelHeight]);
|
|
665
|
+
|
|
655
666
|
return (
|
|
656
|
-
<div className="shrink-0 border-t border-border flex flex-col" style={{ height:
|
|
667
|
+
<div className="shrink-0 border-t border-border flex flex-col" style={{ height: panelHeight }}>
|
|
668
|
+
{/* Resize handle */}
|
|
669
|
+
<div onMouseDown={handleDrag}
|
|
670
|
+
className="shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors">
|
|
671
|
+
<GripHorizontal className="size-3 text-muted-foreground/50" />
|
|
672
|
+
</div>
|
|
657
673
|
<div className="flex items-center gap-1 px-2 py-1 bg-muted/50 border-b border-border shrink-0">
|
|
658
674
|
<Eye className="size-3 text-muted-foreground" />
|
|
659
675
|
<span className="text-xs font-medium text-foreground truncate flex-1">{data.title}</span>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from "react";
|
|
2
|
+
import type { Theme } from "@glideapps/glide-data-grid";
|
|
3
|
+
import "@glideapps/glide-data-grid/dist/index.css";
|
|
4
|
+
|
|
5
|
+
/** Read a CSS custom property from :root */
|
|
6
|
+
function cssVar(name: string): string {
|
|
7
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Add alpha channel to a hex/rgb color string */
|
|
11
|
+
function withAlpha(color: string, alpha: number): string {
|
|
12
|
+
if (color.startsWith("#")) {
|
|
13
|
+
const hex = color.slice(1);
|
|
14
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
15
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
16
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
17
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
18
|
+
}
|
|
19
|
+
return color;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Build Glide theme from current CSS variables */
|
|
23
|
+
function buildTheme(): Partial<Theme> {
|
|
24
|
+
const bg = cssVar("--color-background");
|
|
25
|
+
const fg = cssVar("--color-foreground");
|
|
26
|
+
const muted = cssVar("--color-muted");
|
|
27
|
+
const mutedFg = cssVar("--color-muted-foreground");
|
|
28
|
+
const primary = cssVar("--color-primary");
|
|
29
|
+
const primaryFg = cssVar("--color-primary-foreground");
|
|
30
|
+
const border = cssVar("--color-border");
|
|
31
|
+
const accent = cssVar("--color-accent");
|
|
32
|
+
const textSecondary = cssVar("--color-text-secondary");
|
|
33
|
+
const textSubtle = cssVar("--color-text-subtle");
|
|
34
|
+
const fontSans = cssVar("--font-sans") || "Geist, system-ui, sans-serif";
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
bgCell: bg,
|
|
38
|
+
bgCellMedium: muted,
|
|
39
|
+
bgHeader: muted,
|
|
40
|
+
bgHeaderHasFocus: accent,
|
|
41
|
+
bgHeaderHovered: accent,
|
|
42
|
+
bgBubble: accent,
|
|
43
|
+
bgBubbleSelected: primary,
|
|
44
|
+
textDark: fg,
|
|
45
|
+
textMedium: textSecondary,
|
|
46
|
+
textLight: textSubtle,
|
|
47
|
+
textHeader: mutedFg,
|
|
48
|
+
textGroupHeader: mutedFg,
|
|
49
|
+
textHeaderSelected: fg,
|
|
50
|
+
textBubble: fg,
|
|
51
|
+
accentColor: primary,
|
|
52
|
+
accentFg: primaryFg,
|
|
53
|
+
accentLight: withAlpha(primary, 0.12),
|
|
54
|
+
borderColor: border,
|
|
55
|
+
horizontalBorderColor: border,
|
|
56
|
+
fontFamily: fontSans,
|
|
57
|
+
baseFontStyle: "13px",
|
|
58
|
+
headerFontStyle: "600 12px",
|
|
59
|
+
editorFontSize: "13px",
|
|
60
|
+
lineHeight: 1.5,
|
|
61
|
+
cellHorizontalPadding: 8,
|
|
62
|
+
cellVerticalPadding: 4,
|
|
63
|
+
headerIconSize: 16,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Hook that returns a Glide Data Grid theme synced to PPM's dark/light mode.
|
|
69
|
+
* Watches <html> class changes via MutationObserver to rebuild on theme toggle.
|
|
70
|
+
*/
|
|
71
|
+
export function useGlideTheme(): Partial<Theme> {
|
|
72
|
+
// Bump counter on theme class change to trigger rebuild
|
|
73
|
+
const [rev, setRev] = useState(0);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const observer = new MutationObserver(() => setRev((r) => r + 1));
|
|
77
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
|
|
78
|
+
return () => observer.disconnect();
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
return useMemo(() => buildTheme(), [rev]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
82
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Shared types for Glide Data Grid wrapper used by database-viewer and sqlite-viewer */
|
|
2
|
+
|
|
3
|
+
/** Unified column schema — superset of DbColumnInfo and sqlite ColumnInfo */
|
|
4
|
+
export interface GridColumnSchema {
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
nullable: boolean;
|
|
8
|
+
pk: boolean;
|
|
9
|
+
defaultValue?: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Unified props interface for the Glide Data Grid wrapper component */
|
|
13
|
+
export interface GlideGridProps {
|
|
14
|
+
/** Column names in display order */
|
|
15
|
+
columns: string[];
|
|
16
|
+
/** Row data array (current page) */
|
|
17
|
+
rows: Record<string, unknown>[];
|
|
18
|
+
/** Total row count across all pages */
|
|
19
|
+
total: number;
|
|
20
|
+
/** Rows per page */
|
|
21
|
+
limit: number;
|
|
22
|
+
/** Column schema metadata */
|
|
23
|
+
schema: GridColumnSchema[];
|
|
24
|
+
/** Whether data is currently loading */
|
|
25
|
+
loading: boolean;
|
|
26
|
+
/** Current page number (1-based) */
|
|
27
|
+
page: number;
|
|
28
|
+
onPageChange: (page: number) => void;
|
|
29
|
+
/** Cell edit: (pkColumn, pkValue, editedColumn, newValue) */
|
|
30
|
+
onCellUpdate: (pkCol: string, pkVal: unknown, col: string, val: unknown) => void;
|
|
31
|
+
onRowDelete?: (pkCol: string, pkVal: unknown) => void;
|
|
32
|
+
onBulkDelete?: (pkCol: string, pkValues: unknown[]) => void;
|
|
33
|
+
onInsertRow?: (values: Record<string, unknown>) => Promise<void>;
|
|
34
|
+
/** Current sort column */
|
|
35
|
+
orderBy?: string | null;
|
|
36
|
+
orderDir?: "ASC" | "DESC";
|
|
37
|
+
onToggleSort?: (column: string) => void;
|
|
38
|
+
/** Per-column ILIKE filters (server-side) */
|
|
39
|
+
columnFilters?: Record<string, string>;
|
|
40
|
+
onColumnFilter?: (filters: Record<string, string>) => void;
|
|
41
|
+
/** Metadata for export/viewer features */
|
|
42
|
+
connectionId?: number;
|
|
43
|
+
selectedTable?: string | null;
|
|
44
|
+
selectedSchema?: string;
|
|
45
|
+
connectionName?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Threshold in bytes for showing cell viewer (eye button) */
|
|
49
|
+
export const LARGE_THRESHOLD = 200;
|
|
50
|
+
|
|
51
|
+
/** Check if a cell value needs the large data viewer */
|
|
52
|
+
export function needsViewer(val: unknown): boolean {
|
|
53
|
+
if (val == null) return false;
|
|
54
|
+
if (typeof val === "object") return true;
|
|
55
|
+
const s = String(val);
|
|
56
|
+
if (s.length >= LARGE_THRESHOLD) return true;
|
|
57
|
+
const trimmed = s.trimStart();
|
|
58
|
+
if ((trimmed[0] === "{" || trimmed[0] === "[") && (trimmed.endsWith("}") || trimmed.endsWith("]"))) return true;
|
|
59
|
+
if (trimmed.startsWith("<?xml") || (trimmed.startsWith("<") && trimmed.endsWith(">"))) return true;
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Format cell value for display — JSON-stringify objects, otherwise String() */
|
|
64
|
+
export function formatCellValue(val: unknown): string {
|
|
65
|
+
if (val == null) return "NULL";
|
|
66
|
+
if (typeof val === "object") return JSON.stringify(val);
|
|
67
|
+
return String(val);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Detect language from cell content for syntax highlighting in viewer */
|
|
71
|
+
export function detectLang(text: string): string {
|
|
72
|
+
const t = text.trimStart();
|
|
73
|
+
if (t[0] === "{" || t[0] === "[") {
|
|
74
|
+
try { JSON.parse(t); return "json"; } catch { /* not json */ }
|
|
75
|
+
}
|
|
76
|
+
if (t.startsWith("<?xml") || (t.startsWith("<") && /<\/\w+>/.test(t))) return "xml";
|
|
77
|
+
if (t.startsWith("---") || /^\w+:\s/m.test(t)) return "yaml";
|
|
78
|
+
return "plaintext";
|
|
79
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
import { GridCellKind, type GridCell, type EditableGridCell, type Item } from "@glideapps/glide-data-grid";
|
|
3
|
+
import type { GridColumnSchema } from "./glide-grid-types";
|
|
4
|
+
import { formatCellValue } from "./glide-grid-types";
|
|
5
|
+
|
|
6
|
+
/** Map DB type string to Glide cell kind */
|
|
7
|
+
function dbTypeToKind(type: string): GridCellKind {
|
|
8
|
+
const t = type.toLowerCase();
|
|
9
|
+
if (/^(int|serial|bigint|smallint|float|double|decimal|numeric|real|money)/.test(t)) {
|
|
10
|
+
return GridCellKind.Number;
|
|
11
|
+
}
|
|
12
|
+
if (/^bool/.test(t)) return GridCellKind.Boolean;
|
|
13
|
+
return GridCellKind.Text;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Truncate display string for canvas rendering performance */
|
|
17
|
+
function truncateDisplay(val: string, max = 200): string {
|
|
18
|
+
return val.length > max ? val.slice(0, max) + "…" : val;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface UseGlideCellContentResult {
|
|
22
|
+
getCellContent: (cell: Item) => GridCell;
|
|
23
|
+
onCellEdited: (cell: Item, newValue: EditableGridCell) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Provides getCellContent and onCellEdited callbacks for Glide Data Grid.
|
|
28
|
+
* Uses refs for rows/columnOrder to avoid stale closures in canvas render loop.
|
|
29
|
+
*/
|
|
30
|
+
export function useGlideCellContent(
|
|
31
|
+
rows: Record<string, unknown>[],
|
|
32
|
+
columnOrder: string[],
|
|
33
|
+
schema: GridColumnSchema[],
|
|
34
|
+
pkCol: string | null,
|
|
35
|
+
onCellUpdate: (pkCol: string, pkVal: unknown, col: string, val: unknown) => void,
|
|
36
|
+
): UseGlideCellContentResult {
|
|
37
|
+
// Use refs to avoid stale data in canvas callbacks
|
|
38
|
+
const rowsRef = useRef(rows);
|
|
39
|
+
rowsRef.current = rows;
|
|
40
|
+
const colOrderRef = useRef(columnOrder);
|
|
41
|
+
colOrderRef.current = columnOrder;
|
|
42
|
+
|
|
43
|
+
const schemaMap = useRef(new Map<string, GridColumnSchema>());
|
|
44
|
+
schemaMap.current = new Map(schema.map((s) => [s.name, s]));
|
|
45
|
+
|
|
46
|
+
const getCellContent = useCallback(([colIdx, rowIdx]: Item): GridCell => {
|
|
47
|
+
const colName = colOrderRef.current[colIdx];
|
|
48
|
+
const row = rowsRef.current[rowIdx];
|
|
49
|
+
if (!colName || !row) {
|
|
50
|
+
return { kind: GridCellKind.Text, data: "", displayData: "", allowOverlay: false };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const val = row[colName];
|
|
54
|
+
const colSchema = schemaMap.current.get(colName);
|
|
55
|
+
const kind = colSchema ? dbTypeToKind(colSchema.type) : GridCellKind.Text;
|
|
56
|
+
const isPk = colSchema?.pk ?? false;
|
|
57
|
+
|
|
58
|
+
// NULL values — styled as italic gray text
|
|
59
|
+
if (val == null) {
|
|
60
|
+
return {
|
|
61
|
+
kind: GridCellKind.Text,
|
|
62
|
+
data: "",
|
|
63
|
+
displayData: "NULL",
|
|
64
|
+
allowOverlay: !isPk,
|
|
65
|
+
readonly: isPk,
|
|
66
|
+
themeOverride: { textDark: "#6b7280" },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Number cells
|
|
71
|
+
if (kind === GridCellKind.Number && typeof val === "number") {
|
|
72
|
+
return {
|
|
73
|
+
kind: GridCellKind.Number,
|
|
74
|
+
data: val,
|
|
75
|
+
displayData: String(val),
|
|
76
|
+
allowOverlay: !isPk,
|
|
77
|
+
readonly: isPk,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Boolean cells — display only, edit via text overlay
|
|
82
|
+
if (kind === GridCellKind.Boolean && typeof val === "boolean") {
|
|
83
|
+
return {
|
|
84
|
+
kind: GridCellKind.Boolean,
|
|
85
|
+
data: val,
|
|
86
|
+
readonly: isPk,
|
|
87
|
+
allowOverlay: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Default: Text cell
|
|
92
|
+
const strVal = formatCellValue(val);
|
|
93
|
+
return {
|
|
94
|
+
kind: GridCellKind.Text,
|
|
95
|
+
data: strVal,
|
|
96
|
+
displayData: truncateDisplay(strVal),
|
|
97
|
+
allowOverlay: !isPk,
|
|
98
|
+
readonly: isPk,
|
|
99
|
+
};
|
|
100
|
+
}, []); // stable — reads from refs
|
|
101
|
+
|
|
102
|
+
const onCellEdited = useCallback(([colIdx, rowIdx]: Item, newValue: EditableGridCell) => {
|
|
103
|
+
if (!pkCol) return;
|
|
104
|
+
const colName = colOrderRef.current[colIdx];
|
|
105
|
+
const row = rowsRef.current[rowIdx];
|
|
106
|
+
if (!colName || !row) return;
|
|
107
|
+
|
|
108
|
+
const pkVal = row[pkCol];
|
|
109
|
+
let parsed: unknown;
|
|
110
|
+
if (newValue.kind === GridCellKind.Text) {
|
|
111
|
+
parsed = newValue.data === "" ? null : newValue.data;
|
|
112
|
+
} else if (newValue.kind === GridCellKind.Number) {
|
|
113
|
+
parsed = newValue.data;
|
|
114
|
+
} else if (newValue.kind === GridCellKind.Boolean) {
|
|
115
|
+
parsed = newValue.data;
|
|
116
|
+
} else {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
onCellUpdate(pkCol, pkVal, colName, parsed);
|
|
121
|
+
}, [pkCol, onCellUpdate]);
|
|
122
|
+
|
|
123
|
+
return { getCellContent, onCellEdited };
|
|
124
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import type { GridColumn } from "@glideapps/glide-data-grid";
|
|
3
|
+
import type { GridColumnSchema } from "./glide-grid-types";
|
|
4
|
+
|
|
5
|
+
interface UseGlideColumnsResult {
|
|
6
|
+
/** Ordered GridColumn definitions (pinned first) */
|
|
7
|
+
columns: GridColumn[];
|
|
8
|
+
/** Number of frozen columns from left */
|
|
9
|
+
freezeColumns: number;
|
|
10
|
+
/** Column name order matching GridColumn indices */
|
|
11
|
+
columnOrder: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build Glide Data Grid column definitions from schema.
|
|
16
|
+
* Reorders columns: pinned first, then unpinned.
|
|
17
|
+
*/
|
|
18
|
+
export function useGlideColumns(
|
|
19
|
+
schema: GridColumnSchema[],
|
|
20
|
+
columnNames: string[],
|
|
21
|
+
pinnedCols: Set<string>,
|
|
22
|
+
colWidths: Map<string, number>,
|
|
23
|
+
orderBy?: string | null,
|
|
24
|
+
orderDir?: "ASC" | "DESC",
|
|
25
|
+
): UseGlideColumnsResult {
|
|
26
|
+
return useMemo(() => {
|
|
27
|
+
// Reorder: pinned first, then rest (preserve original order within each group)
|
|
28
|
+
const pinned = columnNames.filter((c) => pinnedCols.has(c));
|
|
29
|
+
const unpinned = columnNames.filter((c) => !pinnedCols.has(c));
|
|
30
|
+
const ordered = [...pinned, ...unpinned];
|
|
31
|
+
|
|
32
|
+
const schemaMap = new Map(schema.map((s) => [s.name, s]));
|
|
33
|
+
|
|
34
|
+
const columns: GridColumn[] = ordered.map((name) => {
|
|
35
|
+
const col = schemaMap.get(name);
|
|
36
|
+
const isPk = col?.pk ?? false;
|
|
37
|
+
|
|
38
|
+
// Determine sort icon
|
|
39
|
+
let icon: string | undefined;
|
|
40
|
+
if (orderBy === name) {
|
|
41
|
+
icon = orderDir === "ASC" ? "sortAsc" : "sortDesc";
|
|
42
|
+
} else if (isPk) {
|
|
43
|
+
icon = "headerRowID";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
title: name,
|
|
48
|
+
id: name,
|
|
49
|
+
width: colWidths.get(name) ?? 150,
|
|
50
|
+
hasMenu: true,
|
|
51
|
+
icon,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
columns,
|
|
57
|
+
freezeColumns: pinned.length,
|
|
58
|
+
columnOrder: ordered,
|
|
59
|
+
};
|
|
60
|
+
}, [schema, columnNames, pinnedCols, colWidths, orderBy, orderDir]);
|
|
61
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useState, useCallback, useMemo } from "react";
|
|
2
|
+
import { CompactSelection, type GridSelection } from "@glideapps/glide-data-grid";
|
|
3
|
+
|
|
4
|
+
const EMPTY_SELECTION: GridSelection = {
|
|
5
|
+
columns: CompactSelection.empty(),
|
|
6
|
+
rows: CompactSelection.empty(),
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
interface UseGlideSelectionResult {
|
|
10
|
+
gridSelection: GridSelection;
|
|
11
|
+
onGridSelectionChange: (newSel: GridSelection) => void;
|
|
12
|
+
/** Array of selected row indices (derived from CompactSelection) */
|
|
13
|
+
selectedRowIndices: number[];
|
|
14
|
+
clearSelection: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Manages controlled selection state for Glide Data Grid.
|
|
19
|
+
* Provides row indices array for bulk operations (delete, export).
|
|
20
|
+
*/
|
|
21
|
+
export function useGlideSelection(): UseGlideSelectionResult {
|
|
22
|
+
const [gridSelection, setGridSelection] = useState<GridSelection>(EMPTY_SELECTION);
|
|
23
|
+
|
|
24
|
+
const onGridSelectionChange = useCallback((newSel: GridSelection) => {
|
|
25
|
+
setGridSelection(newSel);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const selectedRowIndices = useMemo(() => {
|
|
29
|
+
const indices: number[] = [];
|
|
30
|
+
if (gridSelection.rows) {
|
|
31
|
+
for (const range of gridSelection.rows) {
|
|
32
|
+
// CompactSelection stores [start, end) ranges
|
|
33
|
+
if (Array.isArray(range)) {
|
|
34
|
+
for (let i = range[0]; i < range[1]; i++) indices.push(i);
|
|
35
|
+
} else {
|
|
36
|
+
indices.push(range);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return indices;
|
|
41
|
+
}, [gridSelection.rows]);
|
|
42
|
+
|
|
43
|
+
const clearSelection = useCallback(() => {
|
|
44
|
+
setGridSelection(EMPTY_SELECTION);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return { gridSelection, onGridSelectionChange, selectedRowIndices, clearSelection };
|
|
48
|
+
}
|