gadget/gadget-code/frontend/src/components/FilesPanel.tsx
Rob Colbert 1e13f95808 Phase 2: ACE Editor integration and file operations
- Added react-ace and ace-builds dependencies
- Created EditorPanel component with ACE editor integration
- Implemented file read/write socket protocol
- Added backend handlers for fileReadRequest and fileWriteRequest
- Implemented file loading from tree click
- Implemented file saving with Ctrl+S shortcut
- Added dirty state tracking and unsaved changes indicator
- Enforced workspace mode (read-only in Agent mode)
- Added security: path traversal prevention, binary file detection, file size limits
- Updated FilesPanel with split view (tree + editor)

Enables Users to edit files for the first time in Gadget Code.
2026-05-12 19:32:58 -04:00

85 lines
2.8 KiB
TypeScript

import { useState } from "react";
import { WorkspaceMode } from "../lib/types";
import FileTree from "./FileTree";
import EditorPanel from "./EditorPanel";
interface FilesPanelProps {
workspaceMode: WorkspaceMode;
}
export default function FilesPanel({ workspaceMode }: FilesPanelProps) {
const [selectedFilePath, setSelectedFilePath] = useState<string | undefined>(undefined);
const isReadOnly = workspaceMode === WorkspaceMode.Agent;
const isReadWrite = workspaceMode === WorkspaceMode.User;
const handleFileSelect = (path: string) => {
setSelectedFilePath(path);
};
const handleCloseFile = () => {
setSelectedFilePath(undefined);
};
return (
<div className="border-t border-border-subtle flex flex-col flex-1 min-h-0">
<div className="flex items-center justify-between px-4 py-2 bg-bg-tertiary">
<h3 className="text-sm font-semibold text-text-secondary uppercase tracking-wider">
Files
</h3>
<div className="flex items-center gap-1">
<span
className={`w-6 h-6 flex items-center justify-center font-mono font-bold text-xs rounded border ${
isReadWrite
? "border-green-500 strobe text-green-500"
: "border-border-default text-text-muted"
}`}
title="Read / Write"
>
RW
</span>
<span
className={`w-6 h-6 flex items-center justify-center font-mono font-bold text-xs rounded border ${
isReadOnly
? "border-green-500 strobe text-green-500"
: "border-border-default text-text-muted"
}`}
title="Read Only"
>
RO
</span>
</div>
</div>
{/* Split view: File tree on left, editor on right */}
<div className="flex-1 flex overflow-hidden min-h-0">
{/* File Tree - 30% width, resizable in future */}
<div className="w-1/3 min-w-[200px] border-r border-border-subtle overflow-auto flex-shrink-0">
<FileTree
workspaceMode={workspaceMode}
onFileSelect={handleFileSelect}
/>
</div>
{/* Editor Panel - remaining space */}
<div className="flex-1 min-w-0">
<EditorPanel
workspaceMode={workspaceMode}
filePath={selectedFilePath}
onCloseFile={handleCloseFile}
/>
</div>
</div>
<div className="px-4 py-2 bg-bg-tertiary border-t border-border-subtle">
<p className="text-xs text-text-muted">
{isReadOnly
? "Read-only: Agent is working"
: isReadWrite
? "Read-write mode enabled"
: "Select User or Agent mode to access files"}
</p>
</div>
</div>
);
}