From d6512d6c9714b2c29ad496a577721d1d41ac4838 Mon Sep 17 00:00:00 2001 From: nikita Date: Sun, 5 Apr 2026 04:57:16 +0300 Subject: [PATCH] feat: update button run scripts --- frontend/src/modules/ide/api/scripts.api.ts | 8 +++ .../src/modules/ide/components/FilePicker.tsx | 30 ++++++---- .../modules/ide/components/FilePickerItem.tsx | 57 +++++++++++-------- .../modules/ide/store/useFilePickerStore.ts | 17 ++++++ frontend/src/pages/templates.page.tsx | 13 ++++- 5 files changed, 87 insertions(+), 38 deletions(-) diff --git a/frontend/src/modules/ide/api/scripts.api.ts b/frontend/src/modules/ide/api/scripts.api.ts index 3f41a48..c71fdc4 100644 --- a/frontend/src/modules/ide/api/scripts.api.ts +++ b/frontend/src/modules/ide/api/scripts.api.ts @@ -83,4 +83,12 @@ export const scriptsApi = { ); return res.data; }, + + run: async (path: string): Promise<{ id: number; status: string }> => { + const res = await apiClient.post<{ id: number; status: string }>( + "/scripts/run", + { path }, + ); + return res.data; + }, }; diff --git a/frontend/src/modules/ide/components/FilePicker.tsx b/frontend/src/modules/ide/components/FilePicker.tsx index d7217d0..944f866 100644 --- a/frontend/src/modules/ide/components/FilePicker.tsx +++ b/frontend/src/modules/ide/components/FilePicker.tsx @@ -5,20 +5,21 @@ import { useFilePickerStore } from "../store/useFilePickerStore"; interface FilePickerProps { files: FileNode; + onRun?: (path: string) => void; } -const FilePickerTree: React.FC<{ node: FileNode; level: number }> = ({ - node, - level, -}) => { +const FilePickerTree: React.FC<{ + node: FileNode; + level: number; + onRun?: (path: string) => void; +}> = ({ node, level, onRun }) => { const expandedFolders = useFilePickerStore((s) => s.expandedFolders); - const selectedPaths = useFilePickerStore((s) => s.selectedPaths); - const toggleSelection = useFilePickerStore((s) => s.toggleSelection); + const runningScripts = useFilePickerStore((s) => s.runningScripts); const toggleFolder = useFilePickerStore((s) => s.toggleFolder); const nodePath = node.path || node.name; const isExpanded = expandedFolders.has(nodePath); - const isSelected = node.type === "file" && selectedPaths.has(nodePath); + const isRunning = node.type === "file" && runningScripts.has(nodePath); if (node.type === "file") { return ( @@ -26,9 +27,9 @@ const FilePickerTree: React.FC<{ node: FileNode; level: number }> = ({ name={node.name} type="file" path={nodePath} - isSelected={isSelected} + isSelected={isRunning} level={level} - onToggleSelect={toggleSelection} + onRun={onRun} /> ); } @@ -44,14 +45,19 @@ const FilePickerTree: React.FC<{ node: FileNode; level: number }> = ({ onToggleFolder={toggleFolder} > {node.children?.map((child, idx) => ( - + ))} ); }; -export const FilePicker: React.FC = ({ files }) => { +export const FilePicker: React.FC = ({ files, onRun }) => { return (
= ({ files }) => { }} > {(files.children || []).map((child, idx) => ( - + ))}
); diff --git a/frontend/src/modules/ide/components/FilePickerItem.tsx b/frontend/src/modules/ide/components/FilePickerItem.tsx index 22f5f5f..bf6479f 100644 --- a/frontend/src/modules/ide/components/FilePickerItem.tsx +++ b/frontend/src/modules/ide/components/FilePickerItem.tsx @@ -4,6 +4,7 @@ import { FiChevronDown, FiFile, FiFolder, + FiPlay, } from "react-icons/fi"; interface FilePickerItemProps { @@ -16,6 +17,7 @@ interface FilePickerItemProps { level: number; onToggleSelect?: (path: string) => void; onToggleFolder?: (path: string) => void; + onRun?: (path: string) => void; } export const FilePickerItem: React.FC = ({ @@ -28,6 +30,7 @@ export const FilePickerItem: React.FC = ({ level, onToggleSelect, onToggleFolder, + onRun, }) => { const isFolder = type === "folder"; const extension = name.includes(".") @@ -120,40 +123,48 @@ export const FilePickerItem: React.FC = ({ )} - {/* Checkbox — только у файлов */} - {!isFolder && onToggleSelect && ( -
{ e.stopPropagation(); - onToggleSelect(path); + onRun(path); }} + onMouseEnter={(e) => { + e.currentTarget.style.backgroundColor = "#238636"; + e.currentTarget.style.color = "#ffffff"; + e.currentTarget.style.borderColor = "#2ea043"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.backgroundColor = isSelected + ? "#238636" + : "transparent"; + e.currentTarget.style.color = isSelected + ? "#ffffff" + : "var(--text-secondary)"; + e.currentTarget.style.borderColor = isSelected + ? "#2ea043" + : "transparent"; + }} + title="Run script" > - {isSelected && ( - - - - )} -
+ + )} diff --git a/frontend/src/modules/ide/store/useFilePickerStore.ts b/frontend/src/modules/ide/store/useFilePickerStore.ts index e6b9e74..35badac 100644 --- a/frontend/src/modules/ide/store/useFilePickerStore.ts +++ b/frontend/src/modules/ide/store/useFilePickerStore.ts @@ -1,19 +1,23 @@ import { create } from "zustand"; +import { scriptsApi } from "../api/scripts.api"; interface FilePickerState { selectedPaths: Set; expandedFolders: Set; + runningScripts: Map; toggleSelection: (path: string) => void; selectAll: (paths: string[]) => void; clearSelection: () => void; toggleFolder: (path: string) => void; getSelectedPaths: () => string[]; + runScript: (path: string) => Promise; } export const useFilePickerStore = create((set, get) => ({ selectedPaths: new Set(), expandedFolders: new Set(), + runningScripts: new Map(), toggleSelection: (path: string) => { set((state) => { @@ -54,4 +58,17 @@ export const useFilePickerStore = create((set, get) => ({ getSelectedPaths: () => { return Array.from(get().selectedPaths); }, + + runScript: async (path: string) => { + try { + const result = await scriptsApi.run(path); + set((state) => { + const newMap = new Map(state.runningScripts); + newMap.set(path, result); + return { runningScripts: newMap }; + }); + } catch (e) { + console.error("Failed to run script:", e); + } + }, })); diff --git a/frontend/src/pages/templates.page.tsx b/frontend/src/pages/templates.page.tsx index ee95823..be9cf0e 100644 --- a/frontend/src/pages/templates.page.tsx +++ b/frontend/src/pages/templates.page.tsx @@ -41,6 +41,8 @@ const convertTreeToFileNode = (data: any[]): FileNode => { export const TemplatesPage = () => { const navigate = useNavigate(); const selectedPaths = useFilePickerStore((s) => s.selectedPaths); + const runningScripts = useFilePickerStore((s) => s.runningScripts); + const runScript = useFilePickerStore((s) => s.runScript); const [files, setFiles] = useState(null); const [loading, setLoading] = useState(true); @@ -57,6 +59,12 @@ export const TemplatesPage = () => { .finally(() => setLoading(false)); }, []); + const handleRun = async (path: string) => { + await runScript(path); + }; + + const runningCount = runningScripts.size; + return (
{ > - {selectedPaths.size} script{selectedPaths.size !== 1 ? "s" : ""}{" "} - running + {runningCount} script{runningCount !== 1 ? "s" : ""} running
@@ -145,7 +152,7 @@ export const TemplatesPage = () => { /> ) : files ? ( - + ) : null}