@hienlh/ppm 0.13.57 → 0.13.59

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 (34) hide show
  1. package/CHANGELOG.md +4 -1
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/dist/web/assets/{audio-preview-B4ZHsV9k.js → audio-preview-DPUWgkFM.js} +1 -1
  5. package/dist/web/assets/{chat-tab-DJzZ5i7L.js → chat-tab-jeH2KOyI.js} +3 -3
  6. package/dist/web/assets/{code-editor-BsCmjMzu.js → code-editor-D689bNqg.js} +2 -2
  7. package/dist/web/assets/{conflict-editor-CXWlXO9Z.js → conflict-editor-BBLBwDP3.js} +1 -1
  8. package/dist/web/assets/{database-viewer-VRJVJPQL.js → database-viewer-Dn_XmPuG.js} +1 -1
  9. package/dist/web/assets/{diff-viewer-KulvDlWa.js → diff-viewer-BawweIzs.js} +1 -1
  10. package/dist/web/assets/{extension-webview-BoHzYEwh.js → extension-webview-CnW4FmbT.js} +1 -1
  11. package/dist/web/assets/{git-log-panel-95S8tWSL.js → git-log-panel-DOtefVWL.js} +1 -1
  12. package/dist/web/assets/{glide-data-grid-D7NyiuHK.js → glide-data-grid-ckYYsHxU.js} +1 -1
  13. package/dist/web/assets/{image-preview-K9Z3rVNA.js → image-preview-vivCL0Rz.js} +1 -1
  14. package/dist/web/assets/{index-heahuZzl.js → index-tXhZwam6.js} +3 -3
  15. package/dist/web/assets/keybindings-store-BPLIbuiX.js +1 -0
  16. package/dist/web/assets/{markdown-renderer-D2zxqOkH.js → markdown-renderer-BiLohd4E.js} +1 -1
  17. package/dist/web/assets/notification-store-DfPnCRYc.js +1 -0
  18. package/dist/web/assets/pdf-preview-CZxInEra.js +1 -0
  19. package/dist/web/assets/{port-forwarding-tab-Cq1866z4.js → port-forwarding-tab-cEuRXpkT.js} +1 -1
  20. package/dist/web/assets/{postgres-viewer-JQEY1K99.js → postgres-viewer-DotG-n6G.js} +1 -1
  21. package/dist/web/assets/{settings-tab-CMZVAcoh.js → settings-tab-DCNSHpkk.js} +1 -1
  22. package/dist/web/assets/{sql-query-editor-BAsdaqSY.js → sql-query-editor-wMM7vxn4.js} +1 -1
  23. package/dist/web/assets/{sqlite-viewer-BN3gOamm.js → sqlite-viewer-fczUQ3pm.js} +1 -1
  24. package/dist/web/assets/{system-monitor-tab-BI7MfACw.js → system-monitor-tab-DnjKZ6kX.js} +1 -1
  25. package/dist/web/assets/{terminal-tab-ScnvZlQ9.js → terminal-tab-4VTsRAv_.js} +1 -1
  26. package/dist/web/assets/{video-preview-CHP7HPJ1.js → video-preview-Aabd59Fv.js} +1 -1
  27. package/dist/web/index.html +1 -1
  28. package/dist/web/sw.js +1 -1
  29. package/package.json +1 -1
  30. package/src/server/middleware/auth.ts +9 -6
  31. package/src/web/components/editor/pdf-preview.tsx +35 -34
  32. package/dist/web/assets/keybindings-store-DWD9jyRg.js +0 -1
  33. package/dist/web/assets/notification-store-DcY2JHZt.js +0 -1
  34. package/dist/web/assets/pdf-preview-BukxhJ6o.js +0 -1
@@ -1,44 +1,36 @@
1
- import { useCallback, useEffect, useRef, useState } from "react";
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { Loader2, FileWarning, ExternalLink } from "lucide-react";
3
- import { useBlobUrl } from "./use-blob-url";
3
+ import { projectUrl, getAuthToken } from "@/lib/api-client";
4
4
 
