(
+
+);
+
+const ModalHeader = () => (
+
+
+
Configure .goosehints
+
+
+);
+
+const ModalHelpText = () => (
+
+
+ .goosehints is a text file used to provide additional context about your project and improve
+ the communication with Goose.
+
+
You'll need to restart your session for .goosehints updates to take effect.
+
+ See{' '}
+ {' '}
+ for more information.
+
+
+);
+
+const ModalError = ({ error }) => (
+
+
Error reading .goosehints file: {JSON.stringify(error)}
+
+);
+
+const ModalFileInfo = ({ filePath, found }) => (
+
+ {found ? (
+
+ .goosehints file found at: {filePath}
+
+ ) : (
+
Creating new .goosehints file at: {filePath}
+ )}
+
+);
+
+const ModalButtons = ({ onSubmit, onCancel }) => (
+
+
+
+
+);
+
+const getGoosehintsFile = async (filePath) => await window.electron.readFile(filePath);
+
+type GoosehintsModalProps = {
+ directory: string;
+ setIsGoosehintsModalOpen: (isOpen: boolean) => void;
+};
+const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
+ const goosehintsFilePath = `${directory}/.goosehints`;
+ const [goosehintsFile, setGoosehintsFile] = useState
(null);
+ const [goosehintsFileFound, setGoosehintsFileFound] = useState(false);
+ const [goosehintsFileReadError, setGoosehintsFileReadError] = useState(null);
+
+ useEffect(() => {
+ const fetchGoosehintsFile = async () => {
+ try {
+ const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
+ setGoosehintsFile(file);
+ setGoosehintsFileFound(found);
+ setGoosehintsFileReadError(error);
+ } catch (error) {
+ console.error('Error fetching .goosehints file:', error);
+ }
+ };
+ if (directory) fetchGoosehintsFile();
+ }, [directory, goosehintsFilePath]);
+
+ const writeFile = async () => {
+ await window.electron.writeFile(goosehintsFilePath, goosehintsFile);
+ setIsGoosehintsModalOpen(false);
+ };
+
+ return (
+
+
+
+
+ {goosehintsFileReadError ? (
+
+ ) : (
+
+
+
+ )}
+
+ setIsGoosehintsModalOpen(false)} />
+
+ );
+};
+
+export const ConfigureGooseHints = ({ directory }: { directory: string }) => {
+ const [isGooseHintsModalOpen, setIsGoosehintsModalOpen] = useState(false);
+ return (
+
+ setIsGoosehintsModalOpen(true)}
+ >
+ Configure .goosehints
+
+
+ {isGooseHintsModalOpen ? (
+
+ ) : null}
+
+ );
+};
diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts
index ac52514b..71c72b1c 100644
--- a/ui/desktop/src/main.ts
+++ b/ui/desktop/src/main.ts
@@ -623,6 +623,41 @@ app.whenReady().then(async () => {
spawn('xdg-open', [url]);
}
});
+
+ ipcMain.handle('read-file', (event, filePath) => {
+ return new Promise((resolve) => {
+ exec(`cat ${filePath}`, (error, stdout, stderr) => {
+ if (error) {
+ // File not found
+ resolve({ file: "", filePath, error: null, found: false });
+ }
+ if (stderr) {
+ console.error('Error output:', stderr);
+ resolve({ file: "", filePath, error, found: false });
+ }
+ resolve({ file: stdout, filePath, error: null, found: true });
+ });
+ })
+ })
+
+ ipcMain.handle('write-file', (event, filePath, content) => {
+ return new Promise((resolve) => {
+ const command = `cat << 'EOT' > ${filePath}
+${content}
+EOT`;
+ exec(command, (error, stdout, stderr) => {
+ if (error) {
+ console.error('Error writing to file:', error);
+ resolve(false);
+ }
+ if (stderr) {
+ console.error('Error output:', stderr);
+ resolve(false);
+ }
+ resolve(true);
+ });
+ });
+ });
});
// Quit when all windows are closed, except on macOS.
diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts
index 943e27e6..446452ec 100644
--- a/ui/desktop/src/preload.ts
+++ b/ui/desktop/src/preload.ts
@@ -18,6 +18,8 @@ type ElectronAPI = {
startPowerSaveBlocker: () => Promise;
stopPowerSaveBlocker: () => Promise;
getBinaryPath: (binaryName: string) => Promise;
+ readFile: (directory: string) => Promise<{ file: string; filePath: string; error: string; found: boolean }>;
+ writeFile: (directory: string, content: string) => Promise;
on: (
channel: string,
callback: (event: Electron.IpcRendererEvent, ...args: any[]) => void
@@ -50,6 +52,8 @@ const electronAPI: ElectronAPI = {
startPowerSaveBlocker: () => ipcRenderer.invoke('start-power-save-blocker'),
stopPowerSaveBlocker: () => ipcRenderer.invoke('stop-power-save-blocker'),
getBinaryPath: (binaryName: string) => ipcRenderer.invoke('get-binary-path', binaryName),
+ readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath),
+ writeFile: (filePath: string, content: string) => ipcRenderer.invoke('write-file', filePath, content),
on: (channel: string, callback: (event: Electron.IpcRendererEvent, ...args: any[]) => void) => {
ipcRenderer.on(channel, callback);
},