@hienlh/ppm 0.13.6 → 0.13.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -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-CHgpQ_OP.js +1 -0
- package/dist/web/assets/{api-settings-t7Leca7J.js → api-settings-D0_eiIYv.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-WMbLpD5Y.js +1 -0
- package/dist/web/assets/{audio-preview-DEJiSXcO.js → audio-preview-DQbX8gfL.js} +1 -1
- package/dist/web/assets/{chat-tab-DQBl8oq4.js → chat-tab-BJQT9kie.js} +9 -9
- package/dist/web/assets/code-editor-CeKTvfyz.js +8 -0
- package/dist/web/assets/{conflict-editor-BZ4mdQJX.js → conflict-editor-BKwJLX0D.js} +1 -1
- package/dist/web/assets/{csv-preview-C9qGhDlb.js → csv-preview-D5lmgVEy.js} +1 -1
- package/dist/web/assets/database-viewer-DixWWvjx.js +5 -0
- package/dist/web/assets/diff-viewer-SAtaBwNI.js +4 -0
- package/dist/web/assets/{esm-B3je8j5P.js → esm-nXReYVnB.js} +1 -1
- package/dist/web/assets/{extension-webview-DEOG3sef.js → extension-webview-PiV4bKJ1.js} +1 -1
- package/dist/web/assets/{file-store-BgZggznw.js → file-store-BrbCNyLm.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BdPTuzO3.js +1 -0
- package/dist/web/assets/{image-preview-YNpn3xj7.js → image-preview-CbFFD9BS.js} +1 -1
- package/dist/web/assets/{index-BJ76xcQz.css → index-C1RBJe0a.css} +1 -1
- package/dist/web/assets/index-ZFyltHwi.js +27 -0
- package/dist/web/assets/info-3K5VOQVL-MHX_1JfR.js +1 -0
- package/dist/web/assets/{input-bGJExpJZ.js → input-BMvRUOr7.js} +1 -1
- package/dist/web/assets/keybindings-store-D0C-Pq2o.js +1 -0
- package/dist/web/assets/{markdown-renderer-B7yiCjpQ.js → markdown-renderer-CHWA0KAo.js} +3 -3
- package/dist/web/assets/packet-RMMSAZCW-CreFbf9A.js +1 -0
- package/dist/web/assets/{pdf-preview-D3u3Hr2R.js → pdf-preview-DQMdjqa2.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-CnaHXUh8.js +1 -0
- package/dist/web/assets/port-forwarding-tab-9BpNC9_7.js +1 -0
- package/dist/web/assets/{postgres-viewer-Cf4sjXxY.js → postgres-viewer-Bm5T51n6.js} +3 -3
- package/dist/web/assets/radar-KQ55EAFF-UxsdRHvt.js +1 -0
- package/dist/web/assets/{scroll-area-D0EQpAH2.js → scroll-area-BEllam7_.js} +1 -1
- package/dist/web/assets/{settings-store-CdcSAgEZ.js → settings-store-BHBb62gq.js} +2 -2
- package/dist/web/assets/settings-tab-BUstSDLR.js +1 -0
- package/dist/web/assets/sparkles-B0mRBy_j.js +1 -0
- package/dist/web/assets/{sql-query-editor-vpD0I0KG.js → sql-query-editor-CMQpaOjA.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-WAA8mf_X.js → sqlite-viewer-C7rhO4bn.js} +1 -1
- package/dist/web/assets/tab-store-0rGchMXr.js +1 -0
- package/dist/web/assets/terminal-tab-Xtj6RN0d.js +1 -0
- package/dist/web/assets/treemap-KZPCXAKY-CBVPi4NV.js +1 -0
- package/dist/web/assets/{use-blob-url-BgxxT-n_.js → use-blob-url-Hn6n1730.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-dtPsv6sh.js → use-monaco-theme-CP-vyTF8.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-DCxaaPi4.js → vendor-mermaid-CMiurk2b.js} +2 -2
- package/dist/web/assets/{video-preview-CRDjNIVX.js → video-preview-BLI_RruT.js} +1 -1
- package/dist/web/index.html +14 -14
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/server/ws/terminal.ts +4 -0
- package/src/services/terminal.service.ts +4 -1
- package/src/web/components/database/data-grid.tsx +117 -17
- package/src/web/components/editor/code-editor.tsx +36 -3
- package/src/web/components/editor/editor-mobile-toolbar.tsx +144 -0
- package/src/web/components/layout/draggable-tab.tsx +1 -0
- package/src/web/components/terminal/terminal-tab.tsx +19 -8
- package/src/web/hooks/use-terminal.ts +22 -2
- package/src/web/stores/panel-utils.ts +1 -0
- package/dist/web/assets/ai-settings-section-DeW4WN43.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-Dy3PgD6O.js +0 -1
- package/dist/web/assets/code-editor-JxVGEgWY.js +0 -8
- package/dist/web/assets/database-viewer-CBfw4hkk.js +0 -2
- package/dist/web/assets/diff-viewer-4hobWs-B.js +0 -4
- package/dist/web/assets/gitGraph-HDMCJU4V-Bu1SIFFq.js +0 -1
- package/dist/web/assets/index-C7gvr4Xo.js +0 -27
- package/dist/web/assets/info-3K5VOQVL-DzfAxmVd.js +0 -1
- package/dist/web/assets/keybindings-store-BhvgfX51.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DpzHf4xp.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BpzFCKJ8.js +0 -1
- package/dist/web/assets/port-forwarding-tab-CSGpa5Bk.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-DAxWKxM4.js +0 -1
- package/dist/web/assets/settings-tab-BLlfwRRc.js +0 -1
- package/dist/web/assets/tab-store-Jvy1eZGM.js +0 -1
- package/dist/web/assets/terminal-tab-Bn8wLy9d.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-D6dgXbAe.js +0 -1
- /package/dist/web/assets/{api-client-r4nyVy7H.js → api-client-Dvzcc_EO.js} +0 -0
- /package/dist/web/assets/{csv-parser-DxVplKKB.js → csv-parser-DO0dz4x_.js} +0 -0
- /package/dist/web/assets/{dist-BqoEabX7.js → dist-CGvx1c8C.js} +0 -0
- /package/dist/web/assets/{katex-bpagxk3Z.js → katex-BFE6i_OH.js} +0 -0
- /package/dist/web/assets/{lib-BqkcKGFq.js → lib-D_kRA9p6.js} +0 -0
- /package/dist/web/assets/{react-BkWDCPD7.js → react-GqWghJ-L.js} +0 -0
- /package/dist/web/assets/{sql-completion-provider-EzHOQLfo.js → sql-completion-provider-tCzZfqWs.js} +0 -0
- /package/dist/web/assets/{table-DbSviOmw.js → table-Dq575bPF.js} +0 -0
- /package/dist/web/assets/{text-wrap-DzvCTq_i.js → text-wrap-Cn6BNQfq.js} +0 -0
- /package/dist/web/assets/{trash-2-BgDIBl6f.js → trash-2-CJYoLw7Q.js} +0 -0
- /package/dist/web/assets/{utils-ChWX7pZv.js → utils-CTg5uAYR.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-D7SePDJp.js → vendor-xterm-u3AZMvTx.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-Baz81y5z.js";import"./api-client-
|
|
1
|
+
import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-Baz81y5z.js";import"./api-client-Dvzcc_EO.js";import{G as n}from"./index-ZFyltHwi.js";import{t as r}from"./use-blob-url-Hn6n1730.js";var i=e();function a({filePath:e,projectName:a}){let{blobUrl:o,error:s}=r(e,a);return s?(0,i.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,i.jsx)(t,{className:`size-10 text-text-subtle`}),(0,i.jsx)(`p`,{className:`text-sm`,children:`Failed to load video.`})]}):o?(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,i.jsx)(`video`,{src:o,controls:!0,className:`max-w-full max-h-full`})}):(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,i.jsx)(n,{className:`size-5 animate-spin text-text-subtle`})})}export{a as VideoPreview};
|
package/dist/web/index.html
CHANGED
|
@@ -39,30 +39,30 @@
|
|
|
39
39
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
40
40
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
41
41
|
<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
42
|
-
<script type="module" crossorigin src="/assets/index-
|
|
42
|
+
<script type="module" crossorigin src="/assets/index-ZFyltHwi.js"></script>
|
|
43
43
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
|
|
44
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-
|
|
44
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-CMiurk2b.js">
|
|
45
45
|
<link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
|
|
46
46
|
<link rel="modulepreload" crossorigin href="/assets/vendor-ui-B-89Uj8i.js">
|
|
47
|
-
<link rel="modulepreload" crossorigin href="/assets/utils-
|
|
47
|
+
<link rel="modulepreload" crossorigin href="/assets/utils-CTg5uAYR.js">
|
|
48
48
|
<link rel="modulepreload" crossorigin href="/assets/createLucideIcon-BjHrJDVb.js">
|
|
49
49
|
<link rel="modulepreload" crossorigin href="/assets/x-BtqbfkR7.js">
|
|
50
|
-
<link rel="modulepreload" crossorigin href="/assets/input-
|
|
51
|
-
<link rel="modulepreload" crossorigin href="/assets/react-
|
|
52
|
-
<link rel="modulepreload" crossorigin href="/assets/api-client-
|
|
53
|
-
<link rel="modulepreload" crossorigin href="/assets/settings-store-
|
|
54
|
-
<link rel="modulepreload" crossorigin href="/assets/scroll-area-
|
|
50
|
+
<link rel="modulepreload" crossorigin href="/assets/input-BMvRUOr7.js">
|
|
51
|
+
<link rel="modulepreload" crossorigin href="/assets/react-GqWghJ-L.js">
|
|
52
|
+
<link rel="modulepreload" crossorigin href="/assets/api-client-Dvzcc_EO.js">
|
|
53
|
+
<link rel="modulepreload" crossorigin href="/assets/settings-store-BHBb62gq.js">
|
|
54
|
+
<link rel="modulepreload" crossorigin href="/assets/scroll-area-BEllam7_.js">
|
|
55
55
|
<link rel="modulepreload" crossorigin href="/assets/dist-D7KGU7Vl.js">
|
|
56
56
|
<link rel="modulepreload" crossorigin href="/assets/plus-51UQ45rf.js">
|
|
57
57
|
<link rel="modulepreload" crossorigin href="/assets/refresh-cw-CSFrDtiu.js">
|
|
58
|
-
<link rel="modulepreload" crossorigin href="/assets/trash-2-
|
|
59
|
-
<link rel="modulepreload" crossorigin href="/assets/api-settings-
|
|
60
|
-
<link rel="modulepreload" crossorigin href="/assets/ai-settings-section-
|
|
58
|
+
<link rel="modulepreload" crossorigin href="/assets/trash-2-CJYoLw7Q.js">
|
|
59
|
+
<link rel="modulepreload" crossorigin href="/assets/api-settings-D0_eiIYv.js">
|
|
60
|
+
<link rel="modulepreload" crossorigin href="/assets/ai-settings-section-CHgpQ_OP.js">
|
|
61
61
|
<link rel="modulepreload" crossorigin href="/assets/chevron-right-BzAdxJRG.js">
|
|
62
62
|
<link rel="modulepreload" crossorigin href="/assets/database-DCT0OjgQ.js">
|
|
63
|
-
<link rel="modulepreload" crossorigin href="/assets/file-store-
|
|
64
|
-
<link rel="modulepreload" crossorigin href="/assets/tab-store-
|
|
65
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
63
|
+
<link rel="modulepreload" crossorigin href="/assets/file-store-BrbCNyLm.js">
|
|
64
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-store-0rGchMXr.js">
|
|
65
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C1RBJe0a.css">
|
|
66
66
|
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
67
67
|
<body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
|
|
68
68
|
<div id="root"></div>
|
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"15aac7907981c1ae87e62d1dc7acc2df","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/sql-query-editor-vpD0I0KG.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-DpzHf4xp.js"},{"revision":null,"url":"assets/database-viewer-CBfw4hkk.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/settings-tab-BLlfwRRc.js"},{"revision":null,"url":"assets/scroll-area-D0EQpAH2.js"},{"revision":null,"url":"assets/vendor-xterm-D7SePDJp.js"},{"revision":null,"url":"assets/extension-webview-DEOG3sef.js"},{"revision":null,"url":"assets/markdown-renderer-B7yiCjpQ.js"},{"revision":null,"url":"assets/file-exclamation-point-Baz81y5z.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"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/keybindings-store-BhvgfX51.js"},{"revision":null,"url":"assets/image-preview-YNpn3xj7.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/code-editor-JxVGEgWY.js"},{"revision":null,"url":"assets/pdf-preview-D3u3Hr2R.js"},{"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/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/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/terminal-tab-Bn8wLy9d.js"},{"revision":null,"url":"assets/sql-completion-provider-EzHOQLfo.js"},{"revision":null,"url":"assets/audio-preview-DEJiSXcO.js"},{"revision":null,"url":"assets/conflict-editor-BZ4mdQJX.js"},{"revision":null,"url":"assets/esm-B3je8j5P.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-D6dgXbAe.js"},{"revision":null,"url":"assets/port-forwarding-tab-CSGpa5Bk.js"},{"revision":null,"url":"assets/index-C7gvr4Xo.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/video-preview-CRDjNIVX.js"},{"revision":null,"url":"assets/sqlite-viewer-WAA8mf_X.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/diff-viewer-4hobWs-B.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/code-CuravVys.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/chat-tab-DQBl8oq4.js"},{"revision":null,"url":"assets/postgres-viewer-Cf4sjXxY.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/use-monaco-theme-dtPsv6sh.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/table-DbSviOmw.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
|
1
|
+
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"0243ed2ed58acd13176b487d17e042d9","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/dist-D7KGU7Vl.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/scroll-area-BEllam7_.js"},{"revision":null,"url":"assets/extension-webview-PiV4bKJ1.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-BdPTuzO3.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CnaHXUh8.js"},{"revision":null,"url":"assets/audio-preview-DQbX8gfL.js"},{"revision":null,"url":"assets/chat-tab-BJQT9kie.js"},{"revision":null,"url":"assets/ai-settings-section-CHgpQ_OP.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/code-editor-CeKTvfyz.js"},{"revision":null,"url":"assets/settings-store-BHBb62gq.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/file-exclamation-point-Baz81y5z.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/diff-viewer-SAtaBwNI.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/info-3K5VOQVL-MHX_1JfR.js"},{"revision":null,"url":"assets/conflict-editor-BKwJLX0D.js"},{"revision":null,"url":"assets/lib-D_kRA9p6.js"},{"revision":null,"url":"assets/x-BtqbfkR7.js"},{"revision":null,"url":"assets/dist-CGvx1c8C.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/postgres-viewer-Bm5T51n6.js"},{"revision":null,"url":"assets/api-settings-D0_eiIYv.js"},{"revision":null,"url":"assets/port-forwarding-tab-9BpNC9_7.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-CBVPi4NV.js"},{"revision":null,"url":"assets/csv-preview-D5lmgVEy.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/index-C1RBJe0a.css"},{"revision":null,"url":"assets/radar-KQ55EAFF-UxsdRHvt.js"},{"revision":null,"url":"assets/api-client-Dvzcc_EO.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-WMbLpD5Y.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/tab-store-0rGchMXr.js"},{"revision":null,"url":"assets/settings-tab-BUstSDLR.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/database-viewer-DixWWvjx.js"},{"revision":null,"url":"assets/pdf-preview-DQMdjqa2.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/sql-query-editor-CMQpaOjA.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/markdown-renderer-CHWA0KAo.js"},{"revision":null,"url":"assets/sqlite-viewer-C7rhO4bn.js"},{"revision":null,"url":"assets/input-BMvRUOr7.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/esm-nXReYVnB.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/github-dark-dimmed.min-BrpRStFV.css"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/katex-BFE6i_OH.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/trash-2-CJYoLw7Q.js"},{"revision":null,"url":"assets/keybindings-store-D0C-Pq2o.js"},{"revision":null,"url":"assets/use-monaco-theme-CP-vyTF8.js"},{"revision":null,"url":"assets/file-store-BrbCNyLm.js"},{"revision":null,"url":"assets/use-blob-url-Hn6n1730.js"},{"revision":null,"url":"assets/utils-CTg5uAYR.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/database-DCT0OjgQ.js"},{"revision":null,"url":"assets/vendor-mermaid-CMiurk2b.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/csv-parser-DO0dz4x_.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/terminal-tab-Xtj6RN0d.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/sparkles-B0mRBy_j.js"},{"revision":null,"url":"assets/vendor-ui-B-89Uj8i.js"},{"revision":null,"url":"assets/index-ZFyltHwi.js"},{"revision":null,"url":"assets/vendor-xterm-u3AZMvTx.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-CreFbf9A.js"},{"revision":null,"url":"assets/sql-completion-provider-tCzZfqWs.js"},{"revision":null,"url":"assets/image-preview-CbFFD9BS.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/video-preview-BLI_RruT.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
package/package.json
CHANGED
|
@@ -83,6 +83,10 @@ export const terminalWebSocket = {
|
|
|
83
83
|
|
|
84
84
|
close(ws: { data: { type: string; id: string } }) {
|
|
85
85
|
const sessionId = ws.data.id;
|
|
86
|
+
const session = terminalService.get(sessionId);
|
|
87
|
+
// Only clean up if this WS is still the active connection.
|
|
88
|
+
// Prevents race: stale WS close fires after new WS open, removing the new listener.
|
|
89
|
+
if (!session || session.ws !== ws) return;
|
|
86
90
|
terminalService.removeOutputListener(sessionId);
|
|
87
91
|
terminalService.setDisconnected(sessionId);
|
|
88
92
|
},
|
|
@@ -144,7 +144,10 @@ class TerminalService {
|
|
|
144
144
|
};
|
|
145
145
|
const onExit = () => {
|
|
146
146
|
const listener = this.outputListeners.get(id);
|
|
147
|
-
if (listener)
|
|
147
|
+
if (listener) {
|
|
148
|
+
listener(id, "\r\n[Process exited]\r\n");
|
|
149
|
+
listener(id, JSON.stringify({ type: "exited" }));
|
|
150
|
+
}
|
|
148
151
|
};
|
|
149
152
|
|
|
150
153
|
const pty = isWindows
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useState, useCallback, useMemo, useRef, memo, useEffect } from "react";
|
|
2
|
-
import
|
|
2
|
+
import Editor from "@monaco-editor/react";
|
|
3
|
+
import { Loader2, ChevronLeft, ChevronRight, ChevronUp, ChevronDown, Trash2, Plus, Search, X, Eye, ExternalLink, WrapText, Sparkles, Filter, Pin, PinOff, Columns3 } from "lucide-react";
|
|
3
4
|
import { useShallow } from "zustand/react/shallow";
|
|
4
5
|
import { useTabStore } from "@/stores/tab-store";
|
|
6
|
+
import { useMonacoTheme } from "@/lib/use-monaco-theme";
|
|
5
7
|
import type { DbColumnInfo } from "./use-database";
|
|
6
8
|
import { ExportButton } from "./export-button";
|
|
7
9
|
|
|
@@ -44,15 +46,14 @@ export function DataGrid({
|
|
|
44
46
|
const [insertError, setInsertError] = useState<string | null>(null);
|
|
45
47
|
const [confirmBulkDelete, setConfirmBulkDelete] = useState(false);
|
|
46
48
|
const { openTab } = useTabStore(useShallow((s) => ({ openTab: s.openTab })));
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}, [openTab]);
|
|
49
|
+
const [previewData, setPreviewData] = useState<{ title: string; content: string; language: string; viewerKey: string } | null>(null);
|
|
50
|
+
const openCellViewer = useCallback((cell: { col: string; value: string; pkVal: string }) => {
|
|
51
|
+
const table = selectedTable ?? "";
|
|
52
|
+
const lang = detectLang(cell.value);
|
|
53
|
+
const key = `${connectionId ?? "local"}:${table}:${cell.col}:${cell.pkVal}`;
|
|
54
|
+
const title = table ? `${cell.col} #${cell.pkVal} — ${table}` : `${cell.col} #${cell.pkVal}`;
|
|
55
|
+
setPreviewData({ title, content: cell.value, language: lang, viewerKey: key });
|
|
56
|
+
}, [connectionId, selectedTable]);
|
|
56
57
|
const [pinnedCols, setPinnedCols] = useState<Set<string>>(new Set());
|
|
57
58
|
const [pinnedRows, setPinnedRows] = useState<Set<number>>(new Set());
|
|
58
59
|
const [filterOpenCol, setFilterOpenCol] = useState<string | null>(null);
|
|
@@ -69,14 +70,21 @@ export function DataGrid({
|
|
|
69
70
|
const openRowViewer = useCallback((row: Record<string, unknown>) => {
|
|
70
71
|
const json = JSON.stringify(row, null, 2);
|
|
71
72
|
const pk = pkCol ? String(row[pkCol] ?? "") : "";
|
|
73
|
+
const table = selectedTable ?? "";
|
|
74
|
+
const key = `${connectionId ?? "local"}:${table}:row:${pk || "unknown"}`;
|
|
75
|
+
const title = pk ? `Row #${pk}${table ? ` — ${table}` : ""}` : `Row${table ? ` — ${table}` : ""}`;
|
|
76
|
+
setPreviewData({ title, content: json, language: "json", viewerKey: key });
|
|
77
|
+
}, [pkCol, connectionId, selectedTable]);
|
|
78
|
+
const openPreviewInTab = useCallback(() => {
|
|
79
|
+
if (!previewData) return;
|
|
72
80
|
openTab({
|
|
73
81
|
type: "editor",
|
|
74
|
-
title:
|
|
82
|
+
title: previewData.title,
|
|
75
83
|
projectId: null,
|
|
76
84
|
closable: true,
|
|
77
|
-
metadata: { inlineContent:
|
|
85
|
+
metadata: { inlineContent: previewData.content, inlineLanguage: previewData.language, viewerKey: previewData.viewerKey },
|
|
78
86
|
});
|
|
79
|
-
}, [openTab,
|
|
87
|
+
}, [openTab, previewData]);
|
|
80
88
|
|
|
81
89
|
// Refs for cell renderers — avoid column memo rebuild on every state change
|
|
82
90
|
const editingRef = useRef(editingCell);
|
|
@@ -196,7 +204,7 @@ export function DataGrid({
|
|
|
196
204
|
if (!el) return;
|
|
197
205
|
const handler = (e: KeyboardEvent) => {
|
|
198
206
|
// Escape closes column search from anywhere
|
|
199
|
-
if (e.key === "Escape") { setColSearchOpen(false); return; }
|
|
207
|
+
if (e.key === "Escape") { setColSearchOpen(false); setPreviewData(null); return; }
|
|
200
208
|
|
|
201
209
|
// Skip if focus is in an input/textarea
|
|
202
210
|
const tag = (e.target as HTMLElement)?.tagName;
|
|
@@ -433,7 +441,7 @@ export function DataGrid({
|
|
|
433
441
|
)}
|
|
434
442
|
|
|
435
443
|
{/* Table */}
|
|
436
|
-
<div ref={scrollRef} className="flex-1 overflow-auto relative">
|
|
444
|
+
<div ref={scrollRef} className="flex-1 overflow-auto relative min-h-0">
|
|
437
445
|
{loading && (
|
|
438
446
|
<div className="absolute inset-0 z-30 flex items-center justify-center bg-background/60">
|
|
439
447
|
<Loader2 className="size-5 animate-spin text-primary" />
|
|
@@ -540,6 +548,11 @@ export function DataGrid({
|
|
|
540
548
|
</table>
|
|
541
549
|
</div>
|
|
542
550
|
|
|
551
|
+
{/* Inline preview panel */}
|
|
552
|
+
{previewData && (
|
|
553
|
+
<DataPreviewPanel data={previewData} onClose={() => setPreviewData(null)} onOpenInTab={openPreviewInTab} />
|
|
554
|
+
)}
|
|
555
|
+
|
|
543
556
|
{/* Footer: row count + pagination */}
|
|
544
557
|
<div className="flex items-center justify-between px-3 py-1.5 border-t border-border bg-background shrink-0 text-xs text-muted-foreground">
|
|
545
558
|
<span>{tableData.total.toLocaleString()} rows</span>
|
|
@@ -596,6 +609,93 @@ function detectLang(text: string): string {
|
|
|
596
609
|
return "plaintext";
|
|
597
610
|
}
|
|
598
611
|
|
|
612
|
+
/** Inline preview panel for cell/row content with Monaco editor */
|
|
613
|
+
function DataPreviewPanel({ data, onClose, onOpenInTab }: {
|
|
614
|
+
data: { title: string; content: string; language: string };
|
|
615
|
+
onClose: () => void;
|
|
616
|
+
onOpenInTab: () => void;
|
|
617
|
+
}) {
|
|
618
|
+
const monacoTheme = useMonacoTheme();
|
|
619
|
+
const [wordWrap, setWordWrap] = useState(true);
|
|
620
|
+
const [displayContent, setDisplayContent] = useState(data.content);
|
|
621
|
+
const [beautified, setBeautified] = useState(false);
|
|
622
|
+
const canBeautify = data.language === "json" || data.language === "xml";
|
|
623
|
+
|
|
624
|
+
// Reset state when data changes
|
|
625
|
+
const prevKey = useRef(data.title);
|
|
626
|
+
if (prevKey.current !== data.title) {
|
|
627
|
+
prevKey.current = data.title;
|
|
628
|
+
setDisplayContent(data.content);
|
|
629
|
+
setBeautified(false);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const toggleBeautify = useCallback(() => {
|
|
633
|
+
if (beautified) {
|
|
634
|
+
setDisplayContent(data.content);
|
|
635
|
+
setBeautified(false);
|
|
636
|
+
} else {
|
|
637
|
+
if (data.language === "json") {
|
|
638
|
+
try { setDisplayContent(JSON.stringify(JSON.parse(data.content.trim()), null, 2)); setBeautified(true); } catch { /* invalid */ }
|
|
639
|
+
} else if (data.language === "xml") {
|
|
640
|
+
// Simple XML indent
|
|
641
|
+
let depth = 0;
|
|
642
|
+
const formatted = data.content.trim().replace(/>\s*</g, ">\n<").split("\n").map((line) => {
|
|
643
|
+
const trimmed = line.trim();
|
|
644
|
+
if (trimmed.startsWith("</")) depth = Math.max(0, depth - 1);
|
|
645
|
+
const indented = " ".repeat(depth) + trimmed;
|
|
646
|
+
if (trimmed.startsWith("<") && !trimmed.startsWith("</") && !trimmed.endsWith("/>") && !trimmed.startsWith("<?")) depth++;
|
|
647
|
+
return indented;
|
|
648
|
+
}).join("\n");
|
|
649
|
+
setDisplayContent(formatted);
|
|
650
|
+
setBeautified(true);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}, [beautified, data.content, data.language]);
|
|
654
|
+
|
|
655
|
+
return (
|
|
656
|
+
<div className="shrink-0 border-t border-border flex flex-col" style={{ height: "40%" }}>
|
|
657
|
+
<div className="flex items-center gap-1 px-2 py-1 bg-muted/50 border-b border-border shrink-0">
|
|
658
|
+
<Eye className="size-3 text-muted-foreground" />
|
|
659
|
+
<span className="text-xs font-medium text-foreground truncate flex-1">{data.title}</span>
|
|
660
|
+
{canBeautify && (
|
|
661
|
+
<button type="button" onClick={toggleBeautify} title={beautified ? "Raw" : "Beautify"}
|
|
662
|
+
className={`p-0.5 rounded transition-colors ${beautified ? "text-primary" : "text-muted-foreground hover:text-foreground"}`}>
|
|
663
|
+
<Sparkles className="size-3" />
|
|
664
|
+
</button>
|
|
665
|
+
)}
|
|
666
|
+
<button type="button" onClick={() => setWordWrap(!wordWrap)} title={wordWrap ? "No wrap" : "Word wrap"}
|
|
667
|
+
className={`p-0.5 rounded transition-colors ${wordWrap ? "text-primary" : "text-muted-foreground hover:text-foreground"}`}>
|
|
668
|
+
<WrapText className="size-3" />
|
|
669
|
+
</button>
|
|
670
|
+
<button type="button" onClick={onOpenInTab} title="Open in new tab"
|
|
671
|
+
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors">
|
|
672
|
+
<ExternalLink className="size-3" />
|
|
673
|
+
<span className="hidden sm:inline">Open in Tab</span>
|
|
674
|
+
</button>
|
|
675
|
+
<button type="button" onClick={onClose} title="Close preview (Esc)"
|
|
676
|
+
className="p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors">
|
|
677
|
+
<X className="size-3" />
|
|
678
|
+
</button>
|
|
679
|
+
</div>
|
|
680
|
+
<div className="flex-1 min-h-0">
|
|
681
|
+
<Editor
|
|
682
|
+
height="100%"
|
|
683
|
+
language={data.language === "plaintext" ? undefined : data.language}
|
|
684
|
+
value={displayContent}
|
|
685
|
+
theme={monacoTheme}
|
|
686
|
+
options={{
|
|
687
|
+
readOnly: true, minimap: { enabled: false }, scrollBeyondLastLine: false,
|
|
688
|
+
wordWrap: wordWrap ? "on" : "off", lineNumbers: "on", fontSize: 12,
|
|
689
|
+
folding: true, bracketPairColorization: { enabled: true },
|
|
690
|
+
domReadOnly: true, contextmenu: false, overviewRulerLanes: 0,
|
|
691
|
+
}}
|
|
692
|
+
loading={<Loader2 className="size-4 animate-spin text-muted-foreground" />}
|
|
693
|
+
/>
|
|
694
|
+
</div>
|
|
695
|
+
</div>
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
599
699
|
/** Column search dropdown — owns query/index state internally to avoid re-rendering DataGrid */
|
|
600
700
|
function ColumnSearchDropdown({ columns, onSelect, onClose }: {
|
|
601
701
|
columns: string[]; onSelect: (col: string) => void; onClose: () => void;
|
|
@@ -651,7 +751,7 @@ const DataRow = memo(function DataRow({ row, rowIdx, columns, selected, onToggle
|
|
|
651
751
|
onSetEditValue: (v: string) => void;
|
|
652
752
|
showDelete: boolean; confirmingDelete: boolean;
|
|
653
753
|
onDelete: (i: number) => void; onConfirmDelete: (i: number | null) => void;
|
|
654
|
-
onViewCell: (cell: { col: string; value: string }) => void;
|
|
754
|
+
onViewCell: (cell: { col: string; value: string; pkVal: string }) => void;
|
|
655
755
|
onViewRow: (row: Record<string, unknown>) => void;
|
|
656
756
|
pinned: boolean; onTogglePin: (i: number) => void;
|
|
657
757
|
pinnedCols: Set<string>; pinnedColOffsets: Map<string, number>;
|
|
@@ -702,7 +802,7 @@ const DataRow = memo(function DataRow({ row, rowIdx, columns, selected, onToggle
|
|
|
702
802
|
</span>
|
|
703
803
|
{showEye && (
|
|
704
804
|
<button type="button" title="View full content"
|
|
705
|
-
onClick={() => onViewCell({ col, value: formatCellValue(val) })}
|
|
805
|
+
onClick={() => onViewCell({ col, value: formatCellValue(val), pkVal: pkCol ? String(row[pkCol] ?? rowIdx) : String(rowIdx) })}
|
|
706
806
|
className="shrink-0 p-0.5 rounded text-muted-foreground/50 hover:text-foreground transition-colors">
|
|
707
807
|
<Eye className="size-3" />
|
|
708
808
|
</button>
|
|
@@ -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);
|
|
@@ -273,9 +299,9 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
273
299
|
return () => window.removeEventListener("file:changed", handler);
|
|
274
300
|
}, [filePath, projectName, isExternalFile, inlineContent, isUntitled]);
|
|
275
301
|
|
|
276
|
-
// Update tab title unsaved indicator
|
|
302
|
+
// Update tab title unsaved indicator (skip for inline content — title set by caller)
|
|
277
303
|
useEffect(() => {
|
|
278
|
-
if (!ownTab) return;
|
|
304
|
+
if (!ownTab || inlineContent != null) return;
|
|
279
305
|
const baseName = isUntitled
|
|
280
306
|
? `Untitled-${metadata?.untitledNumber ?? 1}`
|
|
281
307
|
: (filePath ? basename(filePath) : "Untitled");
|
|
@@ -502,7 +528,11 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
502
528
|
) : null;
|
|
503
529
|
|
|
504
530
|
return (
|
|
505
|
-
<div
|
|
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
|
+
}
|