2025-12-24 17:46:27 +00:00
|
|
|
import { useState, useEffect } from "react";
|
2025-12-24 16:29:33 +00:00
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
2025-12-24 16:59:14 +00:00
|
|
|
import { open } from "@tauri-apps/plugin-dialog";
|
2025-12-24 17:17:35 +00:00
|
|
|
import { Chat } from "./components/Chat";
|
2025-12-24 16:29:33 +00:00
|
|
|
import "./App.css";
|
|
|
|
|
|
|
|
|
|
function App() {
|
2025-12-24 16:59:14 +00:00
|
|
|
const [projectPath, setProjectPath] = useState<string | null>(null);
|
|
|
|
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
2025-12-24 16:29:33 +00:00
|
|
|
|
2025-12-24 17:46:27 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
invoke<string | null>("get_current_project")
|
|
|
|
|
.then((path) => {
|
|
|
|
|
if (path) setProjectPath(path);
|
|
|
|
|
})
|
|
|
|
|
.catch((e) => console.error(e));
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
async function closeProject() {
|
|
|
|
|
try {
|
|
|
|
|
await invoke("close_project");
|
|
|
|
|
setProjectPath(null);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 16:59:14 +00:00
|
|
|
async function selectProject() {
|
|
|
|
|
try {
|
|
|
|
|
setErrorMsg(null);
|
|
|
|
|
// Open native folder picker
|
|
|
|
|
const selected = await open({
|
|
|
|
|
directory: true,
|
|
|
|
|
multiple: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (selected === null) {
|
|
|
|
|
// User cancelled selection
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Invoke backend command to verify and set state
|
|
|
|
|
// Note: invoke argument names must match Rust function args
|
|
|
|
|
const confirmedPath = await invoke<string>("open_project", {
|
|
|
|
|
path: selected,
|
|
|
|
|
});
|
|
|
|
|
setProjectPath(confirmedPath);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
setErrorMsg(
|
|
|
|
|
typeof e === "string" ? e : "An error occurred opening the project.",
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-12-24 16:29:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<main className="container">
|
2025-12-24 16:59:14 +00:00
|
|
|
<h1>AI Code Assistant</h1>
|
|
|
|
|
|
|
|
|
|
{!projectPath ? (
|
|
|
|
|
<div className="selection-screen">
|
|
|
|
|
<p>
|
|
|
|
|
Please select a project folder to start the Story-Driven Spec
|
|
|
|
|
Workflow.
|
|
|
|
|
</p>
|
|
|
|
|
<button onClick={selectProject}>Open Project Directory</button>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="workspace">
|
|
|
|
|
<div
|
|
|
|
|
className="toolbar"
|
|
|
|
|
style={{
|
|
|
|
|
padding: "10px",
|
|
|
|
|
background: "#f0f0f0",
|
|
|
|
|
borderRadius: "4px",
|
|
|
|
|
color: "#333",
|
2025-12-24 17:46:27 +00:00
|
|
|
display: "flex",
|
|
|
|
|
justifyContent: "space-between",
|
|
|
|
|
alignItems: "center",
|
2025-12-24 16:59:14 +00:00
|
|
|
}}
|
|
|
|
|
>
|
2025-12-24 17:46:27 +00:00
|
|
|
<span>
|
|
|
|
|
<strong>Active Project:</strong> {projectPath}
|
|
|
|
|
</span>
|
|
|
|
|
<button
|
|
|
|
|
onClick={closeProject}
|
|
|
|
|
style={{ padding: "5px 10px", fontSize: "0.9em" }}
|
|
|
|
|
>
|
|
|
|
|
Close
|
|
|
|
|
</button>
|
2025-12-24 16:59:14 +00:00
|
|
|
</div>
|
|
|
|
|
<hr style={{ margin: "20px 0" }} />
|
2025-12-24 17:17:35 +00:00
|
|
|
<Chat />
|
2025-12-24 16:59:14 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{errorMsg && (
|
|
|
|
|
<div className="error-message" style={{ marginTop: "20px" }}>
|
|
|
|
|
<p style={{ color: "red" }}>Error: {errorMsg}</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-12-24 16:29:33 +00:00
|
|
|
</main>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App;
|