v0.3.1 — view-panel tabs, tray + notifications, server-activity, media preview, dockable locks

- Tabbed view panel: File Viewer / File Tree / File Locks, header hidden at one tab, re-openable from Window, state persisted
- Right-click Workspace/Working-folder zones -> Show in File Tree (focuses the right side)
- Commit weight shown in History (p4 sizes -s //...@=CH)
- Rich code editor (highlighted underlay + gutter) and clean spec-editor surfaces (no more grey notepad)
- System tray (close-to-tray, tray menu Quit) + native OS notifications for teammate submits, even when hidden
- 'You're behind the server' banner + native notification (p4 sync -n), rechecked only when the server advances
- Audio & video preview with playback (media-src blob CSP); File Locks dockable into the view panel
- Backend: depot_ls/fs_ls, p4_change_sizes, p4_behind; tauri-plugin-notification + tray-icon

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bonchellon
2026-07-08 14:26:22 +03:00
parent 9c24d45bf9
commit 5652f489a2
12 changed files with 689 additions and 126 deletions

View File

@ -75,6 +75,7 @@ export const p4 = {
cleanPreview: (scope = "") => invoke<OpenedFile[]>("p4_clean_preview", { scope }),
cleanApply: (scope = "") => invoke<string>("p4_clean_apply", { scope }),
depotLs: (path = "") => invoke<FsEntry[]>("depot_ls", { path }),
changeSizes: (changes: string[], scope = "") => invoke<{ change: string; size: number }[]>("p4_change_sizes", { changes, scope }),
fsLs: (path = "") => invoke<FsEntry[]>("fs_ls", { path }),
reconcilePreview: (scope = "") => invoke<OpenedFile[]>("p4_reconcile_preview", { scope }),
reconcileApply: (paths: string[]) => invoke<OpenedFile[]>("p4_reconcile_apply", { paths }),
@ -157,6 +158,7 @@ export const p4 = {
searchFiles: (query: string, scope = "") => invoke<{ depotFile?: string; rev?: string }[]>("p4_search_files", { query, scope }),
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 }),
// 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 });
@ -281,12 +283,14 @@ export function splitPath(p?: string): { name: string; dir: string } {
}
// classify by extension for the preview panel
export type Kind = "model" | "uasset" | "image" | "code";
export type Kind = "model" | "uasset" | "image" | "audio" | "video" | "code";
export function kindOf(name: string): Kind {
const n = name.toLowerCase();
if (/\.(fbx|obj|gltf|glb|stl|ply|3mf|dae)$/.test(n)) return "model";
if (/\.(uasset|umap)$/.test(n)) return "uasset";
if (/\.(png|jpe?g|webp|gif|bmp|svg)$/.test(n)) return "image";
if (/\.(wav|mp3|ogg|flac|m4a|aac|opus)$/.test(n)) return "audio";
if (/\.(mp4|webm|mov|m4v|ogv)$/.test(n)) return "video";
return "code";
}