Skip to content

Commit 8a90515

Browse files
ozgesolidkeyclaude
andcommitted
Add video player panel with log-to-video timestamp sync
Adds a bottom slide-up video player (Ctrl+9) that syncs with log timestamps. Users can open a screen recording, set a sync point from any log line, then click other lines to seek the video to the corresponding moment. Supports MP4/WebM/OGG via drag-drop or file picker, with per-file persistence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e8bde75 commit 8a90515

7 files changed

Lines changed: 542 additions & 5 deletions

File tree

src/main/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,6 +2472,27 @@ function parseTimestampFast(text: string): { date: Date; str: string } | null {
24722472
return null;
24732473
}
24742474

2475+
// Get timestamp from a specific line
2476+
ipcMain.handle(IPC.GET_LINE_TIMESTAMP, async (_, lineNumber: number) => {
2477+
const handler = getFileHandler();
2478+
if (!handler) {
2479+
return { epochMs: null, timestampStr: null };
2480+
}
2481+
try {
2482+
const lines = handler.getLines(lineNumber, 1);
2483+
if (lines.length === 0) {
2484+
return { epochMs: null, timestampStr: null };
2485+
}
2486+
const parsed = parseTimestampFast(lines[0].text);
2487+
if (!parsed) {
2488+
return { epochMs: null, timestampStr: null };
2489+
}
2490+
return { epochMs: parsed.date.getTime(), timestampStr: parsed.str };
2491+
} catch {
2492+
return { epochMs: null, timestampStr: null };
2493+
}
2494+
});
2495+
24752496
ipcMain.handle('detect-time-gaps', async (_, options: TimeGapOptions) => {
24762497
const handler = getFileHandler();
24772498
if (!handler || !currentFilePath) {

src/preload/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ 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+
GET_LINE_TIMESTAMP: 'get-line-timestamp',
3435
} as const;
3536

3637
// API exposed to renderer
@@ -346,6 +347,10 @@ const api = {
346347
searchConfigExport: (configId: string, lines: string[]): Promise<{ success: boolean; filePath?: string; error?: string }> =>
347348
ipcRenderer.invoke(IPC.SEARCH_CONFIG_EXPORT, configId, lines),
348349

350+
// Video player
351+
getLineTimestamp: (lineNumber: number): Promise<{ epochMs: number | null; timestampStr: string | null }> =>
352+
ipcRenderer.invoke(IPC.GET_LINE_TIMESTAMP, lineNumber),
353+
349354
// Window controls
350355
windowMinimize: (): Promise<void> => ipcRenderer.invoke('window-minimize'),
351356
windowMaximize: (): Promise<void> => ipcRenderer.invoke('window-maximize'),

src/renderer/index.html

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; media-src 'self';">
6+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; media-src 'self' file:;">
77
<title>LOGAN - Log Analyzer</title>
88
<link rel="stylesheet" href="styles.css">
99
<link rel="stylesheet" href="lib/xterm.css">
@@ -124,6 +124,10 @@
124124
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line><line x1="11" y1="8" x2="11" y2="14"></line></svg>
125125
<span class="activity-badge" id="badge-search-configs"></span>
126126
</button>
127+
<button class="activity-bar-btn" id="btn-video-player" title="Video Player (Ctrl+9)">
128+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><polygon points="10 8 16 12 10 16 10 8"></polygon></svg>
129+
<span class="activity-badge" id="badge-video-player"></span>
130+
</button>
127131
</div>
128132
<div class="activity-bar-bottom">
129133
<button class="activity-bar-btn" id="btn-activity-settings" title="Settings">
@@ -390,6 +394,36 @@ <h2>LOGAN</h2>
390394
</div>
391395
</div>
392396

397+
<!-- Video Player Panel (slide-up from bottom) -->
398+
<div id="video-overlay" class="video-overlay hidden">
399+
<div id="video-panel" class="video-panel">
400+
<div id="video-resize-handle" class="video-resize-handle-top"></div>
401+
<div class="video-panel-header">
402+
<span class="video-panel-title">Video Player</span>
403+
<span id="video-file-name" class="video-file-name"></span>
404+
<button id="btn-video-close" class="video-close-btn" title="Close (Esc)">&times;</button>
405+
</div>
406+
<div class="video-panel-body">
407+
<div id="video-container" class="video-container">
408+
<video id="video-element" class="video-element" controls></video>
409+
<div id="video-drop-zone" class="video-drop-zone">
410+
<div class="video-drop-zone-content">
411+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.5"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><polygon points="10 8 16 12 10 16 10 8"></polygon></svg>
412+
<p>Drag & drop a video file here</p>
413+
<button id="btn-video-open" class="video-open-btn">Open Video</button>
414+
</div>
415+
</div>
416+
</div>
417+
<div class="video-sync-bar">
418+
<label class="video-sync-label">Sync:</label>
419+
<input type="text" id="video-sync-input" class="video-sync-input" placeholder="Timestamp at video 0:00" title="Enter the log timestamp that corresponds to video start (0:00)">
420+
<button id="btn-video-sync-from-line" class="video-sync-btn" title="Use selected line's timestamp">Set from line</button>
421+
<span id="video-sync-status" class="video-sync-status"></span>
422+
</div>
423+
</div>
424+
</div>
425+
</div>
426+
393427
<!-- Status Bar -->
394428
<footer class="status-bar">
395429
<div class="status-left">
@@ -635,7 +669,8 @@ <h4>Terminal & Notes</h4>
635669
<div class="shortcut-item"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>N</kbd><span>Toggle notes drawer</span></div>
636670
<div class="shortcut-item"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd><span>Toggle search results panel</span></div>
637671
<div class="shortcut-item"><kbd>Ctrl</kbd>+<kbd>8</kbd><span>Toggle search configs panel</span></div>
638-
<div class="shortcut-item"><kbd>Esc</kbd><span>Close terminal/notes/search results/search configs (when focused)</span></div>
672+
<div class="shortcut-item"><kbd>Ctrl</kbd>+<kbd>9</kbd><span>Toggle video player</span></div>
673+
<div class="shortcut-item"><kbd>Esc</kbd><span>Close terminal/notes/search results/search configs/video (when focused)</span></div>
639674
</div>
640675
</div>
641676
<div class="help-section">

0 commit comments

Comments
 (0)