@hienlh/ppm 0.13.56 → 0.13.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/bun.lock +2129 -0
  5. package/bunfig.toml +2 -0
  6. package/dist/web/assets/{audio-preview-LCxWo-25.js → audio-preview-B4ZHsV9k.js} +1 -1
  7. package/dist/web/assets/{chat-tab-3GUmdAJN.js → chat-tab-DJzZ5i7L.js} +3 -3
  8. package/dist/web/assets/{code-editor-Cd7NZ0VX.js → code-editor-BsCmjMzu.js} +2 -2
  9. package/dist/web/assets/{conflict-editor-No7IVPRN.js → conflict-editor-CXWlXO9Z.js} +1 -1
  10. package/dist/web/assets/{database-viewer-zeQXxkg-.js → database-viewer-VRJVJPQL.js} +1 -1
  11. package/dist/web/assets/{diff-viewer-DmSkyBaT.js → diff-viewer-KulvDlWa.js} +1 -1
  12. package/dist/web/assets/{extension-webview-DCwz2Wso.js → extension-webview-BoHzYEwh.js} +1 -1
  13. package/dist/web/assets/{git-log-panel-Bk7Eh-Bn.js → git-log-panel-95S8tWSL.js} +1 -1
  14. package/dist/web/assets/{glide-data-grid-ha8hjunf.js → glide-data-grid-D7NyiuHK.js} +1 -1
  15. package/dist/web/assets/{image-preview-C1VLHLdJ.js → image-preview-K9Z3rVNA.js} +1 -1
  16. package/dist/web/assets/{index-CP6EIXkh.js → index-heahuZzl.js} +3 -3
  17. package/dist/web/assets/keybindings-store-DWD9jyRg.js +1 -0
  18. package/dist/web/assets/{markdown-renderer-C7ZoGWso.js → markdown-renderer-D2zxqOkH.js} +1 -1
  19. package/dist/web/assets/notification-store-DcY2JHZt.js +1 -0
  20. package/dist/web/assets/pdf-preview-BukxhJ6o.js +1 -0
  21. package/dist/web/assets/{port-forwarding-tab-BMb8neu0.js → port-forwarding-tab-Cq1866z4.js} +1 -1
  22. package/dist/web/assets/{postgres-viewer-DkM6ndux.js → postgres-viewer-JQEY1K99.js} +1 -1
  23. package/dist/web/assets/{settings-tab-WdL5pYxG.js → settings-tab-CMZVAcoh.js} +1 -1
  24. package/dist/web/assets/{sql-query-editor-CNwOnFgF.js → sql-query-editor-BAsdaqSY.js} +1 -1
  25. package/dist/web/assets/{sqlite-viewer-FFlsRPWf.js → sqlite-viewer-BN3gOamm.js} +1 -1
  26. package/dist/web/assets/{system-monitor-tab-CefzxLVi.js → system-monitor-tab-BI7MfACw.js} +1 -1
  27. package/dist/web/assets/{terminal-tab-C4MNU5S3.js → terminal-tab-ScnvZlQ9.js} +1 -1
  28. package/dist/web/assets/use-blob-url-DCUIEzjB.js +1 -0
  29. package/dist/web/assets/{video-preview-izCy3xj7.js → video-preview-CHP7HPJ1.js} +1 -1
  30. package/dist/web/index.html +1 -1
  31. package/dist/web/sw.js +1 -1
  32. package/package.json +1 -1
  33. package/packages/ext-git-graph/src/webview-html.ts +17 -11
  34. package/src/index.ts +0 -0
  35. package/src/web/components/editor/pdf-preview.tsx +36 -3
  36. package/src/web/components/editor/use-blob-url.ts +18 -9
  37. package/dist/web/assets/keybindings-store-Rf9YKWAp.js +0 -1
  38. package/dist/web/assets/notification-store-C3tg-ZXm.js +0 -1
  39. package/dist/web/assets/pdf-preview-BdmR2RsT.js +0 -1
  40. package/dist/web/assets/use-blob-url-CI2h4qu0.js +0 -1
