@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.
- package/CHANGELOG.md +6 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/bun.lock +2129 -0
- package/bunfig.toml +2 -0
- package/dist/web/assets/{audio-preview-LCxWo-25.js → audio-preview-B4ZHsV9k.js} +1 -1
- package/dist/web/assets/{chat-tab-3GUmdAJN.js → chat-tab-DJzZ5i7L.js} +3 -3
- package/dist/web/assets/{code-editor-Cd7NZ0VX.js → code-editor-BsCmjMzu.js} +2 -2
- package/dist/web/assets/{conflict-editor-No7IVPRN.js → conflict-editor-CXWlXO9Z.js} +1 -1
- package/dist/web/assets/{database-viewer-zeQXxkg-.js → database-viewer-VRJVJPQL.js} +1 -1
- package/dist/web/assets/{diff-viewer-DmSkyBaT.js → diff-viewer-KulvDlWa.js} +1 -1
- package/dist/web/assets/{extension-webview-DCwz2Wso.js → extension-webview-BoHzYEwh.js} +1 -1
- package/dist/web/assets/{git-log-panel-Bk7Eh-Bn.js → git-log-panel-95S8tWSL.js} +1 -1
- package/dist/web/assets/{glide-data-grid-ha8hjunf.js → glide-data-grid-D7NyiuHK.js} +1 -1
- package/dist/web/assets/{image-preview-C1VLHLdJ.js → image-preview-K9Z3rVNA.js} +1 -1
- package/dist/web/assets/{index-CP6EIXkh.js → index-heahuZzl.js} +3 -3
- package/dist/web/assets/keybindings-store-DWD9jyRg.js +1 -0
- package/dist/web/assets/{markdown-renderer-C7ZoGWso.js → markdown-renderer-D2zxqOkH.js} +1 -1
- package/dist/web/assets/notification-store-DcY2JHZt.js +1 -0
- package/dist/web/assets/pdf-preview-BukxhJ6o.js +1 -0
- package/dist/web/assets/{port-forwarding-tab-BMb8neu0.js → port-forwarding-tab-Cq1866z4.js} +1 -1
- package/dist/web/assets/{postgres-viewer-DkM6ndux.js → postgres-viewer-JQEY1K99.js} +1 -1
- package/dist/web/assets/{settings-tab-WdL5pYxG.js → settings-tab-CMZVAcoh.js} +1 -1
- package/dist/web/assets/{sql-query-editor-CNwOnFgF.js → sql-query-editor-BAsdaqSY.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-FFlsRPWf.js → sqlite-viewer-BN3gOamm.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-CefzxLVi.js → system-monitor-tab-BI7MfACw.js} +1 -1
- package/dist/web/assets/{terminal-tab-C4MNU5S3.js → terminal-tab-ScnvZlQ9.js} +1 -1
- package/dist/web/assets/use-blob-url-DCUIEzjB.js +1 -0
- package/dist/web/assets/{video-preview-izCy3xj7.js → video-preview-CHP7HPJ1.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/packages/ext-git-graph/src/webview-html.ts +17 -11
- package/src/index.ts +0 -0
- package/src/web/components/editor/pdf-preview.tsx +36 -3
- package/src/web/components/editor/use-blob-url.ts +18 -9
- package/dist/web/assets/keybindings-store-Rf9YKWAp.js +0 -1
- package/dist/web/assets/notification-store-C3tg-ZXm.js +0 -1
- package/dist/web/assets/pdf-preview-BdmR2RsT.js +0 -1
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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};
|