v0.3.2 — commit/submit progress, cancel, thumbnails, waitlist landing+admin
Client: - Commit: batch `p4 reopen` so huge changelists (63k+) no longer blow the Windows command-line limit; live "N / total" progress + spinner during commit. - Submit dialog: white arrow, "N of total" + % bar, running file log, Cancel button (aborts pre-commit; changelist stays pending). - Disk scan: streaming reconcile with a live "found N files" counter + Cancel. - Thumbnails: only decode browser-renderable image formats; glyph fallback (+ onError) instead of a broken-image icon for bmp/tga/dds/etc. Landing + waitlist: - waitlist-server: Express + SQLite email collector (dedup) with a secure admin panel (scrypt auth, rate-limited login, signed cookies) and CSV export. - landing form now POSTs signups to the waitlist API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
25
src/p4.ts
25
src/p4.ts
@ -102,12 +102,18 @@ export const p4 = {
|
||||
del: (files: string[]) => invoke<OpenedFile[]>("p4_delete", { files }),
|
||||
reconcile: (path: string) => invoke<OpenedFile[]>("p4_reconcile", { path }),
|
||||
scan: (scope = "") => invoke<OpenedFile[]>("p4_scan", { scope }),
|
||||
// streaming disk scan: emits `p4-scan` progress events, returns the file count.
|
||||
// The caller re-reads opened() for the actual list.
|
||||
scanStream: (scope = "") => invoke<number>("p4_scan_stream", { scope }),
|
||||
scanCancel: () => invoke<void>("p4_scan_cancel"),
|
||||
// ask an in-progress submit/sync transfer to stop (safe pre-commit for submit)
|
||||
transferCancel: () => invoke<void>("p4_transfer_cancel"),
|
||||
describe: (change: string) => invoke<Describe>("p4_describe", { change }),
|
||||
undoChange: (change: string) => invoke<string>("p4_undo_change", { change }),
|
||||
openInExplorer: (target: string) => invoke<void>("open_in_explorer", { target }),
|
||||
findUproject: (scope: string) => invoke<string>("find_uproject", { scope }),
|
||||
findSln: (scope: string) => invoke<string>("find_sln", { scope }),
|
||||
buildSln: (path: string, config: string) => invoke<string>("build_sln", { path, config }),
|
||||
buildSln: (path: string, config: string, uproject = "") => invoke<string>("build_sln", { path, config, uproject }),
|
||||
launchFile: (path: string) => invoke<void>("launch_file", { path }),
|
||||
listEditors: () => invoke<Editor[]>("list_editors"),
|
||||
openInEditor: (depot: string, editor: string) => invoke<void>("open_in_editor", { depot, editor }),
|
||||
@ -159,6 +165,10 @@ export const p4 = {
|
||||
reopenTo: (change: string, files: string[]) => invoke<string>("p4_reopen_to", { change, files }),
|
||||
latestChange: (scope = "") => invoke<Change | null>("p4_latest_change", { scope }),
|
||||
behind: (scope = "") => invoke<{ count: number; sample: string[] }>("p4_behind", { scope }),
|
||||
makeFolder: (scope: string, name: string) => invoke<string>("make_folder", { scope, name }),
|
||||
deleteFolder: (path: string) => invoke<OpenedFile[]>("delete_folder", { path }),
|
||||
renameFolder: (path: string, newName: string) => invoke<OpenedFile[]>("rename_folder", { path, newName }),
|
||||
folderInfo: (path: string) => invoke<{ path: string; fileCount: number; size: number }>("folder_info", { path }),
|
||||
// embedded Unreal .uasset thumbnail (PNG bytes), for working copy or history
|
||||
uassetThumb: async (spec: string, historical: boolean): Promise<ArrayBuffer> => {
|
||||
const r: unknown = await invoke("uasset_thumbnail", { spec, historical });
|
||||
@ -185,7 +195,7 @@ export interface Editor { id: string; name: string; exe: string; args: string[]
|
||||
export function getEditor(): string { try { return localStorage.getItem("exd-editor") || ""; } catch { return ""; } }
|
||||
export function setEditorPref(id: string) { try { localStorage.setItem("exd-editor", id); } catch {} }
|
||||
|
||||
export interface Dir { dir?: string; [k: string]: unknown }
|
||||
export interface Dir { dir?: string; local?: boolean; [k: string]: unknown }
|
||||
// depot path -> short leaf name (last segment)
|
||||
export function leaf(path?: string): string {
|
||||
if (!path) return "";
|
||||
@ -256,6 +266,17 @@ export function saveScope(client: string, path: string) {
|
||||
export function loadScope(client: string): string {
|
||||
try { return client ? (localStorage.getItem("exd-scope-" + client) || "") : ""; } catch { return ""; }
|
||||
}
|
||||
// last 3 chosen working folders (Recently) — most-recent first, per workspace
|
||||
export function recentScopes(client: string): string[] {
|
||||
try { const a = JSON.parse(localStorage.getItem("exd-recent-" + client) || "[]"); return Array.isArray(a) ? a.slice(0, 3) : []; } catch { return []; }
|
||||
}
|
||||
export function pushRecentScope(client: string, path: string) {
|
||||
try {
|
||||
const cur = recentScopes(client).filter((p) => p !== path);
|
||||
const next = [path, ...cur].slice(0, 3);
|
||||
localStorage.setItem("exd-recent-" + client, JSON.stringify(next));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// format a unix-epoch string as a short local datetime
|
||||
export function fmtTime(t?: string): string {
|
||||
|
||||
Reference in New Issue
Block a user