@@ -1,9 +1,42 @@
1
- import { useCallback } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { Loader2, FileWarning, ExternalLink } from "lucide-react";
3
3
  import { useBlobUrl } from "./use-blob-url";
4
4
 
5
5
  export function PdfPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
6
- const { blobUrl, error } = useBlobUrl(filePath, projectName, "application/pdf");
6
+ const [refreshKey, setRefreshKey] = useState(0);
7
+ const iframeRef = useRef<HTMLIFrameElement>(null);
8
+ const scrollYRef = useRef(0);
9
+
10
+ const { blobUrl, error } = useBlobUrl(filePath, projectName, "application/pdf", refreshKey);
11
+
12
+ // Auto-reload: listen for file:changed WS events
13
+ useEffect(() => {
14
+ const handler = (e: Event) => {
15
+ const detail = (e as CustomEvent).detail;
16
+ if (detail.projectName !== projectName || detail.path !== filePath) return;
17
+ // Save scroll position before re-fetch (best-effort for browser PDF viewer)
18
+ try {
19
+ const win = iframeRef.current?.contentWindow;
20
+ if (win) scrollYRef.current = win.scrollY || 0;
21
+ } catch { /* cross-origin or plugin restriction */ }
22
+ setRefreshKey((k) => k + 1);
23
+ };
24
+ window.addEventListener("file:changed", handler);
25
+ return () => window.removeEventListener("file:changed", handler);
26
+ }, [filePath, projectName]);
27
+
28
+ // Restore scroll after new blob loads
29
+ useEffect(() => {
30
+ if (!blobUrl || !iframeRef.current || scrollYRef.current === 0) return;
31
+ const iframe = iframeRef.current;
32
+ const saved = scrollYRef.current;
33
+ const onLoad = () => {
34
+ try { iframe.contentWindow?.scrollTo(0, saved); } catch { /* ignore */ }
35
+ scrollYRef.current = 0;
36
+ };
37
+ iframe.addEventListener("load", onLoad);
38
+ return () => iframe.removeEventListener("load", onLoad);
39
+ }, [blobUrl]);
7
40
 
8
41
  const openInNewTab = useCallback(() => { if (blobUrl) window.open(blobUrl, "_blank"); }, [blobUrl]);
9
42
 
@@ -26,7 +59,7 @@ export function PdfPreview({ filePath, projectName }: { filePath: string; projec
26
59
  <ExternalLink className="size-3" /> Open in new tab
27
60
  </button>
28
61
  </div>
29
- <iframe src={blobUrl} title={filePath} className="flex-1 w-full border-none" />
62
+ <iframe ref={iframeRef} src={blobUrl} title={filePath} className="flex-1 w-full border-none" />
30
63
  </div>
31
64
  );
32
65
  }
@@ -1,18 +1,21 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
  import { projectUrl, getAuthToken } from "@/lib/api-client";
3
3
 
