175 lines
5.1 KiB
TypeScript
175 lines
5.1 KiB
TypeScript
// ---- 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: {} });
|
|
},
|
|
};
|
|
}; |