添加显示图片和视频功能
This commit is contained in:
131
src/App.vue
131
src/App.vue
@@ -93,6 +93,17 @@ interface WorkspaceEntry {
|
||||
path: string;
|
||||
}
|
||||
|
||||
interface WorkspaceChangedPayload {
|
||||
root: string;
|
||||
paths: string[];
|
||||
kind: string;
|
||||
}
|
||||
|
||||
interface WorkspaceWatchErrorPayload {
|
||||
root: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const folderPath = ref("");
|
||||
const dirEntries = ref<DirEntry[]>([]);
|
||||
const LAST_WORKSPACE_PATH_KEY = "yurou:last-workspace-path";
|
||||
@@ -100,6 +111,10 @@ const RECENT_WORKSPACES_KEY = "yurou:recent-workspaces";
|
||||
const MAX_RECENT_WORKSPACES = 12;
|
||||
const recentWorkspaces = ref<WorkspaceEntry[]>(loadRecentWorkspaces());
|
||||
const editorResetKey = ref(0);
|
||||
const WORKSPACE_REFRESH_DELAY_MS = 140;
|
||||
let workspaceRefreshTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let workspaceRefreshInFlight = false;
|
||||
let workspaceRefreshQueued = false;
|
||||
|
||||
// ---- editor zoom (Ctrl+scroll) ----
|
||||
const ZOOM_MIN = 0.7;
|
||||
@@ -552,24 +567,49 @@ async function saveCurrentWorkspaceDocumentBeforeSwitch() {
|
||||
return doSave(contentToSave);
|
||||
}
|
||||
|
||||
function resetWorkspaceAndEditorState() {
|
||||
function resetWorkspaceAndEditorState(options: { stopWatcher?: boolean } = {}) {
|
||||
if (options.stopWatcher !== false) {
|
||||
void stopWorkspaceWatcher();
|
||||
} else {
|
||||
clearPendingWorkspaceRefresh();
|
||||
}
|
||||
folderPath.value = "";
|
||||
dirEntries.value = [];
|
||||
resetMarkdownEditorState();
|
||||
}
|
||||
|
||||
async function startWorkspaceWatcher(path: string) {
|
||||
if (!isTauri()) return;
|
||||
try {
|
||||
await invoke("watch_workspace", {path});
|
||||
} catch (e) {
|
||||
console.error("Failed to watch workspace:", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function stopWorkspaceWatcher() {
|
||||
clearPendingWorkspaceRefresh();
|
||||
if (!isTauri()) return;
|
||||
try {
|
||||
await invoke("unwatch_workspace");
|
||||
} catch (e) {
|
||||
console.error("Failed to stop watching workspace:", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function switchWorkspace(path: string, options: { persist?: boolean; resetEditor?: boolean } = {}) {
|
||||
const normalized = normalizeWorkspacePath(path);
|
||||
|
||||
if (options.resetEditor !== false) {
|
||||
const saved = await saveCurrentWorkspaceDocumentBeforeSwitch();
|
||||
if (!saved) return false;
|
||||
resetWorkspaceAndEditorState();
|
||||
resetWorkspaceAndEditorState({stopWatcher: false});
|
||||
}
|
||||
|
||||
const loaded = await loadDir(normalized);
|
||||
|
||||
if (!loaded) {
|
||||
await stopWorkspaceWatcher();
|
||||
forgetWorkspace(normalized);
|
||||
|
||||
const lastWorkspacePath = getLastWorkspacePath();
|
||||
@@ -580,6 +620,7 @@ async function switchWorkspace(path: string, options: { persist?: boolean; reset
|
||||
return false;
|
||||
}
|
||||
|
||||
void startWorkspaceWatcher(normalized);
|
||||
rememberWorkspace(normalized);
|
||||
|
||||
if (options.persist !== false) {
|
||||
@@ -680,6 +721,79 @@ function findEntry(entries: DirEntry[], path: string): DirEntry | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function mergeLoadedDirectoryEntries(freshEntries: DirEntry[], currentEntries: DirEntry[]): Promise<DirEntry[]> {
|
||||
const currentByPath = new Map(
|
||||
currentEntries.map((entry) => [getWorkspaceIdentity(entry.path), entry]),
|
||||
);
|
||||
|
||||
return Promise.all(
|
||||
freshEntries.map(async (freshEntry) => {
|
||||
const currentEntry = currentByPath.get(getWorkspaceIdentity(freshEntry.path));
|
||||
if (!freshEntry.is_dir || currentEntry?.children === undefined) {
|
||||
return freshEntry;
|
||||
}
|
||||
|
||||
try {
|
||||
const children = await invoke<DirEntry[]>("read_dir", {path: freshEntry.path});
|
||||
freshEntry.children = await mergeLoadedDirectoryEntries(children, currentEntry.children);
|
||||
} catch (e) {
|
||||
console.error("Failed to refresh subdirectory:", e);
|
||||
freshEntry.children = currentEntry.children;
|
||||
}
|
||||
|
||||
return freshEntry;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function clearPendingWorkspaceRefresh() {
|
||||
if (workspaceRefreshTimer) {
|
||||
clearTimeout(workspaceRefreshTimer);
|
||||
workspaceRefreshTimer = null;
|
||||
}
|
||||
workspaceRefreshQueued = false;
|
||||
}
|
||||
|
||||
function scheduleWorkspaceRefresh(root = folderPath.value) {
|
||||
const expectedRoot = normalizeWorkspacePath(root);
|
||||
if (!expectedRoot) return;
|
||||
|
||||
if (workspaceRefreshTimer) {
|
||||
clearTimeout(workspaceRefreshTimer);
|
||||
}
|
||||
|
||||
workspaceRefreshTimer = setTimeout(() => {
|
||||
workspaceRefreshTimer = null;
|
||||
void refreshWorkspaceTree(expectedRoot);
|
||||
}, WORKSPACE_REFRESH_DELAY_MS);
|
||||
}
|
||||
|
||||
async function refreshWorkspaceTree(expectedRoot = folderPath.value) {
|
||||
const normalizedRoot = normalizeWorkspacePath(expectedRoot);
|
||||
if (!normalizedRoot || !folderPath.value) return;
|
||||
if (getWorkspaceIdentity(folderPath.value) !== getWorkspaceIdentity(normalizedRoot)) return;
|
||||
|
||||
if (workspaceRefreshInFlight) {
|
||||
workspaceRefreshQueued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
workspaceRefreshInFlight = true;
|
||||
try {
|
||||
const entries = await invoke<DirEntry[]>("read_dir", {path: normalizedRoot});
|
||||
if (!folderPath.value || getWorkspaceIdentity(folderPath.value) !== getWorkspaceIdentity(normalizedRoot)) return;
|
||||
dirEntries.value = await mergeLoadedDirectoryEntries(entries, dirEntries.value);
|
||||
} catch (e) {
|
||||
console.error("Failed to refresh workspace:", e);
|
||||
} finally {
|
||||
workspaceRefreshInFlight = false;
|
||||
if (workspaceRefreshQueued) {
|
||||
workspaceRefreshQueued = false;
|
||||
scheduleWorkspaceRefresh(normalizedRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onToggleFolder(path: string) {
|
||||
const entry = findEntry(dirEntries.value, path);
|
||||
if (!entry || entry.children !== undefined) return;
|
||||
@@ -793,6 +907,19 @@ registerTauriListener<string>("folder-opened", (event) => {
|
||||
void switchWorkspace(event.payload);
|
||||
});
|
||||
|
||||
registerTauriListener<WorkspaceChangedPayload>("workspace-changed", (event) => {
|
||||
if (!folderPath.value) return;
|
||||
if (getWorkspaceIdentity(event.payload.root) !== getWorkspaceIdentity(folderPath.value)) return;
|
||||
scheduleWorkspaceRefresh(event.payload.root);
|
||||
});
|
||||
|
||||
registerTauriListener<WorkspaceWatchErrorPayload>("workspace-watch-error", (event) => {
|
||||
if (!folderPath.value) return;
|
||||
if (getWorkspaceIdentity(event.payload.root) !== getWorkspaceIdentity(folderPath.value)) return;
|
||||
console.error("Workspace watcher error:", event.payload.message);
|
||||
scheduleWorkspaceRefresh(event.payload.root);
|
||||
});
|
||||
|
||||
registerTauriListener("menu-save", () => {
|
||||
doSave();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user