4
4
  /** Shared hook: fetch a project file as a blob URL via /files/raw endpoint.
5
- * Detects absolute paths (external files) and uses /api/fs/raw instead. */
5
+ * Detects absolute paths (external files) and uses /api/fs/raw instead.
6
+ * Pass a changing `refreshKey` to re-fetch without unmounting. */
6
7
  export function useBlobUrl(
7
8
  filePath: string,
8
9
  projectName: string,
9
10
  mimeOverride?: string,
11
+ refreshKey = 0,
10
12
  ) {
11
13
  const [blobUrl, setBlobUrl] = useState<string | null>(null);
12
14
  const [error, setError] = useState(false);
15
+ const urlRef = useRef<string | null>(null);
13
16
 
14
17
  useEffect(() => {
15
- let revoke: string | undefined;
18
+ let cancelled = false;
16
19
  const isExternal = /^(\/|[A-Za-z]:[/\\])/.test(filePath);
17
20
  const url = isExternal
18
21
  ? `/api/fs/raw?path=${encodeURIComponent(filePath)}`
@@ -24,16 +27,22 @@ export function useBlobUrl(
24
27
  return r.blob();
25
28
  })
26
29
  .then((blob) => {
30
+ if (cancelled) return;
27
31
  const final = mimeOverride ? new Blob([blob], { type: mimeOverride }) : blob;
28
32
  const u = URL.createObjectURL(final);
29
- revoke = u;
33
+ // Revoke old URL only after new one is ready (avoids blank flash)
34
+ if (urlRef.current) URL.revokeObjectURL(urlRef.current);
35
+ urlRef.current = u;
30
36
  setBlobUrl(u);
31
37
  })
32
- .catch(() => setError(true));
33
- return () => {
34
- if (revoke) URL.revokeObjectURL(revoke);
35
- };
36
- }, [filePath, projectName, mimeOverride]);
38
+ .catch(() => { if (!cancelled) setError(true); });
39
+ return () => { cancelled = true; };
40
+ }, [filePath, projectName, mimeOverride, refreshKey]);
41
+
42
+ // Revoke on unmount
43
+ useEffect(() => () => {
44
+ if (urlRef.current) URL.revokeObjectURL(urlRef.current);
45
+ }, []);
37
46
 
38
47
  return { blobUrl, error };
39
48
  }
@@ -1 +0,0 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./api-client-DiZgVOok.js";import{D as e}from"./index-CP6EIXkh.js";export{e as useKeybindingsStore};
@@ -1 +0,0 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./api-client-DiZgVOok.js";import{P as e}from"./index-CP6EIXkh.js";export{e as useNotificationStore};
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{t as r}from"./file-exclamation-point-B__2Hrd6.js";import"./api-client-DiZgVOok.js";import{$ as i,ct as a}from"./index-CP6EIXkh.js";import{t as o}from"./use-blob-url-CI2h4qu0.js";var s=e(n(),1),c=t();function l({filePath:e,projectName:t}){let{blobUrl:n,error:l}=o(e,t,`application/pdf`),u=(0,s.useCallback)(()=>{n&&window.open(n,`_blank`)},[n]);return l?(0,c.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,c.jsx)(r,{className:`size-10 text-text-subtle`}),(0,c.jsx)(`p`,{className:`text-sm`,children:`Failed to load PDF.`})]}):n?(0,c.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,c.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,c.jsx)(`span`,{className:`text-xs text-text-secondary truncate`,children:e}),(0,c.jsxs)(`button`,{onClick:u,className:`flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors`,children:[(0,c.jsx)(a,{className:`size-3`}),` Open in new tab`]})]}),(0,c.jsx)(`iframe`,{src:n,title:e,className:`flex-1 w-full border-none`})]}):(0,c.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,c.jsx)(i,{className:`size-5 animate-spin text-text-subtle`})})}export{l as PdfPreview};
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{x as t}from"./vendor-markdown-0Mxgxy0L.js";import{i as n,r}from"./api-client-DiZgVOok.js";var i=e(t(),1);function a(e,t,a){let[o,s]=(0,i.useState)(null),[c,l]=(0,i.useState)(!1);return(0,i.useEffect)(()=>{let i,o=/^(\/|[A-Za-z]:[/\\])/.test(e)?`/api/fs/raw?path=${encodeURIComponent(e)}`:`${n(t)}/files/raw?path=${encodeURIComponent(e)}`,c=r();return fetch(o,{headers:c?{Authorization:`Bearer ${c}`}:{}}).then(e=>{if(!e.ok)throw Error(`Failed`);return e.blob()}).then(e=>{let t=a?new Blob([e],{type:a}):e,n=URL.createObjectURL(t);i=n,s(n)}).catch(()=>l(!0)),()=>{i&&URL.revokeObjectURL(i)}},[e,t,a]),{blobUrl:o,error:c}}export{a as t};