diff --git a/frontend/src/modules/ide/IDE.tsx b/frontend/src/modules/ide/IDE.tsx index 837d930..010ae06 100644 --- a/frontend/src/modules/ide/IDE.tsx +++ b/frontend/src/modules/ide/IDE.tsx @@ -65,6 +65,12 @@ export const IDE: React.FC = ({ const initialize = useIDEStore((state) => state.initialize); const isInitialized = useIDEStore((state) => state.isInitialized); const fetchTree = useIDEStore((state) => state.fetchTree); + const fetchInterpreters = useIDEStore((state) => state.fetchInterpreters); + + // Загружаем интерпретаторы при инициализации + useEffect(() => { + fetchInterpreters(); + }, []); // Обработка Ctrl+S useEffect(() => { diff --git a/frontend/src/modules/ide/api/scripts.api.ts b/frontend/src/modules/ide/api/scripts.api.ts index 91c6c3e..61d235b 100644 --- a/frontend/src/modules/ide/api/scripts.api.ts +++ b/frontend/src/modules/ide/api/scripts.api.ts @@ -1,4 +1,5 @@ import { apiClient } from "@/shared/api/axios.instance"; +import type { Interpreter } from "../types"; export interface ScriptNodeDto { id: number; @@ -32,6 +33,11 @@ export interface UpdateScriptPayload { // apiClient уже имеет интерсептор для Authorization header export const scriptsApi = { + getInterpreters: async (): Promise => { + const res = await apiClient.get("/scripts/interpreters"); + return res.data; + }, + getTree: async (): Promise => { const res = await apiClient.get("/scripts/tree"); return res.data; @@ -55,4 +61,15 @@ export const scriptsApi = { deleteScript: async (id: number): Promise => { await apiClient.delete(`/scripts/${id}`); }, + + createFolder: async (path: string): Promise<{ path: string }> => { + const res = await apiClient.post<{ path: string }>("/scripts/folder", { + path, + }); + return res.data; + }, + + deleteFolder: async (path: string): Promise => { + await apiClient.delete(`/scripts/folder`, { data: { path } }); + }, }; diff --git a/frontend/src/modules/ide/components/FileExplorer.tsx b/frontend/src/modules/ide/components/FileExplorer.tsx index 529dd40..17d3856 100644 --- a/frontend/src/modules/ide/components/FileExplorer.tsx +++ b/frontend/src/modules/ide/components/FileExplorer.tsx @@ -46,6 +46,10 @@ export const FileExplorer: React.FC = ({ const handleEmptyContextMenu = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); + // Загружаем интерпретаторы перед открытием меню + if (store.interpreters.length === 0) { + store.fetchInterpreters(); + } store.setContextMenu({ x: e.clientX, y: e.clientY, node: null }); }; @@ -55,6 +59,13 @@ export const FileExplorer: React.FC = ({ store.setContextMenu({ x: e.clientX, y: e.clientY, node }); }; + // Загружаем интерпретаторы при монтировании компонента + useEffect(() => { + if (store.interpreters.length === 0) { + store.fetchInterpreters(); + } + }, []); + const filteredFiles = store.searchQuery ? (files.children || []) .map((child) => filterTree(child, store.searchQuery)) @@ -320,8 +331,13 @@ export const FileExplorer: React.FC = ({ ? store.dialog.node.name : "" } - onConfirm={store.handleDialogConfirm} + onConfirm={(value, interpreterId) => { + store.handleDialogConfirm(value, interpreterId); + }} onCancel={() => store.setDialog(null)} + interpreters={ + store.dialog.type === "newFile" ? store.interpreters : undefined + } /> )} diff --git a/frontend/src/modules/ide/components/InputDialog.tsx b/frontend/src/modules/ide/components/InputDialog.tsx index 38270e9..3005c57 100644 --- a/frontend/src/modules/ide/components/InputDialog.tsx +++ b/frontend/src/modules/ide/components/InputDialog.tsx @@ -1,10 +1,12 @@ import React, { useState, useRef, useEffect } from "react"; +import type { Interpreter } from "../types"; interface InputDialogProps { title: string; initialValue?: string; - onConfirm: (value: string) => void; + onConfirm: (value: string, interpreterId?: number) => void; onCancel: () => void; + interpreters?: Interpreter[]; } export const InputDialog: React.FC = ({ @@ -12,8 +14,12 @@ export const InputDialog: React.FC = ({ initialValue = "", onConfirm, onCancel, + interpreters, }) => { const [value, setValue] = useState(initialValue); + const [interpreterId, setInterpreterId] = useState( + interpreters?.[0]?.id, + ); const inputRef = useRef(null); useEffect(() => { @@ -21,6 +27,8 @@ export const InputDialog: React.FC = ({ inputRef.current?.select(); }, []); + const showInterpreterDropdown = interpreters && interpreters.length > 0; + return (
= ({ {title}

- Enter a new name + Enter a name

= ({ value={value} onChange={(e) => setValue(e.target.value)} onKeyDown={(e) => - e.key === "Enter" && value.trim() && onConfirm(value.trim()) + e.key === "Enter" && + value.trim() && + onConfirm(value.trim(), interpreterId) } style={{ width: "100%", @@ -77,10 +87,48 @@ export const InputDialog: React.FC = ({ borderRadius: "6px", color: "#ccc", fontSize: "14px", - marginBottom: "20px", + marginBottom: showInterpreterDropdown ? "12px" : "20px", outline: "none", }} /> + + {/* Interpreter dropdown */} + {showInterpreterDropdown && ( +
+ + +
+ )} +
@@ -99,7 +147,9 @@ export const InputDialog: React.FC = ({ Cancel