Graduate file editing from experimental (#599)

This commit is contained in:
Will Chen
2025-07-08 11:39:46 -07:00
committed by GitHub
parent dfdd267f53
commit a1aee5c2b8
9 changed files with 6041 additions and 42 deletions

View File

@@ -2,11 +2,19 @@ import React, { useState, useRef, useEffect } from "react";
import Editor, { OnMount } from "@monaco-editor/react";
import { useLoadAppFile } from "@/hooks/useLoadAppFile";
import { useTheme } from "@/contexts/ThemeContext";
import { ChevronRight, Circle } from "lucide-react";
import { ChevronRight, Circle, Save } from "lucide-react";
import "@/components/chat/monaco";
import { IpcClient } from "@/ipc/ipc_client";
import { showError, showSuccess, showWarning } from "@/lib/toast";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useQueryClient } from "@tanstack/react-query";
import { useSettings } from "@/hooks/useSettings";
import { showError } from "@/lib/toast";
import { useCheckProblems } from "@/hooks/useCheckProblems";
interface FileEditorProps {
appId: number | null;
@@ -16,9 +24,16 @@ interface FileEditorProps {
interface BreadcrumbProps {
path: string;
hasUnsavedChanges: boolean;
onSave: () => void;
isSaving: boolean;
}
const Breadcrumb: React.FC<BreadcrumbProps> = ({ path, hasUnsavedChanges }) => {
const Breadcrumb: React.FC<BreadcrumbProps> = ({
path,
hasUnsavedChanges,
onSave,
isSaving,
}) => {
const segments = path.split("/").filter(Boolean);
return (
@@ -39,7 +54,24 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({ path, hasUnsavedChanges }) => {
</React.Fragment>
))}
</div>
<div className="flex-shrink-0 ml-2">
<div className="flex items-center gap-2 flex-shrink-0 ml-2">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="sm"
onClick={onSave}
disabled={!hasUnsavedChanges || isSaving}
className="h-6 w-6 p-0"
data-testid="save-file-button"
>
<Save size={12} />
</Button>
</TooltipTrigger>
<TooltipContent>
{hasUnsavedChanges ? "Save changes" : "No unsaved changes"}
</TooltipContent>
</Tooltip>
{hasUnsavedChanges && (
<Circle
size={8}
@@ -56,10 +88,10 @@ const Breadcrumb: React.FC<BreadcrumbProps> = ({ path, hasUnsavedChanges }) => {
export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
const { content, loading, error } = useLoadAppFile(appId, filePath);
const { theme } = useTheme();
const { settings } = useSettings();
const [value, setValue] = useState<string | undefined>(undefined);
const [displayUnsavedChanges, setDisplayUnsavedChanges] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const { settings } = useSettings();
// Use refs for values that need to be current in event handlers
const originalValueRef = useRef<string | undefined>(undefined);
const editorRef = useRef<any>(null);
@@ -67,6 +99,9 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
const needsSaveRef = useRef<boolean>(false);
const currentValueRef = useRef<string | undefined>(undefined);
const queryClient = useQueryClient();
const { checkProblems } = useCheckProblems(appId);
// Update state when content loads
useEffect(() => {
if (content !== null) {
@@ -75,6 +110,7 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
currentValueRef.current = content;
needsSaveRef.current = false;
setDisplayUnsavedChanges(false);
setIsSaving(false);
}
}, [content, filePath]);
@@ -125,9 +161,23 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
try {
isSavingRef.current = true;
setIsSaving(true);
const ipcClient = IpcClient.getInstance();
await ipcClient.editAppFile(appId, filePath, currentValueRef.current);
const { warning } = await ipcClient.editAppFile(
appId,
filePath,
currentValueRef.current,
);
await queryClient.invalidateQueries({ queryKey: ["versions", appId] });
if (settings?.enableAutoFixProblems) {
checkProblems();
}
if (warning) {
showWarning(warning);
} else {
showSuccess("File saved");
}
originalValueRef.current = currentValueRef.current;
needsSaveRef.current = false;
@@ -136,6 +186,7 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
showError(error);
} finally {
isSavingRef.current = false;
setIsSaving(false);
}
};
@@ -182,7 +233,12 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
return (
<div className="h-full flex flex-col">
<Breadcrumb path={filePath} hasUnsavedChanges={displayUnsavedChanges} />
<Breadcrumb
path={filePath}
hasUnsavedChanges={displayUnsavedChanges}
onSave={saveFile}
isSaving={isSaving}
/>
<div className="flex-1 overflow-hidden">
<Editor
height="100%"
@@ -199,7 +255,6 @@ export const FileEditor = ({ appId, filePath }: FileEditorProps) => {
fontFamily: "monospace",
fontSize: 13,
lineNumbers: "on",
readOnly: !settings?.experiments?.enableFileEditing,
}}
/>
</div>