@hienlh/ppm 0.18.4 → 0.18.6
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 +19 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/{ai-resource-editor-Bb-NNlp1.js → ai-resource-editor-C59ti0ED.js} +1 -1
- package/dist/web/assets/{audio-preview-DG2Tw0cO.js → audio-preview-GGksv6U8.js} +1 -1
- package/dist/web/assets/chat-tab-CM5hUNpk.js +15 -0
- package/dist/web/assets/code-editor-2yL9lCL1.js +10 -0
- package/dist/web/assets/{conflict-editor-B6hVffX3.js → conflict-editor-H1zmUXSQ.js} +1 -1
- package/dist/web/assets/{csv-preview-hL4Z9HS5.js → csv-preview-Dzzyx_Gv.js} +1 -1
- package/dist/web/assets/{database-viewer-CTK5kMaA.js → database-viewer-CiVA--mM.js} +1 -1
- package/dist/web/assets/{diff-viewer-DZxnANY4.js → diff-viewer-69nl3mqO.js} +1 -1
- package/dist/web/assets/{docx-preview-CrAc7Qjv.js → docx-preview-CFvopEBf.js} +1 -1
- package/dist/web/assets/explorer-body-DyPXovdU.js +2 -0
- package/dist/web/assets/{explorer-window-content-NS8Y0v8c.js → explorer-window-content-CXFdvs_i.js} +1 -1
- package/dist/web/assets/{extension-webview-D6eywKPN.js → extension-webview-DIoKwGV4.js} +1 -1
- package/dist/web/assets/{git-log-panel-DAogZ9QV.js → git-log-panel-Copm8mzy.js} +1 -1
- package/dist/web/assets/{glide-data-grid-DxfJRN-r.js → glide-data-grid-D0L8SZVt.js} +4 -4
- package/dist/web/assets/group-chat-tab-D9cazEAI.js +1 -0
- package/dist/web/assets/{image-preview-Byjx8MoL.js → image-preview-vFeN8o43.js} +1 -1
- package/dist/web/assets/{index-CcN0TBm4.js → index-BewcuJw5.js} +24 -24
- package/dist/web/assets/index-oxsyfDa0.css +2 -0
- package/dist/web/assets/keybindings-store-ruNjhVT3.js +1 -0
- package/dist/web/assets/{markdown-renderer-CXlEe3ma.js → markdown-renderer-B46VJEHH.js} +2 -2
- package/dist/web/assets/markdown-renderer-CYiMPviS.js +1 -0
- package/dist/web/assets/{mobile-explorer-sheet-BuqBlP3o.js → mobile-explorer-sheet-BkMZxqs3.js} +1 -1
- package/dist/web/assets/notification-store-B3IK7PXf.js +1 -0
- package/dist/web/assets/{pdf-preview-F2DWVB85.js → pdf-preview-DU_NRMno.js} +1 -1
- package/dist/web/assets/{postgres-viewer-KZqPYfOB.js → postgres-viewer-C0h3Zfk9.js} +2 -2
- package/dist/web/assets/{settings-store-CY-4_U3H.js → settings-store-C9YT1Hir.js} +2 -2
- package/dist/web/assets/{settings-tab-Khj-w6Sr.js → settings-tab-Cep8gjxG.js} +1 -1
- package/dist/web/assets/{sql-query-editor-Cp0_e2P1.js → sql-query-editor-DJofTC9F.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CIv3sagZ.js → sqlite-viewer-D7bskeyk.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-CrssTOf4.js → system-monitor-tab-CxgFsQx9.js} +1 -1
- package/dist/web/assets/team-member-sheet-Dn5NGkcp.js +2 -0
- package/dist/web/assets/{team-member-window-content-D-hXax3c.js → team-member-window-content-DS9V4trr.js} +1 -1
- package/dist/web/assets/{terminal-tab-BIQDG-Ti.js → terminal-tab-CMs8AyFJ.js} +3 -3
- package/dist/web/assets/tool-cards-HHRTIlFv.js +8 -0
- package/dist/web/assets/{use-monaco-theme-Djp1_fKi.js → use-monaco-theme-BLt-n9r1.js} +1 -1
- package/dist/web/assets/use-open-team-member-BwtzTuxf.js +1 -0
- package/dist/web/assets/{use-websocket-CCYA9-a2.js → use-websocket-DA3Zwl3Z.js} +1 -1
- package/dist/web/assets/video-preview-BZhTYMFX.js +1 -0
- package/dist/web/index.html +3 -3
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/web/app.tsx +12 -0
- package/src/web/components/chat/chat-tab.tsx +11 -0
- package/src/web/components/chat/team-activity-panel.tsx +4 -4
- package/src/web/components/chat/team-member-activity-format.ts +38 -0
- package/src/web/components/chat/team-member-activity-row.tsx +1 -29
- package/src/web/components/chat/team-member-sheet.tsx +47 -0
- package/src/web/components/chat/team-working-bar.tsx +62 -0
- package/src/web/components/chat/use-open-team-member.ts +41 -0
- package/src/web/components/os-explorer/actions/collision-prompt-queue.ts +133 -0
- package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +29 -3
- package/src/web/components/os-explorer/actions/explorer-actions-upload.ts +88 -33
- package/src/web/components/os-explorer/actions/use-collision-prompt.ts +56 -0
- package/src/web/components/os-explorer/actions/use-explorer-actions.ts +29 -44
- package/src/web/components/os-explorer/actions/use-permanent-overwrite-prompt.ts +47 -0
- package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +19 -27
- package/src/web/components/os-explorer/explorer-dialogs.tsx +12 -1
- package/src/web/components/os-explorer/upload/capture-picked-files.ts +18 -0
- package/src/web/components/os-explorer/upload/upload-batch-card.tsx +64 -0
- package/src/web/components/os-explorer/upload/upload-batch-progress.ts +34 -0
- package/src/web/components/os-explorer/upload/upload-batch-row.tsx +79 -0
- package/src/web/components/os-explorer/upload/upload-error-message.ts +19 -0
- package/src/web/components/os-explorer/upload/upload-progress-panel.tsx +72 -0
- package/src/web/components/os-explorer/upload/upload-store.ts +109 -0
- package/src/web/components/os-explorer/upload/use-upload-picker.tsx +7 -5
- package/src/web/lib/fs-upload-xhr.ts +13 -2
- package/dist/web/assets/chat-tab-CgAv-crh.js +0 -15
- package/dist/web/assets/code-editor-bX4jlgMy.js +0 -10
- package/dist/web/assets/explorer-body-DykJXCTw.js +0 -2
- package/dist/web/assets/group-chat-tab-DkVi6qL2.js +0 -1
- package/dist/web/assets/index-Bucbpby-.css +0 -2
- package/dist/web/assets/keybindings-store-DJIHqbLM.js +0 -1
- package/dist/web/assets/markdown-renderer-pYznJDbh.js +0 -1
- package/dist/web/assets/notification-store-Bcl5efY2.js +0 -1
- package/dist/web/assets/tool-cards-Dx-Rrpri.js +0 -8
- package/dist/web/assets/video-preview-cvjNMjWA.js +0 -1
- /package/dist/web/assets/{api-themes-CEyYK7AK.js → api-themes-8WTH6KpV.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Cu-XYIgu.js → can-open-in-ppm-DXGOSUQc.js} +0 -0
- /package/dist/web/assets/{csv-parser-r-Nk1oHT.js → csv-parser-BRtnJCfv.js} +0 -0
- /package/dist/web/assets/{edit-diff-preview-HTZPC9VP.js → edit-diff-preview-BivQwZMD.js} +0 -0
- /package/dist/web/assets/{lib-BzxL1ARZ.js → lib-B1clP4ai.js} +0 -0
- /package/dist/web/assets/{media-url-C5vA0ha9.js → media-url-CUpN5vI1.js} +0 -0
- /package/dist/web/assets/{run-in-terminal-B8TDtaM_.js → run-in-terminal-CGW6o37_.js} +0 -0
- /package/dist/web/assets/{send-to-chat-CuBAVC8l.js → send-to-chat-YXHdAyLG.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BeaIjRrv.js → vendor-xterm-C1diXHJl.js} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global upload progress state — batches of in-flight uploads (a drop or a picker pick), each
|
|
3
|
+
* with per-file rows. Backs the persistent upload panel that replaced the old
|
|
4
|
+
* "Uploading N files… NN%" toast, which had no room for per-file outcomes or a cancel button.
|
|
5
|
+
*
|
|
6
|
+
* Cancel hooks are kept in a side map (`cancellers`), not on the serializable item/batch
|
|
7
|
+
* state — an `AbortController.abort` closure has no business living next to plain data a
|
|
8
|
+
* test might snapshot or log.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { create } from "zustand";
|
|
12
|
+
|
|
13
|
+
export type UploadItemState = "queued" | "uploading" | "done" | "skipped" | "failed" | "cancelled";
|
|
14
|
+
|
|
15
|
+
export interface UploadItem {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
relativePath: string;
|
|
19
|
+
size: number;
|
|
20
|
+
bytesLoaded: number;
|
|
21
|
+
state: UploadItemState;
|
|
22
|
+
errorMessage?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UploadBatch {
|
|
26
|
+
id: string;
|
|
27
|
+
dstDir: string;
|
|
28
|
+
items: UploadItem[];
|
|
29
|
+
/** True once every item has left "queued"/"uploading" — the panel swaps Cancel for Close. */
|
|
30
|
+
settled: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type NewUploadItem = Pick<UploadItem, "id" | "name" | "relativePath" | "size">;
|
|
34
|
+
|
|
35
|
+
function deriveSettled(items: UploadItem[]): boolean {
|
|
36
|
+
return items.every((i) => i.state !== "queued" && i.state !== "uploading");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface UploadStoreState {
|
|
40
|
+
order: string[];
|
|
41
|
+
batches: Record<string, UploadBatch>;
|
|
42
|
+
cancellers: Record<string, () => void>;
|
|
43
|
+
|
|
44
|
+
addBatch(id: string, dstDir: string, items: NewUploadItem[]): void;
|
|
45
|
+
registerCanceller(batchId: string, itemId: string, cancel: () => void): void;
|
|
46
|
+
setItemState(batchId: string, itemId: string, state: UploadItemState, errorMessage?: string): void;
|
|
47
|
+
setItemProgress(batchId: string, itemId: string, bytesLoaded: number): void;
|
|
48
|
+
cancelItem(batchId: string, itemId: string): void;
|
|
49
|
+
cancelBatch(batchId: string): void;
|
|
50
|
+
dismissBatch(batchId: string): void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const useUploadStore = create<UploadStoreState>((set, get) => ({
|
|
54
|
+
order: [],
|
|
55
|
+
batches: {},
|
|
56
|
+
cancellers: {},
|
|
57
|
+
|
|
58
|
+
addBatch: (id, dstDir, items) => {
|
|
59
|
+
const batch: UploadBatch = {
|
|
60
|
+
id,
|
|
61
|
+
dstDir,
|
|
62
|
+
items: items.map((item) => ({ ...item, bytesLoaded: 0, state: "queued" })),
|
|
63
|
+
settled: false,
|
|
64
|
+
};
|
|
65
|
+
set((s) => ({ order: [...s.order, id], batches: { ...s.batches, [id]: batch } }));
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
registerCanceller: (batchId, itemId, cancel) => {
|
|
69
|
+
set((s) => ({ cancellers: { ...s.cancellers, [`${batchId}:${itemId}`]: cancel } }));
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
setItemState: (batchId, itemId, state, errorMessage) => {
|
|
73
|
+
set((s) => {
|
|
74
|
+
const batch = s.batches[batchId];
|
|
75
|
+
if (!batch) return s;
|
|
76
|
+
const items = batch.items.map((i) => (i.id === itemId ? { ...i, state, errorMessage } : i));
|
|
77
|
+
return { batches: { ...s.batches, [batchId]: { ...batch, items, settled: deriveSettled(items) } } };
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
setItemProgress: (batchId, itemId, bytesLoaded) => {
|
|
82
|
+
set((s) => {
|
|
83
|
+
const batch = s.batches[batchId];
|
|
84
|
+
if (!batch) return s;
|
|
85
|
+
const items = batch.items.map((i) => (i.id === itemId ? { ...i, bytesLoaded } : i));
|
|
86
|
+
return { batches: { ...s.batches, [batchId]: { ...batch, items } } };
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Cancelling only ever *asks* the in-flight job to stop (abort the XHR, or skip a queued
|
|
91
|
+
// job before it starts) — the resulting "cancelled" state is set by the job itself once its
|
|
92
|
+
// promise actually rejects, not here, so state and reality never disagree.
|
|
93
|
+
cancelItem: (batchId, itemId) => {
|
|
94
|
+
get().cancellers[`${batchId}:${itemId}`]?.();
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
cancelBatch: (batchId) => {
|
|
98
|
+
const batch = get().batches[batchId];
|
|
99
|
+
if (!batch) return;
|
|
100
|
+
for (const item of batch.items) get().cancelItem(batchId, item.id);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
dismissBatch: (batchId) => {
|
|
104
|
+
set((s) => {
|
|
105
|
+
const { [batchId]: _removed, ...batches } = s.batches;
|
|
106
|
+
return { batches, order: s.order.filter((id) => id !== batchId) };
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useRef, type ChangeEvent, type ReactNode } from "react";
|
|
11
|
+
import { capturePickedFiles } from "./capture-picked-files";
|
|
11
12
|
import type { DroppedEntry } from "./collect-dropped-entries";
|
|
12
13
|
|
|
13
14
|
export interface UploadPicker {
|
|
@@ -30,11 +31,12 @@ export function useUploadPicker(onFiles: (entries: DroppedEntry[]) => void): Upl
|
|
|
30
31
|
typeof document !== "undefined" && "webkitdirectory" in document.createElement("input");
|
|
31
32
|
|
|
32
33
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
// Captures the live FileList into a plain array BEFORE clearing `value` (see
|
|
35
|
+
// `capture-picked-files.ts`) — clearing first, so picking the exact same file/folder
|
|
36
|
+
// again still fires `onChange`, would otherwise race Chromium emptying the list itself.
|
|
37
|
+
const picked = capturePickedFiles(e.target.files, () => { e.target.value = ""; });
|
|
38
|
+
if (picked.length === 0) return;
|
|
39
|
+
const entries: DroppedEntry[] = picked.map((file) => ({
|
|
38
40
|
file,
|
|
39
41
|
// The folder picker sets `webkitRelativePath` ("folder/sub/file.txt"); a plain file
|
|
40
42
|
// picker leaves it empty, so the bare name is the relative path.
|
|
@@ -29,8 +29,14 @@ export function uploadFileXhr(
|
|
|
29
29
|
file: File,
|
|
30
30
|
overwrite: boolean,
|
|
31
31
|
onProgress: (progress: UploadProgress) => void,
|
|
32
|
+
signal?: AbortSignal,
|
|
32
33
|
): Promise<{ path: string; size: number }> {
|
|
33
34
|
return new Promise((resolve, reject) => {
|
|
35
|
+
if (signal?.aborted) {
|
|
36
|
+
reject(new DOMException("Upload aborted", "AbortError"));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
const xhr = new XMLHttpRequest();
|
|
35
41
|
xhr.open("PUT", fsApi.uploadUrl(path, overwrite));
|
|
36
42
|
const token = getAuthToken();
|
|
@@ -41,7 +47,12 @@ export function uploadFileXhr(
|
|
|
41
47
|
if (e.lengthComputable) onProgress({ loaded: e.loaded, total: e.total });
|
|
42
48
|
};
|
|
43
49
|
|
|
50
|
+
const onAbortSignal = () => xhr.abort();
|
|
51
|
+
signal?.addEventListener("abort", onAbortSignal);
|
|
52
|
+
const detach = () => signal?.removeEventListener("abort", onAbortSignal);
|
|
53
|
+
|
|
44
54
|
xhr.onload = () => {
|
|
55
|
+
detach();
|
|
45
56
|
let json: UploadResponseBody = {};
|
|
46
57
|
try {
|
|
47
58
|
json = JSON.parse(xhr.responseText);
|
|
@@ -54,8 +65,8 @@ export function uploadFileXhr(
|
|
|
54
65
|
}
|
|
55
66
|
reject(new FsError(json.error ?? `HTTP ${xhr.status}`, json.code ?? "EUNKNOWN", xhr.status));
|
|
56
67
|
};
|
|
57
|
-
xhr.onerror = () => reject(new Error("Network error during upload"));
|
|
58
|
-
xhr.onabort = () => reject(new DOMException("Upload aborted", "AbortError"));
|
|
68
|
+
xhr.onerror = () => { detach(); reject(new Error("Network error during upload")); };
|
|
69
|
+
xhr.onabort = () => { detach(); reject(new DOMException("Upload aborted", "AbortError")); };
|
|
59
70
|
|
|
60
71
|
xhr.send(file);
|
|
61
72
|
});
|