@hienlh/ppm 0.10.4 → 0.11.0
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 +39 -0
- package/dist/web/assets/ai-settings-section-D2vqiydT.js +1 -0
- package/dist/web/assets/{api-settings-CoKe_BdR.js → api-settings-2eTz4SgY.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-BRW4VwMk.js +1 -0
- package/dist/web/assets/chat-tab-CbguR_l0.js +10 -0
- package/dist/web/assets/code-editor-DbZP0Dnj.js +8 -0
- package/dist/web/assets/{conflict-editor-HvxI1A29.js → conflict-editor-BzrH1UpC.js} +3 -3
- package/dist/web/assets/{csv-preview-BizIVMyb.js → csv-preview-D37K2LRd.js} +1 -1
- package/dist/web/assets/{database-viewer-BgCXPc4e.js → database-viewer-CqMOv2Sg.js} +2 -2
- package/dist/web/assets/{diff-viewer-blzXAJHd.js → diff-viewer-B6a2oYYn.js} +1 -1
- package/dist/web/assets/{esm-K1XIK4vc.js → esm-B99v94EE.js} +1 -1
- package/dist/web/assets/{extension-store-3yZYn07W.js → extension-store-CkyOvGbF.js} +1 -1
- package/dist/web/assets/extension-webview-CZr_fvOm.js +3 -0
- package/dist/web/assets/gitGraph-HDMCJU4V-Bt68dqWT.js +1 -0
- package/dist/web/assets/index-C68PuiOm.js +26 -0
- package/dist/web/assets/index-iZHWllzQ.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-ySD5z855.js +1 -0
- package/dist/web/assets/{keybindings-store-D2N-Tq4N.js → keybindings-store-CpP5_miA.js} +1 -1
- package/dist/web/assets/keybindings-store-qfYScgY0.js +1 -0
- package/dist/web/assets/{markdown-renderer-Hcj-59AX.js → markdown-renderer-BhNYbXCp.js} +3 -3
- package/dist/web/assets/packet-RMMSAZCW-CLxaXgIf.js +1 -0
- package/dist/web/assets/pie-UPGHQEXC-C9wPZfkn.js +1 -0
- package/dist/web/assets/port-forwarding-tab-Dw9MUu5a.js +1 -0
- package/dist/web/assets/{postgres-viewer-BEUI1N1X.js → postgres-viewer-YKyNjTLp.js} +3 -3
- package/dist/web/assets/{project-store-Ciq-cK1O.js → project-store-CczGNZyf.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-DxEpzVN_.js +1 -0
- package/dist/web/assets/settings-store-CuYjM0FF.js +2 -0
- package/dist/web/assets/settings-tab-2tdZuQIn.js +1 -0
- package/dist/web/assets/{sql-query-editor-DZ9xskL8.js → sql-query-editor-CVEi0jLM.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-sQs615K6.js → sqlite-viewer-Fx9qDD4-.js} +1 -1
- package/dist/web/assets/{tab-store-DZbiYk7y.js → tab-store-Jvy1eZGM.js} +1 -1
- package/dist/web/assets/terminal-tab-BxljmYb7.js +1 -0
- package/dist/web/assets/treemap-KZPCXAKY-yelcZZqO.js +1 -0
- package/dist/web/assets/{use-monaco-theme-OY18iXNi.js → use-monaco-theme-kjiAwvOp.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-B2SLgECS.js → vendor-mermaid-CylkVm4U.js} +3 -3
- package/dist/web/index.html +13 -13
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +29 -5
- package/docs/project-changelog.md +31 -1
- package/docs/system-architecture.md +106 -1
- package/package.json +1 -1
- package/packages/ext-git-graph/src/extension.ts +11 -4
- package/packages/ext-git-graph/src/webview-html.ts +25 -11
- package/src/cli/commands/jira-cmd.ts +92 -0
- package/src/cli/commands/jira-watcher-cmd.ts +149 -0
- package/src/index.ts +3 -0
- package/src/server/index.ts +19 -0
- package/src/server/routes/files.ts +15 -0
- package/src/server/routes/fs-browse.ts +40 -1
- package/src/server/routes/jira-config-routes.ts +74 -0
- package/src/server/routes/jira-watcher-routes.ts +316 -0
- package/src/server/routes/jira.ts +7 -0
- package/src/server/ws/chat.ts +21 -0
- package/src/services/db.service.ts +65 -1
- package/src/services/extension-host-worker.ts +3 -2
- package/src/services/extension.service.ts +4 -2
- package/src/services/file.service.ts +42 -0
- package/src/services/jira-api-client.ts +216 -0
- package/src/services/jira-config.service.ts +83 -0
- package/src/services/jira-debug-session.service.ts +240 -0
- package/src/services/jira-watcher-db.service.ts +195 -0
- package/src/services/jira-watcher.service.ts +159 -0
- package/src/services/notification.service.ts +6 -0
- package/src/services/supervisor-state.ts +13 -1
- package/src/services/supervisor.ts +4 -3
- package/src/types/jira.ts +128 -0
- package/src/web/app.tsx +15 -12
- package/src/web/components/chat/chat-tab.tsx +32 -1
- package/src/web/components/chat/message-input.tsx +56 -5
- package/src/web/components/explorer/file-tree.tsx +9 -0
- package/src/web/components/extensions/extension-webview.tsx +31 -13
- package/src/web/components/jira/jira-config-form.tsx +109 -0
- package/src/web/components/jira/jira-debug-prompt-dialog.tsx +58 -0
- package/src/web/components/jira/jira-filter-builder.tsx +197 -0
- package/src/web/components/jira/jira-panel.tsx +201 -0
- package/src/web/components/jira/jira-results-panel.tsx +184 -0
- package/src/web/components/jira/jira-settings-section.tsx +58 -0
- package/src/web/components/jira/jira-status-badge.tsx +18 -0
- package/src/web/components/jira/jira-ticket-card.tsx +144 -0
- package/src/web/components/jira/jira-ticket-detail.tsx +153 -0
- package/src/web/components/jira/jira-watcher-form.tsx +154 -0
- package/src/web/components/jira/jira-watcher-list.tsx +98 -0
- package/src/web/components/layout/mobile-drawer.tsx +18 -5
- package/src/web/components/layout/sidebar.tsx +20 -3
- package/src/web/components/settings/settings-tab.tsx +20 -3
- package/src/web/components/shared/markdown-code-block.tsx +5 -3
- package/src/web/components/ui/file-browser-picker.tsx +88 -1
- package/src/web/hooks/use-chat.ts +6 -0
- package/src/web/lib/report-bug.ts +3 -2
- package/src/web/lib/ws-client.ts +14 -6
- package/src/web/stores/jira-store.ts +198 -0
- package/src/web/stores/settings-store.ts +24 -5
- package/src/web/styles/globals.css +7 -0
- package/vite.config.ts +5 -66
- package/bun.lock +0 -2062
- package/bunfig.toml +0 -2
- package/dist/web/assets/ai-settings-section-LMO_cfIW.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-CUZIB1Vq.js +0 -1
- package/dist/web/assets/chat-tab-By7krQ3s.js +0 -10
- package/dist/web/assets/code-editor-BoKL57Co.js +0 -8
- package/dist/web/assets/extension-webview-Dvk_61ON.js +0 -3
- package/dist/web/assets/gitGraph-HDMCJU4V-CtOMUphQ.js +0 -1
- package/dist/web/assets/index-DPnjO2FY.css +0 -2
- package/dist/web/assets/index-EgCQVN13.js +0 -26
- package/dist/web/assets/info-3K5VOQVL-BCrPCWGY.js +0 -1
- package/dist/web/assets/keybindings-store-C7No6mtl.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-D_OqB-zi.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-WUHpLNJz.js +0 -1
- package/dist/web/assets/port-forwarding-tab-CUgwDn_5.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-HQIIecVM.js +0 -1
- package/dist/web/assets/settings-store-B470PCWf.js +0 -2
- package/dist/web/assets/settings-tab-BGvgK51L.js +0 -1
- package/dist/web/assets/square-nsMa3iMk.js +0 -1
- package/dist/web/assets/terminal-tab-CUyHmiHH.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-0wLgUUTz.js +0 -1
- /package/dist/web/assets/{api-client-o_6TmLGC.js → api-client-C3tXCh0r.js} +0 -0
- /package/dist/web/assets/{csv-parser--2WJNgS7.js → csv-parser-BAa56Nnn.js} +0 -0
- /package/dist/web/assets/{dist-im4ynINo.js → dist-On3hz9_g.js} +0 -0
- /package/dist/web/assets/{katex-CKoArbIw.js → katex-Bbu770d9.js} +0 -0
- /package/dist/web/assets/{lib-D_kRA9p6.js → lib-BqkcKGFq.js} +0 -0
- /package/dist/web/assets/{react-GqWghJ-L.js → react-BkWDCPD7.js} +0 -0
- /package/dist/web/assets/{sql-completion-provider-C3cq9j99.js → sql-completion-provider-D3acAhav.js} +0 -0
- /package/dist/web/assets/{table-Dq575bPF.js → table-DbSviOmw.js} +0 -0
- /package/dist/web/assets/{text-wrap-Cn6BNQfq.js → text-wrap-DzvCTq_i.js} +0 -0
- /package/dist/web/assets/{trash-2-CJYoLw7Q.js → trash-2-BgDIBl6f.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-ejLe7-tK.js → vendor-xterm-B9BUAFKA.js} +0 -0
package/dist/web/index.html
CHANGED
|
@@ -39,9 +39,9 @@
|
|
|
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-C68PuiOm.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-CylkVm4U.js">
|
|
45
45
|
<link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
|
|
46
46
|
<link rel="modulepreload" crossorigin href="/assets/vendor-ui-B-T_damt.js">
|
|
47
47
|
<link rel="modulepreload" crossorigin href="/assets/utils-ChWX7pZv.js">
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
<link rel="modulepreload" crossorigin href="/assets/dist-C5IgeqrV.js">
|
|
53
53
|
<link rel="modulepreload" crossorigin href="/assets/plus-51UQ45rf.js">
|
|
54
54
|
<link rel="modulepreload" crossorigin href="/assets/refresh-cw-CSFrDtiu.js">
|
|
55
|
-
<link rel="modulepreload" crossorigin href="/assets/trash-2-
|
|
56
|
-
<link rel="modulepreload" crossorigin href="/assets/api-client-
|
|
57
|
-
<link rel="modulepreload" crossorigin href="/assets/api-settings-
|
|
58
|
-
<link rel="modulepreload" crossorigin href="/assets/ai-settings-section-
|
|
55
|
+
<link rel="modulepreload" crossorigin href="/assets/trash-2-BgDIBl6f.js">
|
|
56
|
+
<link rel="modulepreload" crossorigin href="/assets/api-client-C3tXCh0r.js">
|
|
57
|
+
<link rel="modulepreload" crossorigin href="/assets/api-settings-2eTz4SgY.js">
|
|
58
|
+
<link rel="modulepreload" crossorigin href="/assets/ai-settings-section-D2vqiydT.js">
|
|
59
59
|
<link rel="modulepreload" crossorigin href="/assets/chevron-right-BzAdxJRG.js">
|
|
60
60
|
<link rel="modulepreload" crossorigin href="/assets/database-D4DIhgi-.js">
|
|
61
|
-
<link rel="modulepreload" crossorigin href="/assets/react-
|
|
62
|
-
<link rel="modulepreload" crossorigin href="/assets/extension-store-
|
|
63
|
-
<link rel="modulepreload" crossorigin href="/assets/keybindings-store-
|
|
64
|
-
<link rel="modulepreload" crossorigin href="/assets/tab-store-
|
|
65
|
-
<link rel="modulepreload" crossorigin href="/assets/project-store-
|
|
66
|
-
<link rel="modulepreload" crossorigin href="/assets/settings-store-
|
|
67
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
61
|
+
<link rel="modulepreload" crossorigin href="/assets/react-BkWDCPD7.js">
|
|
62
|
+
<link rel="modulepreload" crossorigin href="/assets/extension-store-CkyOvGbF.js">
|
|
63
|
+
<link rel="modulepreload" crossorigin href="/assets/keybindings-store-CpP5_miA.js">
|
|
64
|
+
<link rel="modulepreload" crossorigin href="/assets/tab-store-Jvy1eZGM.js">
|
|
65
|
+
<link rel="modulepreload" crossorigin href="/assets/project-store-CczGNZyf.js">
|
|
66
|
+
<link rel="modulepreload" crossorigin href="/assets/settings-store-CuYjM0FF.js">
|
|
67
|
+
<link rel="stylesheet" crossorigin href="/assets/index-iZHWllzQ.css">
|
|
68
68
|
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
69
69
|
<body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
|
|
70
70
|
<div id="root"></div>
|
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"ee22c849183a40151c125f9193676212","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.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":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/vendor-xterm-ejLe7-tK.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/vendor-mermaid-B2SLgECS.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/use-monaco-theme-OY18iXNi.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-0wLgUUTz.js"},{"revision":null,"url":"assets/trash-2-CJYoLw7Q.js"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/terminal-tab-CUyHmiHH.js"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/tab-store-DZbiYk7y.js"},{"revision":null,"url":"assets/square-nsMa3iMk.js"},{"revision":null,"url":"assets/sqlite-viewer-sQs615K6.js"},{"revision":null,"url":"assets/sql-query-editor-DZ9xskL8.js"},{"revision":null,"url":"assets/sql-completion-provider-C3cq9j99.js"},{"revision":null,"url":"assets/settings-tab-BGvgK51L.js"},{"revision":null,"url":"assets/settings-store-B470PCWf.js"},{"revision":null,"url":"assets/scroll-area-DwWF9FpN.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-HQIIecVM.js"},{"revision":null,"url":"assets/project-store-Ciq-cK1O.js"},{"revision":null,"url":"assets/postgres-viewer-BEUI1N1X.js"},{"revision":null,"url":"assets/port-forwarding-tab-CUgwDn_5.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-WUHpLNJz.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-D_OqB-zi.js"},{"revision":null,"url":"assets/markdown-renderer-Hcj-59AX.js"},{"revision":null,"url":"assets/lib-D_kRA9p6.js"},{"revision":null,"url":"assets/keybindings-store-D2N-Tq4N.js"},{"revision":null,"url":"assets/keybindings-store-C7No6mtl.js"},{"revision":null,"url":"assets/katex-CKoArbIw.js"},{"revision":null,"url":"assets/input-CHRMley8.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BCrPCWGY.js"},{"revision":null,"url":"assets/index-EgCQVN13.js"},{"revision":null,"url":"assets/index-DPnjO2FY.css"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CtOMUphQ.js"},{"revision":null,"url":"assets/extension-webview-Dvk_61ON.js"},{"revision":null,"url":"assets/extension-store-3yZYn07W.js"},{"revision":null,"url":"assets/esm-K1XIK4vc.js"},{"revision":null,"url":"assets/dist-im4ynINo.js"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/diff-viewer-blzXAJHd.js"},{"revision":null,"url":"assets/database-viewer-BgCXPc4e.js"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/csv-preview-BizIVMyb.js"},{"revision":null,"url":"assets/csv-parser--2WJNgS7.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/conflict-editor-HvxI1A29.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/code-editor-BoKL57Co.js"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/chat-tab-By7krQ3s.js"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CUZIB1Vq.js"},{"revision":null,"url":"assets/api-settings-CoKe_BdR.js"},{"revision":null,"url":"assets/api-client-o_6TmLGC.js"},{"revision":null,"url":"assets/ai-settings-section-LMO_cfIW.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"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":"9970d69faf52d07dd0be5497439be2dd","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/lib-BqkcKGFq.js"},{"revision":null,"url":"assets/input-CHRMley8.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/tab-store-Jvy1eZGM.js"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-Bt68dqWT.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/database-viewer-CqMOv2Sg.js"},{"revision":null,"url":"assets/port-forwarding-tab-Dw9MUu5a.js"},{"revision":null,"url":"assets/ai-settings-section-D2vqiydT.js"},{"revision":null,"url":"assets/index-C68PuiOm.js"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/code-editor-DbZP0Dnj.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/settings-store-CuYjM0FF.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-C9wPZfkn.js"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/sql-query-editor-CVEi0jLM.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-CLxaXgIf.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/trash-2-BgDIBl6f.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/settings-tab-2tdZuQIn.js"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/terminal-tab-BxljmYb7.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/esm-B99v94EE.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/api-settings-2eTz4SgY.js"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DxEpzVN_.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/vendor-xterm-B9BUAFKA.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/api-client-C3tXCh0r.js"},{"revision":null,"url":"assets/markdown-renderer-BhNYbXCp.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/use-monaco-theme-kjiAwvOp.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/extension-store-CkyOvGbF.js"},{"revision":null,"url":"assets/diff-viewer-B6a2oYYn.js"},{"revision":null,"url":"assets/info-3K5VOQVL-ySD5z855.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/sql-completion-provider-D3acAhav.js"},{"revision":null,"url":"assets/react-BkWDCPD7.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/extension-webview-CZr_fvOm.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/architecture-PBZL5I3N-BRW4VwMk.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-yelcZZqO.js"},{"revision":null,"url":"assets/sqlite-viewer-Fx9qDD4-.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/csv-parser-BAa56Nnn.js"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/project-store-CczGNZyf.js"},{"revision":null,"url":"assets/postgres-viewer-YKyNjTLp.js"},{"revision":null,"url":"assets/katex-Bbu770d9.js"},{"revision":null,"url":"assets/keybindings-store-qfYScgY0.js"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/text-wrap-DzvCTq_i.js"},{"revision":null,"url":"assets/conflict-editor-BzrH1UpC.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/scroll-area-DwWF9FpN.js"},{"revision":null,"url":"assets/index-iZHWllzQ.css"},{"revision":null,"url":"assets/chat-tab-CbguR_l0.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/keybindings-store-CpP5_miA.js"},{"revision":null,"url":"assets/csv-preview-D37K2LRd.js"},{"revision":null,"url":"assets/vendor-mermaid-CylkVm4U.js"},{"revision":null,"url":"assets/table-DbSviOmw.js"},{"revision":null,"url":"assets/dist-On3hz9_g.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
package/docs/codebase-summary.md
CHANGED
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
```
|
|
18
18
|
src/
|
|
19
19
|
├── cli/
|
|
20
|
-
│ ├── commands/ #
|
|
21
|
-
│ │
|
|
20
|
+
│ ├── commands/ # 16 CLI command groups (start, stop, init, config, chat, db, git, ext, jira, etc.)
|
|
21
|
+
│ │ ├── ext-cmd.ts # Extension CLI (install/remove/list/enable/disable/dev)
|
|
22
|
+
│ │ ├── jira-cmd.ts # Jira config commands (set, show, remove, test)
|
|
23
|
+
│ │ └── jira-watcher-cmd.ts # Jira watcher commands (add, list, enable, disable, remove, test, pull)
|
|
22
24
|
│ └── utils/
|
|
23
25
|
│ └── project-resolver.ts # Resolve project name -> path
|
|
24
26
|
├── server/
|
|
@@ -36,6 +38,9 @@ src/
|
|
|
36
38
|
│ │ ├── mcp.ts # MCP server CRUD + import (GET, POST, PUT, DELETE)
|
|
37
39
|
│ │ ├── extensions.ts # Extension install/remove/list/enable/disable, contributions
|
|
38
40
|
│ │ ├── upgrade.ts # Version checking, upgrade
|
|
41
|
+
│ │ ├── jira.ts # Jira routes barrel (config, watchers)
|
|
42
|
+
│ │ ├── jira-config-routes.ts # Jira config API (CRUD, test connection)
|
|
43
|
+
│ │ ├── jira-watcher-routes.ts # Jira watcher API (CRUD, poll, results, search, metadata)
|
|
39
44
|
│ │ └── static.ts # Serve frontend (dist/web)
|
|
40
45
|
│ ├── helpers/
|
|
41
46
|
│ │ └── resolve-project.ts # Resolve project from request params
|
|
@@ -92,7 +97,11 @@ src/
|
|
|
92
97
|
│ │ ├── sqlite-adapter.ts
|
|
93
98
|
│ │ ├── postgres-adapter.ts
|
|
94
99
|
│ │ └── readonly-check.ts # CTE-safe readonly validation
|
|
95
|
-
│
|
|
100
|
+
│ ├── jira-api-client.ts # Jira Cloud REST API v3 (search, getIssue, transitions)
|
|
101
|
+
│ ├── jira-config.service.ts # Jira config CRUD, AES-256 token encryption
|
|
102
|
+
│ ├── jira-watcher-db.service.ts # Watchers + results table queries
|
|
103
|
+
│ ├── jira-watcher.service.ts # Poll orchestrator, timer management, result sync
|
|
104
|
+
│ └── ... (16+ other services)
|
|
96
105
|
├── lib/
|
|
97
106
|
│ ├── account-crypto.ts # AES-256 encryption
|
|
98
107
|
│ └── network-utils.ts
|
|
@@ -105,11 +114,13 @@ src/
|
|
|
105
114
|
│ ├── mcp.ts # McpServerConfig, McpTransportType, validation
|
|
106
115
|
│ ├── extension.ts # ExtensionManifest, ExtensionInfo, RpcMessage, ExtensionContext
|
|
107
116
|
│ ├── ppmbot.ts # BotTask, TelegramUpdate, PPMBotCommand (coordinator types)
|
|
117
|
+
│ ├── jira.ts # JiraConfig, JiraWatcher, JiraWatchResult, JiraIssue, JiraCredentials
|
|
108
118
|
│ ├── project.ts
|
|
109
119
|
│ └── terminal.ts
|
|
110
120
|
└── web/ # React frontend (Vite + React 18)
|
|
111
121
|
├── app.tsx # Root component
|
|
112
|
-
├── stores/ # Zustand state (
|
|
122
|
+
├── stores/ # Zustand state (7 stores)
|
|
123
|
+
│ └── jira-store.ts # ADDED: Jira config, watchers, results, filters state
|
|
113
124
|
├── hooks/ # Custom hooks (9 hooks)
|
|
114
125
|
├── components/
|
|
115
126
|
│ ├── chat/
|
|
@@ -122,7 +133,15 @@ src/
|
|
|
122
133
|
│ ├── settings/
|
|
123
134
|
│ │ ├── ai-settings-section.tsx # UPDATED: Per-provider tabs, dynamic model dropdowns
|
|
124
135
|
│ │ ├── mcp-settings-section.tsx # ADDED: MCP servers tab (list, add, edit, delete)
|
|
125
|
-
│ │
|
|
136
|
+
│ │ ├── mcp-server-dialog.tsx # ADDED: Add/Edit MCP server dialog
|
|
137
|
+
│ │ ├── settings-tab.tsx # UPDATED: Added Jira Watcher tab
|
|
138
|
+
│ │ └── jira/ # ADDED: Jira Watcher components
|
|
139
|
+
│ │ ├── jira-settings-tab.tsx
|
|
140
|
+
│ │ ├── jira-config-form.tsx
|
|
141
|
+
│ │ ├── jira-filter-builder.tsx
|
|
142
|
+
│ │ ├── jira-watcher-list.tsx
|
|
143
|
+
│ │ ├── jira-results-panel.tsx
|
|
144
|
+
│ │ └── jira-ticket-detail.tsx
|
|
126
145
|
│ ├── database/
|
|
127
146
|
│ ├── editor/
|
|
128
147
|
│ ├── explorer/
|
|
@@ -222,11 +241,16 @@ src/
|
|
|
222
241
|
│ ├── test-setup.ts # Disable auth for tests
|
|
223
242
|
│ ├── unit/
|
|
224
243
|
│ │ ├── providers/ # Mock provider, SDK tests
|
|
244
|
+
│ │ ├── jira-watcher-poll.test.ts # ADDED: Jira watcher polling, rate limit backoff
|
|
225
245
|
│ │ └── services/ # Chat, config, db, session-log, push-notification tests
|
|
226
246
|
│ └── integration/
|
|
227
247
|
│ ├── claude-agent-sdk-integration.test.ts
|
|
228
248
|
│ ├── sqlite-migration.test.ts # SQLite migration validation
|
|
249
|
+
│ ├── jira-config.test.ts # ADDED: Jira config CRUD, token encryption
|
|
250
|
+
│ ├── jira-migration.test.ts # ADDED: Schema v18 migration validation
|
|
251
|
+
│ ├── jira-watcher-db.test.ts # ADDED: Watcher + result queries
|
|
229
252
|
│ ├── api/ # Chat route tests
|
|
253
|
+
│ ├── api/jira-routes.test.ts # ADDED: Jira API endpoints
|
|
230
254
|
│ └── ws/ # WebSocket tests
|
|
231
255
|
├── scripts/
|
|
232
256
|
│ ├── build.ts # Build CLI binary (bun build --compile)
|
|
@@ -6,9 +6,39 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## [Unreleased] — Frontend Memory Optimization + Git-Graph Stash Management, Rebase, Conflict Resolution + Worktree CRUD
|
|
9
|
+
## [Unreleased] — Jira Debug Session Redesign + Frontend Memory Optimization + Git-Graph Stash Management, Rebase, Conflict Resolution + Worktree CRUD
|
|
10
10
|
|
|
11
11
|
### Added
|
|
12
|
+
- **Jira Debug Session Redesign** — Direct Claude session debug replacing bot_task flow with concurrency queue
|
|
13
|
+
- Replaced bot_task-based debug with direct `chatService.sendMessage()` calls (simpler, faster)
|
|
14
|
+
- Concurrency queue: max 2 concurrent sessions globally, max 1 per project (prevents resource exhaustion)
|
|
15
|
+
- Manual "Start Debug" button with editable prompt in results panel (override watcher template)
|
|
16
|
+
- Unread tracking: `read_at` column on `jira_watch_results`, unread badge count in UI
|
|
17
|
+
- WS toast notifications on debug completion (`jira:debug_complete` event)
|
|
18
|
+
- Prompt override support for custom debug instructions per result
|
|
19
|
+
- Result status flow: pending → queued → running → done/failed
|
|
20
|
+
- Timeout protection: 10-minute abort-on-timeout with graceful cleanup
|
|
21
|
+
- AI summary capture: last assistant text (max 500 chars) stored in result
|
|
22
|
+
- Database schema v19: added `read_at` (nullable timestamp), `triggered_by` ("auto"|"manual")
|
|
23
|
+
- New service: `JiraDebugSessionService` with queue management + concurrency limits
|
|
24
|
+
- New component: `JiraDebugPromptDialog` for manual prompt override UI
|
|
25
|
+
- API: `POST /api/jira/results/:id/debug` to trigger debug (with optional prompt)
|
|
26
|
+
|
|
27
|
+
- **Jira Watcher Auto-Debug (v0.9.86+)** — Poll Jira Cloud per-project, auto-debug matched tickets
|
|
28
|
+
- Jira Cloud REST API integration (search, get issue, transitions, metadata discovery)
|
|
29
|
+
- Per-project config (base URL, email, AES-256 token encryption)
|
|
30
|
+
- JQL-based watchers with two modes: debug (queue session) and notify-only (Telegram notification)
|
|
31
|
+
- Configurable poll intervals (30s–60m per watcher, interval clamping)
|
|
32
|
+
- Rate limit aware (tracks Jira API quota, auto-backoff 429 responses)
|
|
33
|
+
- Result tracking (pending/queued/running/done/failed status, AI summary persistence)
|
|
34
|
+
- Prompt templating ({issue_key}, {summary}, {description}, {status}, {priority} substitution)
|
|
35
|
+
- Soft deletes (preserve result history, don't lose tracking)
|
|
36
|
+
- Frontend filter builder UI (projects, issue types, priorities, statuses, custom JQL)
|
|
37
|
+
- CLI commands: `ppm jira config {set,show,remove,test}`, `ppm jira watch {add,list,enable,disable,remove,test,pull}`
|
|
38
|
+
- API routes: /api/jira/config/*, /api/jira/{watchers,results,search,ticket,metadata}
|
|
39
|
+
- 3 SQLite tables (v18): jira_config, jira_watchers, jira_watch_results
|
|
40
|
+
- 44 tests (integration + unit, JQL builder, result sync, credential encryption, rate limiting)
|
|
41
|
+
|
|
12
42
|
- **Frontend Memory & Performance Optimizations** — Reduce re-renders, lazy-load heavy components, code splitting
|
|
13
43
|
- **useShallow pattern:** All destructured Zustand store calls now use `useShallow` (36 usage sites) to prevent re-renders on object mutations
|
|
14
44
|
- **React.memo wrapping:** 10 heavy components memoized (CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode)
|
|
@@ -203,6 +203,10 @@ Tab IDs are deterministic: `{type}:{identifier}` (e.g., `editor:src/index.ts`, `
|
|
|
203
203
|
| **executeDelegation()** | Task execution in isolated session, result capture | (async function, manages ChatService + result storage) |
|
|
204
204
|
| **PPMBotFormatterService** | Markdown → Telegram HTML + chunking | formatMarkdown, chunkMessage |
|
|
205
205
|
| **PPMBotStreamerService** | ChatEvent → progressive Telegram edits | streamMessageEdits |
|
|
206
|
+
| **JiraConfigService** | Jira config CRUD, token encryption | getConfigByProjectId, upsertConfig, deleteConfig, getDecryptedCredentials |
|
|
207
|
+
| **JiraWatcherDbService** | Jira watchers + results queries | getAllEnabledWatchers, insertResult, updateResultStatus, getWatcherById |
|
|
208
|
+
| **JiraApiClient** | Jira Cloud REST API v3 integration | searchIssues, getIssue, updateIssue, testConnection, getProjects, getFieldOptions |
|
|
209
|
+
| **JiraWatcherService** | Poll orchestrator, timer management | startAll, startWatcher, pollWatcher, syncResultStatuses |
|
|
206
210
|
| **ClawBotService** | LEGACY Telegram bot (deprecated v0.9.11) | (direct-chat model, replaced by coordinator) |
|
|
207
211
|
| **ClawBotTelegramService** | LEGACY Telegram API | (deprecated v0.9.11) |
|
|
208
212
|
| **ClawBotSessionService** | LEGACY chatID mapping | (deprecated v0.9.11) |
|
|
@@ -441,6 +445,107 @@ Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
|
|
|
441
445
|
|
|
442
446
|
---
|
|
443
447
|
|
|
448
|
+
### Jira Watcher Auto-Debug Service
|
|
449
|
+
**Component:** Jira Cloud REST API poller + direct Claude debug session orchestrator
|
|
450
|
+
|
|
451
|
+
**Responsibilities:**
|
|
452
|
+
- Poll Jira Cloud per-project on configurable interval (30s–60m)
|
|
453
|
+
- Match issues via JQL filters (status, project key, priority, etc.)
|
|
454
|
+
- Auto-queue or manually trigger direct Claude debug sessions (no bot_task middleman)
|
|
455
|
+
- Manage concurrency: max 2 concurrent, max 1 per project
|
|
456
|
+
- Track results (pending/queued/running/done/failed) with unread status
|
|
457
|
+
- Notify via WS toast + Telegram when analysis completes
|
|
458
|
+
- Rate-limit aware (tracks Jira API quota, auto-backoff 429 responses)
|
|
459
|
+
|
|
460
|
+
**Architecture:**
|
|
461
|
+
```
|
|
462
|
+
Jira Cloud API ← JiraWatcherService (poller, 30s–60m intervals per watcher)
|
|
463
|
+
├─ searchIssues(jql) → issue list
|
|
464
|
+
├─ insertResult() → SQLite jira_watch_results
|
|
465
|
+
└─ jiraDebugService.enqueue() → concurrency queue
|
|
466
|
+
|
|
467
|
+
JiraDebugSessionService (concurrency queue processor)
|
|
468
|
+
├─ enqueue(resultId, promptOverride?) → validate + queue
|
|
469
|
+
├─ processQueue() — respects MAX_CONCURRENT=2, MAX_PER_PROJECT=1
|
|
470
|
+
├─ runDebugSession()
|
|
471
|
+
│ ├─ chatService.createSession(projectPath) — new isolated session
|
|
472
|
+
│ ├─ chatService.sendMessage(prompt) — send with bypassPermissions
|
|
473
|
+
│ ├─ capture lastAssistantText (max 500 chars)
|
|
474
|
+
│ └─ updateResultStatus() + notificationService.broadcastWs("jira:debug_complete")
|
|
475
|
+
└─ cancelDebug(resultId) — abort running session
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Services (src/services/):**
|
|
479
|
+
- **JiraConfigService** — Config CRUD, AES-256 token encryption/decryption, per-project setup
|
|
480
|
+
- **JiraWatcherDbService** — Watchers + results table queries, enabled/disabled toggle, last polled tracking
|
|
481
|
+
- **JiraApiClient** — Jira Cloud REST v3 (search, getIssue, transitions, test connection), rate limit state, backoff logic
|
|
482
|
+
- **JiraWatcherService** — Main poller, timer management (startAll, startWatcher, stopWatcher, pollWatcher), prompt templating, session enqueueing
|
|
483
|
+
- **JiraDebugSessionService** — Concurrency queue, session lifecycle, timeout management, abort handling
|
|
484
|
+
|
|
485
|
+
**Database Schema (v19):**
|
|
486
|
+
- `jira_config` — id, project_id (FK), base_url, email, api_token_encrypted, created_at
|
|
487
|
+
- `jira_watchers` — id, jira_config_id (FK), name, jql, prompt_template, enabled, mode ("debug"|"notify"), interval_ms, last_polled_at, created_at
|
|
488
|
+
- `jira_watch_results` — id, watcher_id, issue_key, issue_summary, issue_updated, session_id (FK chat_sessions.id), status ("pending"|"queued"|"running"|"done"|"failed"), ai_summary, source ("watcher"|"manual"), triggered_by ("auto"|"manual"), read_at (nullable), deleted, created_at
|
|
489
|
+
|
|
490
|
+
**API Routes (src/server/routes/jira*.ts):**
|
|
491
|
+
```
|
|
492
|
+
POST /api/jira/config — Create/update config (baseUrl, email, token)
|
|
493
|
+
GET /api/jira/config — Get config for active project
|
|
494
|
+
DELETE /api/jira/config — Delete config
|
|
495
|
+
POST /api/jira/config/test — Test Jira connection
|
|
496
|
+
GET /api/jira/watchers — List watchers for config
|
|
497
|
+
POST /api/jira/watchers — Create watcher (name, jql, mode, interval)
|
|
498
|
+
PATCH /api/jira/watchers/:id — Update watcher
|
|
499
|
+
DELETE /api/jira/watchers/:id — Delete watcher (soft delete results)
|
|
500
|
+
POST /api/jira/watchers/:id/enable — Enable/disable watcher
|
|
501
|
+
POST /api/jira/watchers/:id/poll — Trigger poll now
|
|
502
|
+
GET /api/jira/results — List results (paginated, filterable)
|
|
503
|
+
POST /api/jira/results/:id/debug — Manually trigger debug for result (with optional prompt override)
|
|
504
|
+
POST /api/jira/results/:id/read — Mark result as read
|
|
505
|
+
DELETE /api/jira/results/:id — Delete result (soft delete)
|
|
506
|
+
GET /api/jira/search — Search Jira (for filter builder UI)
|
|
507
|
+
GET /api/jira/ticket/:key — Get full ticket details
|
|
508
|
+
GET /api/jira/metadata — Fetch projects, issue types, priorities, statuses
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**CLI Commands (src/cli/commands/jira*.ts):**
|
|
512
|
+
```
|
|
513
|
+
ppm jira config set <project> --url <url> --email <email> --token <token>
|
|
514
|
+
ppm jira config show <project>
|
|
515
|
+
ppm jira config remove <project>
|
|
516
|
+
ppm jira config test <project>
|
|
517
|
+
ppm jira watch add <project> <name> --jql <jql> [--mode debug|notify] [--interval 300000]
|
|
518
|
+
ppm jira watch list <project>
|
|
519
|
+
ppm jira watch enable/disable <project> <watcherId>
|
|
520
|
+
ppm jira watch remove <project> <watcherId>
|
|
521
|
+
ppm jira watch test <project> <watcherId>
|
|
522
|
+
ppm jira watch pull <project> <watcherId>
|
|
523
|
+
ppm jira results list <project> [--limit 50]
|
|
524
|
+
ppm jira results delete <project> <resultId>
|
|
525
|
+
ppm jira track <issue-key> — Manually track ticket (insert result, queue debug)
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Frontend (src/web/components/jira/):**
|
|
529
|
+
- **jira-settings-tab.tsx** — Config form, test button, token input
|
|
530
|
+
- **jira-filter-builder.tsx** — JQL builder UI (projects, issue types, priorities, statuses, custom JQL)
|
|
531
|
+
- **jira-watcher-list.tsx** — List watchers, enable/disable, edit, delete, poll now, interval controls
|
|
532
|
+
- **jira-results-panel.tsx** — Results table (issue key, status, summary, AI summary), unread badge, delete, manual debug button
|
|
533
|
+
- **jira-debug-prompt-dialog.tsx** — Modal for prompt override when manually triggering debug
|
|
534
|
+
- **jira-ticket-detail.tsx** — Modal with full ticket, AI analysis, debug status
|
|
535
|
+
- **jira-store.ts** — Zustand (configs, watchers, results, filters, settings, unread count)
|
|
536
|
+
|
|
537
|
+
**Key Design Decisions:**
|
|
538
|
+
1. **Direct Claude sessions** — Replaced bot_task flow with direct `chatService.sendMessage()` (simpler, faster, no task overhead)
|
|
539
|
+
2. **Concurrency queue** — Max 2 concurrent globally, max 1 per project (prevents resource starvation, respects project context)
|
|
540
|
+
3. **Manual debug trigger** — Users can override watcher prompt and manually queue debug for any pending result
|
|
541
|
+
4. **Unread tracking** — `read_at` column marks when user views result, UI shows unread badge count
|
|
542
|
+
5. **Prompt templating** — Support {issue_key}, {summary}, {description}, {status}, {priority} placeholders in watcher templates
|
|
543
|
+
6. **Timeout protection** — 10-minute timeout with AbortController graceful cleanup and error capture
|
|
544
|
+
7. **WS notifications** — `jira:debug_complete` event streamed to UI for instant toast feedback
|
|
545
|
+
8. **Soft deletes** — Results marked deleted=1 (preserve history, don't lose tracking)
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
444
549
|
### Data Access Layer (SQLite + Filesystem + Git)
|
|
445
550
|
**Components:** SQLite via bun:sqlite, direct filesystem access, simple-git wrapper
|
|
446
551
|
|
|
@@ -452,7 +557,7 @@ Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
|
|
|
452
557
|
- Enforce security (no parent directory access)
|
|
453
558
|
|
|
454
559
|
**Key Patterns:**
|
|
455
|
-
- SQLite: WAL mode, foreign keys, lazy init, schema
|
|
560
|
+
- SQLite: WAL mode, foreign keys, lazy init, schema v19 (18 tables: config, connections, accounts, usage_history, session_logs, push_subscriptions, session_map, table_metadata, workspace_state, extension_storage, mcp_servers, clawbot_sessions, clawbot_memories, clawbot_paired_chats, jira_config, jira_watchers, jira_watch_results, bot_tasks)
|
|
456
561
|
- Path validation: `projectPath/relativePath` only, reject `..`
|
|
457
562
|
- Caching: Directory trees cached with TTL
|
|
458
563
|
- Error handling: Descriptive messages (file not found, permission denied)
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ interface VscodeApi {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
let baseUrl = "";
|
|
37
|
+
let authToken = "";
|
|
37
38
|
|
|
38
39
|
// Track active panel state for reuse across project switches
|
|
39
40
|
let activePanel: ReturnType<VscodeApi["window"]["createWebviewPanel"]> | null = null;
|
|
@@ -59,6 +60,7 @@ async function saveSetting(context: ExtensionContext, key: string, value: unknow
|
|
|
59
60
|
|
|
60
61
|
export function activate(context: ExtensionContext, vscode: VscodeApi): void {
|
|
61
62
|
baseUrl = (globalThis as any).__PPM_BASE_URL__ || "";
|
|
63
|
+
authToken = (globalThis as any).__PPM_AUTH_TOKEN__ || "";
|
|
62
64
|
|
|
63
65
|
context.subscriptions.push(
|
|
64
66
|
vscode.commands.registerCommand("git-graph.view", async (...args: unknown[]) => {
|
|
@@ -87,10 +89,15 @@ export function deactivate(): void {
|
|
|
87
89
|
console.log("[ext-git-graph] deactivated");
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
/** Build fetch options with auth header when token is available */
|
|
93
|
+
function authHeaders(): RequestInit {
|
|
94
|
+
return authToken ? { headers: { Authorization: `Bearer ${authToken}` } } : {};
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
/** Resolve project path from PPM API as fallback */
|
|
91
98
|
async function resolveProjectPath(): Promise<string | null> {
|
|
92
99
|
try {
|
|
93
|
-
const res = await fetch(`${baseUrl}/api/projects
|
|
100
|
+
const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
|
|
94
101
|
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
95
102
|
if (!json.ok || !json.data || json.data.length === 0) return null;
|
|
96
103
|
// Single project — safe to auto-select
|
|
@@ -104,7 +111,7 @@ async function resolveProjectPath(): Promise<string | null> {
|
|
|
104
111
|
/** Resolve project name from path via PPM API */
|
|
105
112
|
async function resolveProjectName(projectPath: string): Promise<string> {
|
|
106
113
|
try {
|
|
107
|
-
const res = await fetch(`${baseUrl}/api/projects
|
|
114
|
+
const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
|
|
108
115
|
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
109
116
|
if (json.ok && json.data) {
|
|
110
117
|
const match = json.data.find((p) => p.path === projectPath);
|
|
@@ -311,7 +318,7 @@ function openGitGraph(
|
|
|
311
318
|
case "openWorktree": {
|
|
312
319
|
// Find project matching worktree path and switch to it
|
|
313
320
|
try {
|
|
314
|
-
const res = await fetch(`${baseUrl}/api/projects
|
|
321
|
+
const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
|
|
315
322
|
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
316
323
|
const match = json.data?.find((p) => p.path === msg.path);
|
|
317
324
|
if (match) {
|
|
@@ -326,7 +333,7 @@ function openGitGraph(
|
|
|
326
333
|
if (answer === "Yes, add project") {
|
|
327
334
|
const addRes = await fetch(`${baseUrl}/api/projects`, {
|
|
328
335
|
method: "POST",
|
|
329
|
-
headers: { "Content-Type": "application/json" },
|
|
336
|
+
headers: { "Content-Type": "application/json", ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}) },
|
|
330
337
|
body: JSON.stringify({ path: msg.path, name: dirName }),
|
|
331
338
|
});
|
|
332
339
|
const addJson = await addRes.json() as { ok: boolean; data?: { name: string } };
|
|
@@ -242,7 +242,7 @@ button:active { background: var(--surface); }
|
|
|
242
242
|
|
|
243
243
|
/* Graph container */
|
|
244
244
|
#graph-container { flex: 1; overflow-y: auto; overflow-x: hidden; }
|
|
245
|
-
.commit-row { display: flex; align-items: center; cursor: pointer;
|
|
245
|
+
.commit-row { display: flex; align-items: center; cursor: pointer; height: 24px; padding: 0 6px; font-size: 12px; box-sizing: border-box; overflow: hidden; }
|
|
246
246
|
.commit-row:hover { background: var(--surface-hover); }
|
|
247
247
|
.commit-row.selected { background: var(--selected); }
|
|
248
248
|
.commit-row.header-row { background: var(--surface); cursor: default; font-weight: 600; font-size: 10px; color: var(--subtext); text-transform: uppercase; letter-spacing: 0.5px; position: sticky; top: 0; z-index: 2; border-bottom: 1px solid var(--border); min-height: 22px; }
|
|
@@ -383,11 +383,25 @@ button:active { background: var(--surface); }
|
|
|
383
383
|
.toast-info { background: var(--blue); color: #fff; }
|
|
384
384
|
@keyframes toast-in { from { opacity: 0; transform: translateX(-50%) translateY(10px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
|
|
385
385
|
|
|
386
|
-
/* Touch
|
|
387
|
-
@media (
|
|
388
|
-
.commit-row {
|
|
389
|
-
.ctx-item { padding:
|
|
390
|
-
button { min-width:
|
|
386
|
+
/* Touch devices — compact mobile layout */
|
|
387
|
+
@media (pointer: coarse) {
|
|
388
|
+
.commit-row { height: 32px; }
|
|
389
|
+
.ctx-item { padding: 8px 12px; min-height: 36px; }
|
|
390
|
+
button { min-width: 32px; min-height: 32px; padding: 2px 6px; }
|
|
391
|
+
#app { flex-direction: column; }
|
|
392
|
+
#toolbar { order: 10; border-bottom: none; border-top: 1px solid var(--border); padding: 2px 6px; }
|
|
393
|
+
#toolbar button { font-size: 10px; }
|
|
394
|
+
.branch-trigger { font-size: 10px !important; padding: 2px 6px !important; }
|
|
395
|
+
#graph-container { order: 1; overflow-x: auto; overflow-y: auto; }
|
|
396
|
+
#find-bar { order: 0; }
|
|
397
|
+
#status-bar { order: 9; }
|
|
398
|
+
#commit-list-wrapper { min-width: 700px; }
|
|
399
|
+
#graph-header { min-width: 700px; }
|
|
400
|
+
.commit-row.header-row { min-height: 20px; }
|
|
401
|
+
.detail-panel { order: 8; max-height: 35vh; }
|
|
402
|
+
}
|
|
403
|
+
/* Column hiding on very narrow non-touch containers (e.g. narrow desktop panel) */
|
|
404
|
+
@media (max-width: 500px) and (pointer: fine) {
|
|
391
405
|
.col-author, .col-hash { display: none; }
|
|
392
406
|
.col-date { width: 60px; min-width: 60px; }
|
|
393
407
|
}
|
|
@@ -1318,11 +1332,11 @@ function graphRender(expandIdx) {
|
|
|
1318
1332
|
container.innerHTML = '';
|
|
1319
1333
|
if (gVertices.length === 0) { if (state.graphColWidth === null) document.documentElement.style.setProperty('--graph-col-w', '40px'); return; }
|
|
1320
1334
|
|
|
1321
|
-
//
|
|
1322
|
-
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1335
|
+
// Measure actual row height (may be fractional due to browser zoom)
|
|
1336
|
+
// and use it for SVG grid to prevent cumulative sub-pixel drift
|
|
1337
|
+
const firstRow = document.querySelector('#commit-list .commit-row');
|
|
1338
|
+
const rowH = firstRow ? firstRow.getBoundingClientRect().height : graphConfig.grid.y;
|
|
1339
|
+
const cfg = { ...graphConfig, grid: { ...graphConfig.grid, y: rowH, offsetY: rowH / 2 } };
|
|
1326
1340
|
|
|
1327
1341
|
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
1328
1342
|
const group = document.createElementNS(SVG_NS, 'g');
|