ignore fleetcode directory

This commit is contained in:
Amrit Subramanian 2025-10-03 15:12:46 -04:00
parent 61d2defd93
commit f05d2502c5
3 changed files with 102 additions and 14 deletions

4
.gitignore vendored
View file

@ -138,6 +138,4 @@ dist
# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/
.fleetcode/
.vite/

41
main.ts
View file

@ -46,6 +46,44 @@ function getNextSessionNumber(): number {
}
// Git worktree helper functions
async function ensureFleetcodeExcluded(projectDir: string) {
// Check if we've already initialized this project (persisted across app restarts)
const initializedProjects: string[] = (store as any).get("excludeInitializedProjects", []);
if (initializedProjects.includes(projectDir)) {
return;
}
const excludeFilePath = path.join(projectDir, ".git", "info", "exclude");
const excludeEntry = ".fleetcode/";
try {
// Ensure .git/info directory exists
const infoDir = path.dirname(excludeFilePath);
if (!fs.existsSync(infoDir)) {
fs.mkdirSync(infoDir, { recursive: true });
}
// Read existing exclude file or create empty string
let excludeContent = "";
if (fs.existsSync(excludeFilePath)) {
excludeContent = fs.readFileSync(excludeFilePath, "utf-8");
}
// Check if .fleetcode/ is already excluded
if (!excludeContent.includes(excludeEntry)) {
// Add .fleetcode/ to exclude file
const newContent = excludeContent.trim() + (excludeContent.trim() ? "\n" : "") + excludeEntry + "\n";
fs.writeFileSync(excludeFilePath, newContent, "utf-8");
}
// Mark this project as initialized and persist
initializedProjects.push(projectDir);
(store as any).set("excludeInitializedProjects", initializedProjects);
} catch (error) {
console.error("Error ensuring .fleetcode excluded:", error);
}
}
async function createWorktree(projectDir: string, parentBranch: string, sessionNumber: number): Promise<string> {
const git = simpleGit(projectDir);
const fleetcodeDir = path.join(projectDir, ".fleetcode");
@ -137,6 +175,9 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
const sessionId = `session-${Date.now()}`;
const sessionName = `Session ${sessionNumber}`;
// Ensure .fleetcode is excluded (async, don't wait)
ensureFleetcodeExcluded(config.projectDir);
// Create git worktree
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber);

View file

@ -76,18 +76,58 @@ function createTerminalUI(sessionId: string) {
// Listen for bell character to mark unread activity
term.onBell(() => {
console.log(`Bell received for session ${sessionId}, activeSessionId: ${activeSessionId}`);
if (activeSessionId !== sessionId) {
console.log(`Marking session ${sessionId} as unread`);
markSessionAsUnread(sessionId);
}
});
// Handle resize
// Handle resize - only refit if dimensions actually changed
let lastCols = term.cols;
let lastRows = term.rows;
let resizeTimeout: NodeJS.Timeout | null = null;
const resizeHandler = () => {
if (activeSessionId === sessionId) {
fitAddon.fit();
ipcRenderer.send("session-resize", sessionId, term.cols, term.rows);
// Clear any pending resize
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
// Debounce the fit call
resizeTimeout = setTimeout(() => {
// Calculate what the new dimensions would be
const container = sessionElement;
if (!container) return;
const rect = container.getBoundingClientRect();
const core = (term as any)._core;
if (!core) return;
// Estimate new dimensions based on container size
const newCols = Math.floor(rect.width / core._renderService.dimensions.actualCellWidth);
const newRows = Math.floor(rect.height / core._renderService.dimensions.actualCellHeight);
// Only fit if dimensions actually changed significantly (more than 1 char difference)
if (Math.abs(newCols - lastCols) > 1 || Math.abs(newRows - lastRows) > 1) {
// Save scroll position before fitting
const wasAtBottom = term.buffer.active.viewportY === term.buffer.active.baseY;
const savedScrollPosition = term.buffer.active.viewportY;
fitAddon.fit();
lastCols = term.cols;
lastRows = term.rows;
// Restore scroll position unless we were at the bottom (in which case stay at bottom)
if (!wasAtBottom && savedScrollPosition !== term.buffer.active.viewportY) {
term.scrollToLine(savedScrollPosition);
}
ipcRenderer.send("session-resize", sessionId, term.cols, term.rows);
}
resizeTimeout = null;
}, 100); // 100ms debounce
}
};
window.addEventListener("resize", resizeHandler);
@ -375,7 +415,17 @@ function switchToSession(sessionId: string) {
session.terminal.focus();
setTimeout(() => {
if (session.fitAddon && session.terminal) {
// Save scroll position before fitting
const wasAtBottom = session.terminal.buffer.active.viewportY === session.terminal.buffer.active.baseY;
const savedScrollPosition = session.terminal.buffer.active.viewportY;
session.fitAddon.fit();
// Restore scroll position unless we were at the bottom
if (!wasAtBottom && savedScrollPosition !== session.terminal.buffer.active.viewportY) {
session.terminal.scrollToLine(savedScrollPosition);
}
ipcRenderer.send("session-resize", sessionId, session.terminal.cols, session.terminal.rows);
}
}, 0);
@ -464,28 +514,27 @@ const IDLE_DELAY_MS = 500; // 0.5 seconds of no output = Claude is done
ipcRenderer.on("session-output", (_event, sessionId: string, data: string) => {
const session = sessions.get(sessionId);
if (session && session.terminal) {
session.terminal.write(data);
// Filter out [3J (clear scrollback) to prevent viewport resets during interactive menus
// Keep [2J (clear screen) which is needed for the menu redraw
const filteredData = data.replace(/\x1b\[3J/g, '');
session.terminal.write(filteredData);
// Only mark as unread if this is not the active session
if (activeSessionId !== sessionId && session.hasActivePty) {
console.log(`[Unread] Session ${sessionId} received output while inactive`);
// Clear any existing idle timer
const existingTimer = sessionIdleTimers.get(sessionId);
if (existingTimer) {
console.log(`[Unread] Clearing existing idle timer for session ${sessionId}`);
clearTimeout(existingTimer);
}
// Set a new timer - if no output for IDLE_DELAY_MS, mark as unread
const timer = setTimeout(() => {
console.log(`[Unread] ✓ No output for ${IDLE_DELAY_MS}ms - Claude is done! Marking session ${sessionId} as unread`);
markSessionAsUnread(sessionId);
sessionIdleTimers.delete(sessionId);
}, IDLE_DELAY_MS);
sessionIdleTimers.set(sessionId, timer);
console.log(`[Unread] Set idle timer for session ${sessionId} - will trigger in ${IDLE_DELAY_MS}ms if no more output`);
}
}
});