feat: workspace 目录树服务端懒加载
- api.ts: listWorkspaceDirectories 支持 parent_path 查询; WorkspaceDirectory 增加 has_children; 同步 WorkspaceBoundApi 签名 - AuthContext: 绑定透传 parentPath - scriptWorkspaceStore: 新增 expandedPaths/loadingChildrenPaths/loadedChildPaths, loadChildren/toggleExpanded, 局部刷新 createFolder/deleteDirectory - WorkspaceTree: 移除 useState, 改为受控展开/加载状态 - ScriptExplorer: 从 store 读取并透传展开状态与 toggle
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { type ChangeEvent, type MouseEvent as ReactMouseEvent, type RefObject, useMemo } from "react";
|
import { type ChangeEvent, type MouseEvent as ReactMouseEvent, type RefObject, useMemo } from "react";
|
||||||
import Icon from "../common/Icon";
|
import Icon from "../common/Icon";
|
||||||
import { WorkspaceTreeGroup } from "~/features/platform/WorkspaceTree";
|
import { WorkspaceTreeGroup } from "~/features/platform/WorkspaceTree";
|
||||||
|
import { useScriptWorkspaceStore } from "~/features/platform/state/scriptWorkspaceStore";
|
||||||
import type { ScriptItem, WorkspaceDirectory } from "~/services/api";
|
import type { ScriptItem, WorkspaceDirectory } from "~/services/api";
|
||||||
import { type AuthUser } from "~/context/AuthContext";
|
import { type AuthUser } from "~/context/AuthContext";
|
||||||
|
|
||||||
@@ -50,6 +51,12 @@ export function ScriptExplorer({
|
|||||||
uploadInputRef,
|
uploadInputRef,
|
||||||
onHandleUpload,
|
onHandleUpload,
|
||||||
}: ScriptExplorerProps) {
|
}: ScriptExplorerProps) {
|
||||||
|
const expandedPaths = useScriptWorkspaceStore((s) => s.expandedPaths);
|
||||||
|
const loadingChildrenPaths = useScriptWorkspaceStore(
|
||||||
|
(s) => s.loadingChildrenPaths,
|
||||||
|
);
|
||||||
|
const onToggle = useScriptWorkspaceStore((s) => s.toggleExpanded);
|
||||||
|
|
||||||
const memberScriptGroups = useMemo(() => {
|
const memberScriptGroups = useMemo(() => {
|
||||||
const visibleScripts =
|
const visibleScripts =
|
||||||
user?.is_system_admin === true
|
user?.is_system_admin === true
|
||||||
@@ -185,6 +192,9 @@ export function ScriptExplorer({
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
readOnly={group.user?.user_id !== user?.user_id}
|
readOnly={group.user?.user_id !== user?.user_id}
|
||||||
|
expandedPaths={expandedPaths}
|
||||||
|
onToggle={onToggle}
|
||||||
|
loadingChildrenPaths={loadingChildrenPaths}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredScripts.length === 0 && (
|
{filteredScripts.length === 0 && (
|
||||||
|
|||||||
@@ -230,7 +230,8 @@ export function useApi(): WorkspaceBoundApi {
|
|||||||
deleteScript: (scriptId) => rawApi.deleteScript(workspaceId, scriptId),
|
deleteScript: (scriptId) => rawApi.deleteScript(workspaceId, scriptId),
|
||||||
setScriptLock: (scriptId, isLocked) =>
|
setScriptLock: (scriptId, isLocked) =>
|
||||||
rawApi.setScriptLock(workspaceId, scriptId, isLocked),
|
rawApi.setScriptLock(workspaceId, scriptId, isLocked),
|
||||||
listWorkspaceDirectories: () => rawApi.listWorkspaceDirectories(workspaceId),
|
listWorkspaceDirectories: (parentPath?: string) =>
|
||||||
|
rawApi.listWorkspaceDirectories(workspaceId, parentPath ?? ""),
|
||||||
createWorkspaceDirectory: (directoryName, parentPath) =>
|
createWorkspaceDirectory: (directoryName, parentPath) =>
|
||||||
rawApi.createWorkspaceDirectory(workspaceId, directoryName, parentPath),
|
rawApi.createWorkspaceDirectory(workspaceId, directoryName, parentPath),
|
||||||
deleteWorkspaceDirectory: (path) =>
|
deleteWorkspaceDirectory: (path) =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type MouseEvent as ReactMouseEvent, useState } from "react";
|
import { type MouseEvent as ReactMouseEvent } from "react";
|
||||||
|
|
||||||
import Icon from "../../components/common/Icon";
|
import Icon from "../../components/common/Icon";
|
||||||
import type {
|
import type {
|
||||||
@@ -23,6 +23,9 @@ type WorkspaceTreeProps = {
|
|||||||
target: WorkspaceTreeTarget,
|
target: WorkspaceTreeTarget,
|
||||||
) => void;
|
) => void;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
|
expandedPaths: Set<string>;
|
||||||
|
onToggle: (path: string) => void;
|
||||||
|
loadingChildrenPaths: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WorkspaceTreeItemsProps = Omit<WorkspaceTreeProps, "title" | "readOnly"> & {
|
type WorkspaceTreeItemsProps = Omit<WorkspaceTreeProps, "title" | "readOnly"> & {
|
||||||
@@ -62,15 +65,19 @@ export function WorkspaceTreeGroup({
|
|||||||
onSelect,
|
onSelect,
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
|
expandedPaths,
|
||||||
|
onToggle,
|
||||||
|
loadingChildrenPaths,
|
||||||
}: WorkspaceTreeProps) {
|
}: WorkspaceTreeProps) {
|
||||||
const [open, setOpen] = useState(true);
|
const open = expandedPaths.has("");
|
||||||
|
const childrenLoading = loadingChildrenPaths.has("");
|
||||||
return (
|
return (
|
||||||
<div className="tree-group">
|
<div className="tree-group">
|
||||||
<button
|
<button
|
||||||
className={`tree-group__title${open ? " is-open" : ""}`}
|
className={`tree-group__title${open ? " is-open" : ""}`}
|
||||||
type="button"
|
type="button"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
onClick={() => setOpen((current) => !current)}
|
onClick={() => onToggle("")}
|
||||||
onContextMenu={onContextMenu
|
onContextMenu={onContextMenu
|
||||||
? (event) => onContextMenu(event, { kind: "root", path: "" })
|
? (event) => onContextMenu(event, { kind: "root", path: "" })
|
||||||
: undefined}
|
: undefined}
|
||||||
@@ -79,6 +86,7 @@ export function WorkspaceTreeGroup({
|
|||||||
<Icon name="folder" size={17} />
|
<Icon name="folder" size={17} />
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
<em>{scripts.length}</em>
|
<em>{scripts.length}</em>
|
||||||
|
{childrenLoading && <span className="loading-spinner" />}
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div className="tree-group__items">
|
<div className="tree-group__items">
|
||||||
@@ -90,6 +98,9 @@ export function WorkspaceTreeGroup({
|
|||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
|
expandedPaths={expandedPaths}
|
||||||
|
onToggle={onToggle}
|
||||||
|
loadingChildrenPaths={loadingChildrenPaths}
|
||||||
/>
|
/>
|
||||||
{scripts.length === 0 && directories.length === 0 && (
|
{scripts.length === 0 && directories.length === 0 && (
|
||||||
<p className="tree-group__empty">
|
<p className="tree-group__empty">
|
||||||
@@ -110,6 +121,9 @@ function WorkspaceTreeItems({
|
|||||||
selectedId,
|
selectedId,
|
||||||
onSelect,
|
onSelect,
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
|
expandedPaths,
|
||||||
|
onToggle,
|
||||||
|
loadingChildrenPaths,
|
||||||
}: WorkspaceTreeItemsProps) {
|
}: WorkspaceTreeItemsProps) {
|
||||||
const childDirectories = directories.filter(
|
const childDirectories = directories.filter(
|
||||||
(item) => item.parent_path === path,
|
(item) => item.parent_path === path,
|
||||||
@@ -129,6 +143,9 @@ function WorkspaceTreeItems({
|
|||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
|
expandedPaths={expandedPaths}
|
||||||
|
onToggle={onToggle}
|
||||||
|
loadingChildrenPaths={loadingChildrenPaths}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{childScripts.map((item) => (
|
{childScripts.map((item) => (
|
||||||
@@ -177,17 +194,21 @@ function DirectoryBranch({
|
|||||||
selectedId,
|
selectedId,
|
||||||
onSelect,
|
onSelect,
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
|
expandedPaths,
|
||||||
|
onToggle,
|
||||||
|
loadingChildrenPaths,
|
||||||
}: Omit<WorkspaceTreeItemsProps, "path"> & {
|
}: Omit<WorkspaceTreeItemsProps, "path"> & {
|
||||||
directory: WorkspaceDirectory;
|
directory: WorkspaceDirectory;
|
||||||
}) {
|
}) {
|
||||||
const [open, setOpen] = useState(true);
|
const open = expandedPaths.has(directory.path);
|
||||||
|
const childrenLoading = loadingChildrenPaths.has(directory.path);
|
||||||
return (
|
return (
|
||||||
<div className="directory-branch">
|
<div className="directory-branch">
|
||||||
<button
|
<button
|
||||||
className="directory-row"
|
className="directory-row"
|
||||||
style={{ paddingLeft: 10 + depth * 16 }}
|
style={{ paddingLeft: 10 + depth * 16 }}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setOpen((current) => !current)}
|
onClick={() => onToggle(directory.path)}
|
||||||
onContextMenu={onContextMenu
|
onContextMenu={onContextMenu
|
||||||
? (event) => onContextMenu(event, {
|
? (event) => onContextMenu(event, {
|
||||||
kind: "directory",
|
kind: "directory",
|
||||||
@@ -200,6 +221,7 @@ function DirectoryBranch({
|
|||||||
</span>
|
</span>
|
||||||
<Icon name="folder" size={17} />
|
<Icon name="folder" size={17} />
|
||||||
<strong title={directory.path}>{directory.name}</strong>
|
<strong title={directory.path}>{directory.name}</strong>
|
||||||
|
{childrenLoading && <span className="loading-spinner" />}
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<WorkspaceTreeItems
|
<WorkspaceTreeItems
|
||||||
@@ -210,6 +232,9 @@ function DirectoryBranch({
|
|||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
|
expandedPaths={expandedPaths}
|
||||||
|
onToggle={onToggle}
|
||||||
|
loadingChildrenPaths={loadingChildrenPaths}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ type State = {
|
|||||||
previewError: string | null;
|
previewError: string | null;
|
||||||
|
|
||||||
pythonEditorBuffers: Record<string, PythonEditorBuffer>;
|
pythonEditorBuffers: Record<string, PythonEditorBuffer>;
|
||||||
|
expandedPaths: Set<string>;
|
||||||
|
loadingChildrenPaths: Set<string>;
|
||||||
|
loadedChildPaths: Set<string>;
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
setApiOnline: (online: boolean) => void;
|
setApiOnline: (online: boolean) => void;
|
||||||
@@ -103,6 +106,8 @@ type State = {
|
|||||||
createFolder: (name: string, parentPath: string) => Promise<void>;
|
createFolder: (name: string, parentPath: string) => Promise<void>;
|
||||||
deleteScript: (script: ScriptItem) => Promise<void>;
|
deleteScript: (script: ScriptItem) => Promise<void>;
|
||||||
deleteDirectory: (path: string) => Promise<void>;
|
deleteDirectory: (path: string) => Promise<void>;
|
||||||
|
toggleExpanded: (path: string) => Promise<void>;
|
||||||
|
loadChildren: (parentPath: string) => Promise<void>;
|
||||||
toggleScriptLock: (script: ScriptItem) => Promise<void>;
|
toggleScriptLock: (script: ScriptItem) => Promise<void>;
|
||||||
openPublishDialog: (script: ScriptItem) => void;
|
openPublishDialog: (script: ScriptItem) => void;
|
||||||
submitPublish: (releaseNote: string, visibility: Visibility) => Promise<void>;
|
submitPublish: (releaseNote: string, visibility: Visibility) => Promise<void>;
|
||||||
@@ -166,6 +171,9 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
previewError: null,
|
previewError: null,
|
||||||
|
|
||||||
pythonEditorBuffers: {},
|
pythonEditorBuffers: {},
|
||||||
|
expandedPaths: new Set<string>(),
|
||||||
|
loadingChildrenPaths: new Set<string>(),
|
||||||
|
loadedChildPaths: new Set<string>(),
|
||||||
|
|
||||||
setApiOnline: (online) => set({ apiOnline: online }),
|
setApiOnline: (online) => set({ apiOnline: online }),
|
||||||
setKeyword: (keyword) => set({ keyword }),
|
setKeyword: (keyword) => set({ keyword }),
|
||||||
@@ -197,6 +205,9 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
previewLoading: false,
|
previewLoading: false,
|
||||||
previewError: null,
|
previewError: null,
|
||||||
pythonEditorBuffers: {},
|
pythonEditorBuffers: {},
|
||||||
|
expandedPaths: new Set<string>(),
|
||||||
|
loadingChildrenPaths: new Set<string>(),
|
||||||
|
loadedChildPaths: new Set<string>(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -207,12 +218,15 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
try {
|
try {
|
||||||
const [items, folderItems] = await Promise.all([
|
const [items, folderItems] = await Promise.all([
|
||||||
api.listScripts(),
|
api.listScripts(),
|
||||||
api.listWorkspaceDirectories(),
|
api.listWorkspaceDirectories(""),
|
||||||
]);
|
]);
|
||||||
|
const nextLoaded = new Set(get().loadedChildPaths);
|
||||||
|
nextLoaded.add("");
|
||||||
set({
|
set({
|
||||||
scripts: items,
|
scripts: items,
|
||||||
directories: folderItems,
|
directories: folderItems,
|
||||||
apiOnline: true,
|
apiOnline: true,
|
||||||
|
loadedChildPaths: nextLoaded,
|
||||||
});
|
});
|
||||||
const validIds = new Set(items.map((item) => item.script_id));
|
const validIds = new Set(items.map((item) => item.script_id));
|
||||||
const currentSelected = get().selectedId;
|
const currentSelected = get().selectedId;
|
||||||
@@ -236,6 +250,56 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
loadChildren: async (parentPath) => {
|
||||||
|
const api = requireApi();
|
||||||
|
if (get().loadedChildPaths.has(parentPath)) return;
|
||||||
|
const next = new Set(get().loadingChildrenPaths);
|
||||||
|
next.add(parentPath);
|
||||||
|
set({ loadingChildrenPaths: next });
|
||||||
|
try {
|
||||||
|
const children = await api.listWorkspaceDirectories(parentPath);
|
||||||
|
set((state) => {
|
||||||
|
const nextLoaded = new Set(state.loadedChildPaths);
|
||||||
|
nextLoaded.add(parentPath);
|
||||||
|
const trimmed = state.directories.filter(
|
||||||
|
(d) => d.parent_path !== parentPath,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
directories: [...trimmed, ...children],
|
||||||
|
loadedChildPaths: nextLoaded,
|
||||||
|
loadingChildrenPaths: new Set(
|
||||||
|
[...state.loadingChildrenPaths].filter((p) => p !== parentPath),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
set((state) => ({
|
||||||
|
loadingChildrenPaths: new Set(
|
||||||
|
[...state.loadingChildrenPaths].filter((p) => p !== parentPath),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
pushToast(
|
||||||
|
"error",
|
||||||
|
error instanceof Error ? error.message : "目录加载失败",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleExpanded: async (path) => {
|
||||||
|
const state = get();
|
||||||
|
const isOpen = state.expandedPaths.has(path);
|
||||||
|
const next = new Set(state.expandedPaths);
|
||||||
|
if (isOpen) {
|
||||||
|
next.delete(path);
|
||||||
|
} else {
|
||||||
|
next.add(path);
|
||||||
|
if (!state.loadedChildPaths.has(path)) {
|
||||||
|
void get().loadChildren(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set({ expandedPaths: next });
|
||||||
|
},
|
||||||
|
|
||||||
selectScript: (id) => {
|
selectScript: (id) => {
|
||||||
_selectedId = id;
|
_selectedId = id;
|
||||||
set({ selectedId: id });
|
set({ selectedId: id });
|
||||||
@@ -712,7 +776,25 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
ui.setFolderBusy(true);
|
ui.setFolderBusy(true);
|
||||||
try {
|
try {
|
||||||
await api.createWorkspaceDirectory(trimmed, parentPath);
|
await api.createWorkspaceDirectory(trimmed, parentPath);
|
||||||
await get().load(true);
|
if (parentPath === "") {
|
||||||
|
await get().load(true);
|
||||||
|
} else {
|
||||||
|
set((state) => {
|
||||||
|
const nextLoaded = new Set(state.loadedChildPaths);
|
||||||
|
for (const p of state.loadedChildPaths) {
|
||||||
|
if (p.startsWith(`${parentPath}/`)) nextLoaded.delete(p);
|
||||||
|
}
|
||||||
|
nextLoaded.delete(parentPath);
|
||||||
|
return {
|
||||||
|
loadedChildPaths: nextLoaded,
|
||||||
|
directories: state.directories.filter(
|
||||||
|
(d) => !d.parent_path.startsWith(`${parentPath}/`),
|
||||||
|
),
|
||||||
|
expandedPaths: new Set(state.expandedPaths),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
await get().loadChildren(parentPath);
|
||||||
|
}
|
||||||
ui.closeFolderDialog();
|
ui.closeFolderDialog();
|
||||||
pushToast("success", `${trimmed} 文件夹已创建`);
|
pushToast("success", `${trimmed} 文件夹已创建`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -779,6 +861,9 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
await get().endEditing(false, false);
|
await get().endEditing(false, false);
|
||||||
if (_editSession?.script_id === activeScript.script_id) return;
|
if (_editSession?.script_id === activeScript.script_id) return;
|
||||||
}
|
}
|
||||||
|
const parentPath = path.includes("/")
|
||||||
|
? path.split("/").slice(0, -1).join("/")
|
||||||
|
: "";
|
||||||
try {
|
try {
|
||||||
const result = await api.deleteWorkspaceDirectory(path);
|
const result = await api.deleteWorkspaceDirectory(path);
|
||||||
const selectedScript = get().scripts.find(
|
const selectedScript = get().scripts.find(
|
||||||
@@ -790,7 +875,28 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
|
|||||||
) {
|
) {
|
||||||
get().selectScript(null);
|
get().selectScript(null);
|
||||||
}
|
}
|
||||||
await get().load(true);
|
set((state) => {
|
||||||
|
const nextLoaded = new Set(state.loadedChildPaths);
|
||||||
|
const nextExpanded = new Set(state.expandedPaths);
|
||||||
|
for (const p of state.loadedChildPaths) {
|
||||||
|
if (p === path || p.startsWith(`${path}/`)) nextLoaded.delete(p);
|
||||||
|
}
|
||||||
|
for (const p of state.expandedPaths) {
|
||||||
|
if (p === path || p.startsWith(`${path}/`)) nextExpanded.delete(p);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
loadedChildPaths: nextLoaded,
|
||||||
|
expandedPaths: nextExpanded,
|
||||||
|
directories: state.directories.filter(
|
||||||
|
(d) => d.parent_path !== path && !d.parent_path.startsWith(`${path}/`),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (parentPath === "") {
|
||||||
|
await get().load(true);
|
||||||
|
} else {
|
||||||
|
await get().loadChildren(parentPath);
|
||||||
|
}
|
||||||
pushToast(
|
pushToast(
|
||||||
"success",
|
"success",
|
||||||
`${path} 已删除(含 ${result.deleted_scripts} 个脚本)`,
|
`${path} 已删除(含 ${result.deleted_scripts} 个脚本)`,
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ export type WorkspaceDirectory = {
|
|||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name: string;
|
||||||
parent_path: string;
|
parent_path: string;
|
||||||
|
has_children?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ApiEnvelope<T> = {
|
type ApiEnvelope<T> = {
|
||||||
@@ -443,9 +444,13 @@ export async function deleteScript(
|
|||||||
|
|
||||||
export async function listWorkspaceDirectories(
|
export async function listWorkspaceDirectories(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
|
parentPath: string = "",
|
||||||
): Promise<WorkspaceDirectory[]> {
|
): Promise<WorkspaceDirectory[]> {
|
||||||
|
const query = parentPath
|
||||||
|
? `?parent_path=${encodeURIComponent(parentPath)}`
|
||||||
|
: "";
|
||||||
const data = await apiRequest<{ directories: WorkspaceDirectory[] }>(
|
const data = await apiRequest<{ directories: WorkspaceDirectory[] }>(
|
||||||
"/api/v1/workspace-tree",
|
`/api/v1/workspace-directories${query}`,
|
||||||
{},
|
{},
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
@@ -1325,9 +1330,9 @@ export type WorkspaceBoundApi = {
|
|||||||
setScriptLock: (
|
setScriptLock: (
|
||||||
scriptId: string,
|
scriptId: string,
|
||||||
isLocked: boolean,
|
isLocked: boolean,
|
||||||
) => Promise<ScriptItem>;
|
) => Promise<ScriptItem>;
|
||||||
listWorkspaceDirectories: () => Promise<WorkspaceDirectory[]>;
|
listWorkspaceDirectories: (parentPath?: string) => Promise<WorkspaceDirectory[]>;
|
||||||
createWorkspaceDirectory: (
|
createWorkspaceDirectory: (
|
||||||
directoryName: string,
|
directoryName: string,
|
||||||
parentPath?: string,
|
parentPath?: string,
|
||||||
) => Promise<WorkspaceDirectory>;
|
) => Promise<WorkspaceDirectory>;
|
||||||
|
|||||||
Reference in New Issue
Block a user