Skip to content

Commit c330cad

Browse files
committed
Fix notes saving: multi-line selection, auto-refresh, and cache invalidation
- Support text drag selection for saving multiple lines (not just Shift+click) - Auto-refresh notes tab when saving while it's inactive - Invalidate backend file cache after appending to notes file - Clear cached lines when marking inactive tab for reload
1 parent c3a1ced commit c330cad

6 files changed

Lines changed: 315 additions & 37 deletions

File tree

src/main/index.ts

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -771,10 +771,71 @@ ipcMain.handle('save-selected-lines', async (_, startLine: number, endLine: numb
771771
});
772772

773773
// === Save to Notes ===
774-
// TODO: Simplify note file creation - append to single file instead of creating new file for each entry
775-
// TODO: Simplify naming convention for notes file
776774

777-
ipcMain.handle('save-to-notes', async (_, startLine: number, endLine: number, note?: string) => {
775+
// Helper: Read notes file header to get source log path
776+
function getNotesFileSource(notesFilePath: string): string | null {
777+
try {
778+
const content = fs.readFileSync(notesFilePath, 'utf-8');
779+
const lines = content.split('\n').slice(0, 10); // Read first 10 lines
780+
for (const line of lines) {
781+
if (line.startsWith('Source: ')) {
782+
return line.substring(8).trim();
783+
}
784+
}
785+
} catch {
786+
// File doesn't exist or can't be read
787+
}
788+
return null;
789+
}
790+
791+
// Find existing notes files for the current log file
792+
ipcMain.handle('find-notes-files', async () => {
793+
const handler = getFileHandler();
794+
if (!handler) return { success: false, error: 'No file open' };
795+
796+
const fileInfo = handler.getFileInfo();
797+
if (!fileInfo) return { success: false, error: 'No file info' };
798+
799+
const currentDir = path.dirname(fileInfo.path);
800+
const logFilePath = fileInfo.path;
801+
802+
try {
803+
const files = fs.readdirSync(currentDir);
804+
const notesFiles: Array<{ name: string; path: string; created: string }> = [];
805+
806+
for (const file of files) {
807+
if (file.endsWith('.notes.txt')) {
808+
const fullPath = path.join(currentDir, file);
809+
const source = getNotesFileSource(fullPath);
810+
811+
// Check if this notes file belongs to the current log file
812+
if (source === logFilePath) {
813+
// Get created date from header
814+
const content = fs.readFileSync(fullPath, 'utf-8');
815+
const createdMatch = content.match(/Created: (.+)/);
816+
notesFiles.push({
817+
name: file,
818+
path: fullPath,
819+
created: createdMatch ? createdMatch[1].trim() : 'Unknown',
820+
});
821+
}
822+
}
823+
}
824+
825+
return { success: true, files: notesFiles, logFilePath };
826+
} catch (error) {
827+
return { success: false, error: String(error) };
828+
}
829+
});
830+
831+
// Save to notes - creates new file or appends to existing
832+
ipcMain.handle('save-to-notes', async (
833+
_,
834+
startLine: number,
835+
endLine: number,
836+
note?: string,
837+
targetFilePath?: string // If provided, append to this file; otherwise create new
838+
) => {
778839
const handler = getFileHandler();
779840
if (!handler) return { success: false, error: 'No file open' };
780841

@@ -792,30 +853,72 @@ ipcMain.handle('save-to-notes', async (_, startLine: number, endLine: number, no
792853
if (!fileInfo) return { success: false, error: 'No file info' };
793854

794855
const currentDir = path.dirname(fileInfo.path);
795-
const originalFileName = path.basename(fileInfo.path, path.extname(fileInfo.path));
796-
const notesFileName = `notes_from-${originalFileName}.log`;
797-
const notesFilePath = path.join(currentDir, notesFileName);
798-
799-
// Build the note entry
800856
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
801857
const separator = '='.repeat(80);
802-
const noteText = note ? `Note: ${note}` : 'Note: (no description)';
803-
const lineInfo = `Lines: ${startLine + 1}-${endLine + 1} | Saved: ${timestamp}`;
804858

805-
const entry = [
859+
let notesFilePath: string;
860+
let isNewFile = false;
861+
862+
if (targetFilePath && fs.existsSync(targetFilePath)) {
863+
// Append to existing file
864+
notesFilePath = targetFilePath;
865+
} else {
866+
// Create new file with unique name
867+
const originalFileName = path.basename(fileInfo.path, path.extname(fileInfo.path));
868+
const dateStr = new Date().toISOString().substring(0, 10).replace(/-/g, '');
869+
let counter = 1;
870+
let notesFileName = `${originalFileName}_${dateStr}.notes.txt`;
871+
notesFilePath = path.join(currentDir, notesFileName);
872+
873+
// Find unique filename if exists
874+
while (fs.existsSync(notesFilePath)) {
875+
counter++;
876+
notesFileName = `${originalFileName}_${dateStr}_${counter}.notes.txt`;
877+
notesFilePath = path.join(currentDir, notesFileName);
878+
}
879+
isNewFile = true;
880+
}
881+
882+
// Build content
883+
let content = '';
884+
885+
if (isNewFile) {
886+
// Add header for new file
887+
content = [
888+
separator,
889+
'LOGAN Notes',
890+
`Source: ${fileInfo.path}`,
891+
`Created: ${timestamp}`,
892+
separator,
893+
'',
894+
].join('\n');
895+
}
896+
897+
// Add the note entry
898+
const noteDesc = note ? ` | ${note}` : '';
899+
content += [
806900
'',
807-
separator,
808-
noteText,
809-
lineInfo,
810-
separator,
901+
`--- [${timestamp}] Lines ${startLine + 1}-${endLine + 1}${noteDesc} ---`,
811902
...lines.map(l => l.text),
812-
''
903+
'',
813904
].join('\n');
814905

815-
// Append to notes file (create if doesn't exist)
816-
fs.appendFileSync(notesFilePath, entry, 'utf-8');
906+
// Write or append
907+
if (isNewFile) {
908+
fs.writeFileSync(notesFilePath, content, 'utf-8');
909+
} else {
910+
fs.appendFileSync(notesFilePath, content, 'utf-8');
911+
}
817912

818-
return { success: true, filePath: notesFilePath, lineCount: lines.length };
913+
// Invalidate cache for this file so it gets re-indexed with new content
914+
fileHandlerCache.delete(notesFilePath);
915+
916+
return {
917+
success: true,
918+
filePath: notesFilePath,
919+
lineCount: lines.length,
920+
isNewFile,
921+
};
819922
} catch (error) {
820923
return { success: false, error: String(error) };
821924
}

src/preload/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ const api = {
8888
ipcRenderer.invoke('save-selected-lines', startLine, endLine),
8989

9090
// Save to notes file
91-
saveToNotes: (startLine: number, endLine: number, note?: string): Promise<{ success: boolean; filePath?: string; lineCount?: number; error?: string }> =>
92-
ipcRenderer.invoke('save-to-notes', startLine, endLine, note),
91+
findNotesFiles: (): Promise<{ success: boolean; files?: Array<{ name: string; path: string; created: string }>; logFilePath?: string; error?: string }> =>
92+
ipcRenderer.invoke('find-notes-files'),
93+
94+
saveToNotes: (startLine: number, endLine: number, note?: string, targetFilePath?: string): Promise<{ success: boolean; filePath?: string; lineCount?: number; isNewFile?: boolean; error?: string }> =>
95+
ipcRenderer.invoke('save-to-notes', startLine, endLine, note, targetFilePath),
9396

9497
// Split file
9598
splitFile: (options: { mode: 'lines' | 'parts'; value: number }): Promise<{ success: boolean; outputDir?: string; files?: string[]; partCount?: number; error?: string }> =>

src/renderer/index.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,18 @@ <h3 id="bookmark-modal-title">Add Bookmark</h3>
454454

455455
<!-- Save to Notes Modal -->
456456
<div id="notes-modal" class="modal hidden">
457-
<div class="modal-content" style="width: 450px;">
457+
<div class="modal-content" style="width: 500px;">
458458
<div class="modal-header">
459459
<h3>Save to Notes</h3>
460460
<button class="modal-close" data-modal="notes-modal">&times;</button>
461461
</div>
462462
<div class="modal-body">
463+
<div class="filter-group" id="notes-file-group">
464+
<label>Notes File</label>
465+
<div id="notes-file-options" class="radio-group">
466+
<!-- Will be populated dynamically -->
467+
</div>
468+
</div>
463469
<div class="filter-group">
464470
<label for="notes-description">Description (optional)</label>
465471
<input type="text" id="notes-description" class="modal-input" placeholder="e.g., Memory leak at startup, Connection timeout...">

0 commit comments

Comments
 (0)