5
5
  export function PdfPreview({ filePath, projectName }: { filePath: string; projectName: string }) {
6
- const [refreshKey, setRefreshKey] = useState(0);
7
6
  const iframeRef = useRef<HTMLIFrameElement>(null);
8
- const scrollYRef = useRef(0);
7
+ const [loaded, setLoaded] = useState(false);
8
+ const [error, setError] = useState(false);
9
9
 
10
- const { blobUrl, error } = useBlobUrl(filePath, projectName, "application/pdf", refreshKey);
10
+ // Build stable direct URL (no blob) so reload() preserves scroll
11
+ const iframeSrc = useMemo(() => {
12
+ const isExternal = /^(\/|[A-Za-z]:[/\\])/.test(filePath);
13
+ const base = isExternal
14
+ ? `/api/fs/raw?path=${encodeURIComponent(filePath)}`
15
+ : `${projectUrl(projectName)}/files/raw?path=${encodeURIComponent(filePath)}`;
16
+ const token = getAuthToken();
17
+ return token ? `${base}&token=${encodeURIComponent(token)}` : base;
18
+ }, [filePath, projectName]);
11
19
 
12
- // Auto-reload: listen for file:changed WS events
20
+ // Auto-reload on file change — reload() preserves browser PDF viewer scroll
13
21
  useEffect(() => {
14
22
  const handler = (e: Event) => {
15
23
  const detail = (e as CustomEvent).detail;
16
24
  if (detail.projectName !== projectName || detail.path !== filePath) return;
17
- // Save scroll position before re-fetch (best-effort for browser PDF viewer)
18
25
  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);
26
+ iframeRef.current?.contentWindow?.location.reload();
27
+ } catch { /* cross-origin fallback — shouldn't happen for same-origin */ }
23
28
  };
24
29
  window.addEventListener("file:changed", handler);
25
30
  return () => window.removeEventListener("file:changed", handler);
26
31
  }, [filePath, projectName]);
27
32
 
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]);
40
-
41
- const openInNewTab = useCallback(() => { if (blobUrl) window.open(blobUrl, "_blank"); }, [blobUrl]);
33
+ const openInNewTab = useCallback(() => { window.open(iframeSrc, "_blank"); }, [iframeSrc]);
42
34
 
43
35
  if (error) {
44
36
  return (
@@ -48,18 +40,27 @@ export function PdfPreview({ filePath, projectName }: { filePath: string; projec
48
40
  </div>
49
41
  );
50
42
  }
51
- if (!blobUrl) {
52
- return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
53
- }
54
43
  return (
55
44
  <div className="flex flex-col h-full">
56
- <div className="flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0">
57
- <span className="text-xs text-text-secondary truncate">{filePath}</span>
58
- <button onClick={openInNewTab} className="flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors">
59
- <ExternalLink className="size-3" /> Open in new tab
60
- </button>
45
+ {!loaded && (
46
+ <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>
47
+ )}
48
+ <div className={`flex flex-col h-full ${loaded ? "" : "hidden"}`}>
49
+ <div className="flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0">
50
+ <span className="text-xs text-text-secondary truncate">{filePath}</span>
51
+ <button onClick={openInNewTab} className="flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors">
52
+ <ExternalLink className="size-3" /> Open in new tab
53
+ </button>
54
+ </div>
55
+ <iframe
56
+ ref={iframeRef}
57
+ src={iframeSrc}
58
+ title={filePath}
59
+ className="flex-1 w-full border-none"
60
+ onLoad={() => setLoaded(true)}
61
+ onError={() => setError(true)}
62
+ />
61
63
  </div>
62
- <iframe ref={iframeRef} src={blobUrl} title={filePath} className="flex-1 w-full border-none" />
63
64
  </div>
64
65
  );
65
66
  }
@@ -1 +0,0 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./api-client-DiZgVOok.js";import{D as e}from"./index-heahuZzl.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-heahuZzl.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-heahuZzl.js";import{t as o}from"./use-blob-url-DCUIEzjB.js";var s=e(n(),1),c=t();function l({filePath:e,projectName:t}){let[n,l]=(0,s.useState)(0),u=(0,s.useRef)(null),d=(0,s.useRef)(0),{blobUrl:f,error:p}=o(e,t,`application/pdf`,n);(0,s.useEffect)(()=>{let n=n=>{let r=n.detail;if(!(r.projectName!==t||r.path!==e)){try{let e=u.current?.contentWindow;e&&(d.current=e.scrollY||0)}catch{}l(e=>e+1)}};return window.addEventListener(`file:changed`,n),()=>window.removeEventListener(`file:changed`,n)},[e,t]),(0,s.useEffect)(()=>{if(!f||!u.current||d.current===0)return;let e=u.current,t=d.current,n=()=>{try{e.contentWindow?.scrollTo(0,t)}catch{}d.current=0};return e.addEventListener(`load`,n),()=>e.removeEventListener(`load`,n)},[f]);let m=(0,s.useCallback)(()=>{f&&window.open(f,`_blank`)},[f]);return p?(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.`})]}):f?(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:m,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`,{ref:u,src:f,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};