refactor: scriptWorkspaceStore.ts
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// ---- pythonEditorSlice ----
|
||||
//
|
||||
// 拥有 pythonEditorBuffers (per-scriptId 临时编辑缓冲)。
|
||||
// Actions: openPythonEditor / setPythonEditorContent / savePythonEditor /
|
||||
// exitPythonEditor / exitAllPythonEditors。
|
||||
// 模块级 _pythonEditorOpeningIds (去重 in-flight open) 通过 helpers 访问。
|
||||
|
||||
import type { StateCreator } from "zustand";
|
||||
import type { ScriptItem } from "~/services/api";
|
||||
|
||||
import {
|
||||
getPythonEditorOpeningIds,
|
||||
pushToast,
|
||||
requireApi,
|
||||
} from "./helpers";
|
||||
|
||||
import type {
|
||||
PythonEditorSliceActions,
|
||||
PythonEditorSliceState,
|
||||
} from "./types";
|
||||
import type { ScriptWorkspaceStore } from "./useScriptWorkspaceStore";
|
||||
|
||||
export const createPythonEditorSlice: StateCreator<
|
||||
ScriptWorkspaceStore,
|
||||
[],
|
||||
[],
|
||||
PythonEditorSliceState & PythonEditorSliceActions
|
||||
> = (set, get) => {
|
||||
const initial: PythonEditorSliceState = {
|
||||
pythonEditorBuffers: {},
|
||||
};
|
||||
|
||||
return {
|
||||
...initial,
|
||||
|
||||
openPythonEditor: async (script: ScriptItem, showToast = true) => {
|
||||
if (!script) return;
|
||||
const opening = getPythonEditorOpeningIds();
|
||||
if (opening.has(script.script_id)) return;
|
||||
opening.add(script.script_id);
|
||||
try {
|
||||
const workspaceId = script.workspace_id;
|
||||
const url =
|
||||
`/jupyter/${workspaceId}/api/contents/${script.jupyter_path}` +
|
||||
`?type=file&content=1&hash=1&format=text`;
|
||||
const response = await fetch(url, {
|
||||
credentials: "include",
|
||||
cache: "no-store",
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`加载文件失败: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const content = typeof data.content === "string" ? data.content : "";
|
||||
set((state) => ({
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[script.script_id]: {
|
||||
initialContent: content,
|
||||
content,
|
||||
dirty: false,
|
||||
saving: false,
|
||||
initial: true,
|
||||
loadError: null,
|
||||
},
|
||||
},
|
||||
}));
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "打开 Python 编辑器失败";
|
||||
set((state) => ({
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[script.script_id]: {
|
||||
initialContent: null,
|
||||
content: null,
|
||||
dirty: false,
|
||||
saving: false,
|
||||
initial: false,
|
||||
loadError: message,
|
||||
},
|
||||
},
|
||||
}));
|
||||
if (showToast) {
|
||||
pushToast("error", message);
|
||||
}
|
||||
} finally {
|
||||
opening.delete(script.script_id);
|
||||
}
|
||||
},
|
||||
|
||||
setPythonEditorContent: (scriptId, value) => {
|
||||
set((state) => {
|
||||
const buffer = state.pythonEditorBuffers[scriptId];
|
||||
if (!buffer) return state;
|
||||
return {
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[scriptId]: {
|
||||
...buffer,
|
||||
content: value,
|
||||
dirty: value !== buffer.initialContent,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
savePythonEditor: async (scriptId) => {
|
||||
const buffer = get().pythonEditorBuffers[scriptId];
|
||||
if (!buffer || buffer.content === null || !buffer.dirty || buffer.saving) {
|
||||
return;
|
||||
}
|
||||
const script = get().scripts.find((s) => s.script_id === scriptId);
|
||||
if (!script) return;
|
||||
const savedContent = buffer.content;
|
||||
set((state) => ({
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[scriptId]: { ...buffer, saving: true },
|
||||
},
|
||||
}));
|
||||
try {
|
||||
const api = requireApi();
|
||||
await api.updateScript(scriptId, { content: savedContent });
|
||||
set((state) => {
|
||||
const current = state.pythonEditorBuffers[scriptId];
|
||||
if (!current) return state;
|
||||
return {
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[scriptId]: {
|
||||
...current,
|
||||
initialContent: savedContent,
|
||||
dirty: current.content !== savedContent,
|
||||
saving: false,
|
||||
initial: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
pushToast("success", "已保存");
|
||||
} catch (error) {
|
||||
set((state) => {
|
||||
const current = state.pythonEditorBuffers[scriptId];
|
||||
if (!current) return state;
|
||||
return {
|
||||
pythonEditorBuffers: {
|
||||
...state.pythonEditorBuffers,
|
||||
[scriptId]: { ...current, saving: false },
|
||||
},
|
||||
};
|
||||
});
|
||||
pushToast(
|
||||
"error",
|
||||
error instanceof Error ? error.message : "保存失败",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
exitPythonEditor: (scriptId) => {
|
||||
set((state) => {
|
||||
const next = { ...state.pythonEditorBuffers };
|
||||
delete next[scriptId];
|
||||
return { pythonEditorBuffers: next };
|
||||
});
|
||||
},
|
||||
|
||||
exitAllPythonEditors: () => {
|
||||
const opening = getPythonEditorOpeningIds();
|
||||
opening.clear();
|
||||
set({ pythonEditorBuffers: {} });
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user