161 lines
4.4 KiB
TypeScript
161 lines
4.4 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { FiEdit3, FiPlay } from "react-icons/fi";
|
|
import { FaSpinner } from "react-icons/fa";
|
|
import { FilePicker, useFilePickerStore } from "../modules/ide";
|
|
import type { FileNode } from "../modules/ide";
|
|
import { scriptsApi } from "../modules/ide/api/scripts.api";
|
|
|
|
const convertTreeToFileNode = (data: any[]): FileNode => {
|
|
const convertItem = (item: any): FileNode => {
|
|
const node: FileNode = {
|
|
id: item.id,
|
|
name: item.name,
|
|
type: item.type === "folder" ? "folder" : "file",
|
|
content: item.content || "",
|
|
path: item.name,
|
|
interpreter_id: item.interpreter_id,
|
|
};
|
|
|
|
if (item.type === "folder") {
|
|
node.children = [];
|
|
if (item.children && Array.isArray(item.children)) {
|
|
node.children = item.children.map((child: any) => {
|
|
const childNode = convertItem(child);
|
|
childNode.path = `${item.name}/${child.name}`;
|
|
return childNode;
|
|
});
|
|
}
|
|
}
|
|
|
|
return node;
|
|
};
|
|
|
|
return {
|
|
name: "templates",
|
|
type: "folder",
|
|
children: data.map((item) => convertItem(item)),
|
|
};
|
|
};
|
|
|
|
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<FileNode | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
scriptsApi
|
|
.getTree()
|
|
.then((data) => {
|
|
setFiles(convertTreeToFileNode(data));
|
|
})
|
|
.catch((e) => {
|
|
console.error("Failed to load tree:", e);
|
|
setFiles({ name: "templates", type: "folder", children: [] });
|
|
})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
const handleRun = async (path: string) => {
|
|
await runScript(path);
|
|
};
|
|
|
|
const runningCount = runningScripts.size;
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
height: "100vh",
|
|
position: "relative",
|
|
backgroundColor: "var(--bg-primary)",
|
|
}}
|
|
>
|
|
{/* Floating header */}
|
|
<div
|
|
style={{
|
|
position: "absolute",
|
|
top: "16px",
|
|
right: "16px",
|
|
zIndex: 10,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: "16px",
|
|
}}
|
|
>
|
|
{/* Running scripts counter */}
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: "8px",
|
|
padding: "6px 12px",
|
|
backgroundColor: "var(--card-bg)",
|
|
borderRadius: "4px",
|
|
border: "1px solid var(--border)",
|
|
}}
|
|
>
|
|
<FiPlay size={13} color="#61c454" />
|
|
<span style={{ fontSize: "12px", color: "var(--text-secondary)" }}>
|
|
{runningCount} script{runningCount !== 1 ? "s" : ""} running
|
|
</span>
|
|
</div>
|
|
|
|
{/* Open in Editor button */}
|
|
<button
|
|
onClick={() => navigate("/ide")}
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: "8px",
|
|
padding: "6px 16px",
|
|
backgroundColor: "#0e639c",
|
|
border: "none",
|
|
borderRadius: "4px",
|
|
color: "#ffffff",
|
|
cursor: "pointer",
|
|
fontSize: "12px",
|
|
fontWeight: 500,
|
|
transition: "all 0.15s",
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.backgroundColor = "#1177bb";
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.backgroundColor = "#0e639c";
|
|
}}
|
|
>
|
|
<FiEdit3 size={14} />
|
|
Open Editor
|
|
</button>
|
|
</div>
|
|
|
|
{/* File Picker */}
|
|
<div style={{ height: "100%", overflow: "hidden" }}>
|
|
{loading ? (
|
|
<div
|
|
style={{
|
|
height: "100%",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<FaSpinner
|
|
size={24}
|
|
style={{
|
|
color: "var(--accent)",
|
|
animation: "spin 1s linear infinite",
|
|
}}
|
|
/>
|
|
</div>
|
|
) : files ? (
|
|
<FilePicker files={files} onRun={handleRun} />
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|