Skip to content

Commit 2fecb47

Browse files
ozgesolidkeyclaude
andcommitted
Add Export All results and multi-search sessions for search configs
Export All: one-click export of all active search config results into a grouped .txt file with header metadata. Multi-search sessions: save/load named groups of search configs (like highlight groups) with global or per-file scope, including session chips UI with apply, rename, and delete. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 81d9bcd commit 2fecb47

7 files changed

Lines changed: 1130 additions & 1278 deletions

File tree

src/main/index.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as os from 'os';
55
import { spawn } from 'child_process';
66
import * as pty from 'node-pty';
77
import { FileHandler, filterLineToVisibleColumns, ColumnConfig } from './fileHandler';
8-
import { IPC, SearchOptions, Bookmark, Highlight, HighlightGroup, SearchConfig, ActivityEntry, LocalFileData } from '../shared/types';
8+
import { IPC, SearchOptions, Bookmark, Highlight, HighlightGroup, SearchConfig, SearchConfigSession, ActivityEntry, LocalFileData } from '../shared/types';
99
import * as Diff from 'diff';
1010
import { analyzerRegistry, AnalyzerOptions } from './analyzers';
1111
import { loadDatadogConfig, saveDatadogConfig, clearDatadogConfig, fetchDatadogLogs, DatadogConfig, DatadogFetchParams } from './datadogClient';
@@ -2141,6 +2141,98 @@ ipcMain.handle(IPC.SEARCH_CONFIG_EXPORT, async (_, configId: string, lines: stri
21412141
}
21422142
});
21432143

2144+
ipcMain.handle(IPC.SEARCH_CONFIG_EXPORT_ALL, async (_, content: string) => {
2145+
if (!currentFilePath) return { success: false, error: 'No file open' };
2146+
2147+
try {
2148+
const baseName = path.basename(currentFilePath, path.extname(currentFilePath));
2149+
const date = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
2150+
const exportName = `${baseName}_multi-search_${date}.txt`;
2151+
const exportPath = path.join(path.dirname(currentFilePath), exportName);
2152+
fs.writeFileSync(exportPath, content, 'utf-8');
2153+
return { success: true, filePath: exportPath };
2154+
} catch (error) {
2155+
return { success: false, error: String(error) };
2156+
}
2157+
});
2158+
2159+
// === Search Config Sessions ===
2160+
2161+
const getSearchConfigSessionsPath = () => path.join(getConfigDir(), 'search-config-sessions.json');
2162+
2163+
function loadGlobalSearchConfigSessions(): SearchConfigSession[] {
2164+
try {
2165+
const filePath = getSearchConfigSessionsPath();
2166+
if (fs.existsSync(filePath)) {
2167+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
2168+
}
2169+
} catch { /* ignore */ }
2170+
return [];
2171+
}
2172+
2173+
function saveGlobalSearchConfigSessions(sessions: SearchConfigSession[]): void {
2174+
ensureConfigDir();
2175+
fs.writeFileSync(getSearchConfigSessionsPath(), JSON.stringify(sessions, null, 2), 'utf-8');
2176+
}
2177+
2178+
function loadLocalSearchConfigSessions(filePath: string): SearchConfigSession[] {
2179+
try {
2180+
const data = loadLocalFileData(filePath);
2181+
return (data as any).searchConfigSessions || [];
2182+
} catch { /* ignore */ }
2183+
return [];
2184+
}
2185+
2186+
function saveLocalSearchConfigSessions(filePath: string, sessions: SearchConfigSession[]): void {
2187+
const data = loadLocalFileData(filePath);
2188+
(data as any).searchConfigSessions = sessions;
2189+
saveLocalFileData(filePath, data);
2190+
}
2191+
2192+
ipcMain.handle(IPC.SEARCH_CONFIG_SESSION_LIST, async () => {
2193+
const globalSessions = loadGlobalSearchConfigSessions().map(s => ({ ...s, isGlobal: true }));
2194+
const localSessions = currentFilePath
2195+
? loadLocalSearchConfigSessions(currentFilePath).map(s => ({ ...s, isGlobal: false }))
2196+
: [];
2197+
return { success: true, sessions: [...globalSessions, ...localSessions] };
2198+
});
2199+
2200+
ipcMain.handle(IPC.SEARCH_CONFIG_SESSION_SAVE, async (_, session: SearchConfigSession) => {
2201+
if (session.isGlobal) {
2202+
const sessions = loadGlobalSearchConfigSessions();
2203+
const existingIdx = sessions.findIndex(s => s.id === session.id);
2204+
if (existingIdx >= 0) {
2205+
sessions[existingIdx] = session;
2206+
} else {
2207+
sessions.push(session);
2208+
}
2209+
saveGlobalSearchConfigSessions(sessions);
2210+
} else {
2211+
if (!currentFilePath) return { success: false, error: 'No file open' };
2212+
const sessions = loadLocalSearchConfigSessions(currentFilePath);
2213+
const existingIdx = sessions.findIndex(s => s.id === session.id);
2214+
if (existingIdx >= 0) {
2215+
sessions[existingIdx] = session;
2216+
} else {
2217+
sessions.push(session);
2218+
}
2219+
saveLocalSearchConfigSessions(currentFilePath, sessions);
2220+
}
2221+
return { success: true };
2222+
});
2223+
2224+
ipcMain.handle(IPC.SEARCH_CONFIG_SESSION_DELETE, async (_, sessionId: string, isGlobal: boolean) => {
2225+
if (isGlobal) {
2226+
const sessions = loadGlobalSearchConfigSessions().filter(s => s.id !== sessionId);
2227+
saveGlobalSearchConfigSessions(sessions);
2228+
} else {
2229+
if (!currentFilePath) return { success: false, error: 'No file open' };
2230+
const sessions = loadLocalSearchConfigSessions(currentFilePath).filter(s => s.id !== sessionId);
2231+
saveLocalSearchConfigSessions(currentFilePath, sessions);
2232+
}
2233+
return { success: true };
2234+
});
2235+
21442236
// === Utility ===
21452237

