Skip to content

Commit f05d250

Browse files
committed
ignore fleetcode directory
1 parent 61d2def commit f05d250

File tree

3 files changed

+102
-14
lines changed

3 files changed

+102
-14
lines changed

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,4 @@ dist
138138
# Vite files
139139
vite.config.js.timestamp-*
140140
vite.config.ts.timestamp-*
141-
.vite/
142-
143-
.fleetcode/
141+
.vite/

main.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,44 @@ function getNextSessionNumber(): number {
4646
}
4747

4848
// Git worktree helper functions
49+
async function ensureFleetcodeExcluded(projectDir: string) {
50+
// Check if we've already initialized this project (persisted across app restarts)
51+
const initializedProjects: string[] = (store as any).get("excludeInitializedProjects", []);
52+
if (initializedProjects.includes(projectDir)) {
53+
return;
54+
}
55+
56+
const excludeFilePath = path.join(projectDir, ".git", "info", "exclude");
57+
const excludeEntry = ".fleetcode/";
58+
59+
try {
60+
// Ensure .git/info directory exists
61+
const infoDir = path.dirname(excludeFilePath);
62+
if (!fs.existsSync(infoDir)) {
63+
fs.mkdirSync(infoDir, { recursive: true });
64+
}
65+
66+
// Read existing exclude file or create empty string
67+
let excludeContent = "";
68+
if (fs.existsSync(excludeFilePath)) {
69+
excludeContent = fs.readFileSync(excludeFilePath, "utf-8");
70+
}
71+
72+
// Check if .fleetcode/ is already excluded
73+
if (!excludeContent.includes(excludeEntry)) {
74+
// Add .fleetcode/ to exclude file
75+
const newContent = excludeContent.trim() + (excludeContent.trim() ? "\n" : "") + excludeEntry + "\n";
76+
fs.writeFileSync(excludeFilePath, newContent, "utf-8");
77+
}
78+
79+
// Mark this project as initialized and persist
80+
initializedProjects.push(projectDir);
81+
(store as any).set("excludeInitializedProjects", initializedProjects);
82+
} catch (error) {
83+
console.error("Error ensuring .fleetcode excluded:", error);
84+
}
85+
}
86+
4987
async function createWorktree(projectDir: string, parentBranch: string, sessionNumber: number): Promise<string> {
5088
const git = simpleGit(projectDir);
5189
const fleetcodeDir = path.join(projectDir, ".fleetcode");
@@ -137,6 +175,9 @@ ipcMain.on("create-session", async (event, config: SessionConfig) => {
137175
const sessionId = `session-${Date.now()}`;
138176
const sessionName = `Session ${sessionNumber}`;
139177

178+
// Ensure .fleetcode is excluded (async, don't wait)
179+
ensureFleetcodeExcluded(config.projectDir);
180+
140181
// Create git worktree
141182
const worktreePath = await createWorktree(config.projectDir, config.parentBranch, sessionNumber);
142183

renderer.ts

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,58 @@ function createTerminalUI(sessionId: string) {
7676

7777
// Listen for bell character to mark unread activity
7878
term.onBell(() => {
79-
console.log(`Bell received for session ${sessionId}, activeSessionId: ${activeSessionId}`);
8079
if (activeSessionId !== sessionId) {
81-
console.log(`Marking session ${sessionId} as unread`);
8280
markSessionAsUnread(sessionId);
8381
}
8482
});
8583

86-
// Handle resize
84+
// Handle resize - only refit if dimensions actually changed
85+
let lastCols = term.cols;
86+
let lastRows = term.rows;
87+
let resizeTimeout: NodeJS.Timeout | null = null;
88+
8789
const resizeHandler = () => {
8890
if (activeSessionId === sessionId) {
89-
fitAddon.fit();
90-
ipcRenderer.send("session-resize", sessionId, term.cols, term.rows);
91+
// Clear any pending resize
92+
if (resizeTimeout) {
93+
clearTimeout(resizeTimeout);
94+
}
95+
96+
// Debounce the fit call
97+
resizeTimeout = setTimeout(() => {
98+
// Calculate what the new dimensions would be
99+
const container = sessionElement;
100+
if (!container) return;
101+
102+
const rect = container.getBoundingClientRect();
103+
const core = (term as any)._core;
104+
if (!core) return;
105+
106+
// Estimate new dimensions based on container size
107+
const newCols = Math.floor(rect.width / core._renderService.dimensions.actualCellWidth);
108+
const newRows = Math.floor(rect.height / core._renderService.dimensions.actualCellHeight);
109+
110+
// Only fit if dimensions actually changed significantly (more than 1 char difference)
111+
if (Math.abs(newCols - lastCols) > 1 || Math.abs(newRows - lastRows) > 1) {
112+
// Save scroll position before fitting
113+
const wasAtBottom = term.buffer.active.viewportY === term.buffer.active.baseY;
114+
const savedScrollPosition = term.buffer.active.viewportY;
115+
116+
fitAddon.fit();
117+
118+
lastCols = term.cols;
119+
lastRows = term.rows;
120+
121+
// Restore scroll position unless we were at the bottom (in which case stay at bottom)
122+
if (!wasAtBottom && savedScrollPosition !== term.buffer.active.viewportY) {
123+
term.scrollToLine(savedScrollPosition);
124+
}
125+
126+
ipcRenderer.send("session-resize", sessionId, term.cols, term.rows);
127+
}
128+
129+
resizeTimeout = null;
130+
}, 100); // 100ms debounce
91131
}
92132
};
93133
window.addEventListener("resize", resizeHandler);
@@ -375,7 +415,17 @@ function switchToSession(sessionId: string) {
375415
session.terminal.focus();
376416
setTimeout(() => {
377417
if (session.fitAddon && session.terminal) {
418+
// Save scroll position before fitting
419+
const wasAtBottom = session.terminal.buffer.active.viewportY === session.terminal.buffer.active.baseY;
420+
const savedScrollPosition = session.terminal.buffer.active.viewportY;
421+
378422
session.fitAddon.fit();
423+
424+
// Restore scroll position unless we were at the bottom
425+
if (!wasAtBottom && savedScrollPosition !== session.terminal.buffer.active.viewportY) {
426+
session.terminal.scrollToLine(savedScrollPosition);
427+
}
428+
379429
ipcRenderer.send("session-resize", sessionId, session.terminal.cols, session.terminal.rows);
380430
}
381431
}, 0);
@@ -464,28 +514,27 @@ const IDLE_DELAY_MS = 500; // 0.5 seconds of no output = Claude is done
464514
ipcRenderer.on("session-output", (_event, sessionId: string, data: string) => {
465515
const session = sessions.get(sessionId);
466516
if (session && session.terminal) {
467-
session.terminal.write(data);
517+
// Filter out [3J (clear scrollback) to prevent viewport resets during interactive menus
518+
// Keep [2J (clear screen) which is needed for the menu redraw
519+
const filteredData = data.replace(/\x1b\[3J/g, '');
520+
521+
session.terminal.write(filteredData);
468522

469523
// Only mark as unread if this is not the active session
470524
if (activeSessionId !== sessionId && session.hasActivePty) {
471-
console.log(`[Unread] Session ${sessionId} received output while inactive`);
472-
473525
// Clear any existing idle timer
474526
const existingTimer = sessionIdleTimers.get(sessionId);
475527
if (existingTimer) {
476-
console.log(`[Unread] Clearing existing idle timer for session ${sessionId}`);
477528
clearTimeout(existingTimer);
478529
}
479530

480531
// Set a new timer - if no output for IDLE_DELAY_MS, mark as unread
481532
const timer = setTimeout(() => {
482-
console.log(`[Unread] ✓ No output for ${IDLE_DELAY_MS}ms - Claude is done! Marking session ${sessionId} as unread`);
483533
markSessionAsUnread(sessionId);
484534
sessionIdleTimers.delete(sessionId);
485535
}, IDLE_DELAY_MS);
486536

487537
sessionIdleTimers.set(sessionId, timer);
488-
console.log(`[Unread] Set idle timer for session ${sessionId} - will trigger in ${IDLE_DELAY_MS}ms if no more output`);
489538
}
490539
}
491540
});

0 commit comments

Comments
 (0)