21462238
ipcMain.handle('get-file-info', async () => {

src/preload/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ const IPC = {
3131
SEARCH_CONFIG_BATCH: 'search-config-batch',
3232
SEARCH_CONFIG_BATCH_PROGRESS: 'search-config-batch-progress',
3333
SEARCH_CONFIG_EXPORT: 'search-config-export',
34+
SEARCH_CONFIG_EXPORT_ALL: 'search-config-export-all',
35+
SEARCH_CONFIG_SESSION_LIST: 'search-config-session-list',
36+
SEARCH_CONFIG_SESSION_SAVE: 'search-config-session-save',
37+
SEARCH_CONFIG_SESSION_DELETE: 'search-config-session-delete',
3438
GET_LINE_TIMESTAMP: 'get-line-timestamp',
3539
SERIAL_LIST_PORTS: 'serial-list-ports',
3640
SERIAL_CONNECT: 'serial-connect',
@@ -363,6 +367,19 @@ const api = {
363367
searchConfigExport: (configId: string, lines: string[]): Promise<{ success: boolean; filePath?: string; error?: string }> =>
364368
ipcRenderer.invoke(IPC.SEARCH_CONFIG_EXPORT, configId, lines),
365369

370+
searchConfigExportAll: (content: string): Promise<{ success: boolean; filePath?: string; error?: string }> =>
371+
ipcRenderer.invoke(IPC.SEARCH_CONFIG_EXPORT_ALL, content),
372+
373+
// Search config sessions
374+
searchConfigSessionList: (): Promise<{ success: boolean; sessions?: any[] }> =>
375+
ipcRenderer.invoke(IPC.SEARCH_CONFIG_SESSION_LIST),
376+
377+
searchConfigSessionSave: (session: any): Promise<{ success: boolean }> =>
378+
ipcRenderer.invoke(IPC.SEARCH_CONFIG_SESSION_SAVE, session),
379+
380+
searchConfigSessionDelete: (sessionId: string, isGlobal: boolean): Promise<{ success: boolean }> =>
381+
ipcRenderer.invoke(IPC.SEARCH_CONFIG_SESSION_DELETE, sessionId, isGlobal),
382+
366383
// Video player
367384
getLineTimestamp: (lineNumber: number): Promise<{ epochMs: number | null; timestampStr: string | null }> =>
368385
ipcRenderer.invoke(IPC.GET_LINE_TIMESTAMP, lineNumber),

0 commit comments

Comments
 (0)