Skip to content

Commit 3adefc3

Browse files
ozgesolidkeyclaude
andcommitted
Add Settings modal with user preferences
Settings (persisted to localStorage): - Scroll Speed: Adjusts trackpad scroll sensitivity (10-100%) - Default Font Size: Starting font size for log viewer (10-20px) - Default Time Gap Threshold: Default seconds for time gap detection (1-60s) - Auto-analyze: Automatically run analysis when opening files Includes reset to defaults button. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6cea27d commit 3adefc3

3 files changed

Lines changed: 224 additions & 3 deletions

File tree

src/renderer/index.html

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<button id="btn-word-wrap" class="toolbar-btn" title="Toggle word wrap (⌥Z)">Wrap</button>
4545
</div>
4646
<div class="toolbar-right">
47+
<button id="btn-settings" class="toolbar-btn small" title="Settings">&#9881;</button>
4748
<button id="btn-help" class="toolbar-btn small" title="Keyboard shortcuts & help (F1)">&#9432;</button>
4849
<button id="btn-toggle-sidebar" class="toolbar-btn small" title="Toggle sidebar">&#9776;</button>
4950
</div>
@@ -591,6 +592,50 @@ <h3>Save to Notes</h3>
591592
</div>
592593
</div>
593594
</div>
595+
596+
<!-- Settings Modal -->
597+
<div id="settings-modal" class="modal hidden">
598+
<div class="modal-content" style="width: 400px;">
599+
<div class="modal-header">
600+
<h3>Settings</h3>
601+
<button class="modal-close" data-modal="settings-modal">&times;</button>
602+
</div>
603+
<div class="modal-body">
604+
<div class="settings-group">
605+
<label for="scroll-speed">Scroll Speed</label>
606+
<div class="slider-container">
607+
<input type="range" id="scroll-speed" min="10" max="100" value="30" class="settings-slider">
608+
<span id="scroll-speed-value" class="slider-value">30%</span>
609+
</div>
610+
<p class="hint">Adjusts trackpad scroll sensitivity (default: 30%)</p>
611+
</div>
612+
<div class="settings-group">
613+
<label for="default-font-size">Default Font Size</label>
614+
<div class="slider-container">
615+
<input type="range" id="default-font-size" min="10" max="20" value="13" class="settings-slider">
616+
<span id="default-font-size-value" class="slider-value">13px</span>
617+
</div>
618+
</div>
619+
<div class="settings-group">
620+
<label for="default-gap-threshold">Default Time Gap Threshold</label>
621+
<div class="slider-container">
622+
<input type="range" id="default-gap-threshold" min="1" max="60" value="5" class="settings-slider">
623+
<span id="default-gap-threshold-value" class="slider-value">5s</span>
624+
</div>
625+
</div>
626+
<div class="settings-group">
627+
<label class="checkbox-label">
628+
<input type="checkbox" id="auto-analyze"> Auto-analyze when opening files
629+
</label>
630+
<p class="hint">Automatically run analysis when a file is opened</p>
631+
</div>
632+
</div>
633+
<div class="modal-footer">
634+
<button id="btn-reset-settings" class="secondary-btn">Reset to Defaults</button>
635+
<button id="btn-close-settings" class="primary-btn">Close</button>
636+
</div>
637+
</div>
638+
</div>
594639
</div>
595640

596641
<script src="renderer.js"></script>

src/renderer/renderer.ts

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,50 @@ const ZOOM_MAX = 200;
308308
const ZOOM_STEP = 10;
309309
let zoomLevel = 100; // Percentage (100 = default)
310310

311+
// User settings (persisted to localStorage)
312+
interface UserSettings {
313+
scrollSpeed: number; // 10-100, percentage
314+
defaultFontSize: number; // 10-20, pixels
315+
defaultGapThreshold: number; // 1-60, seconds
316+
autoAnalyze: boolean;
317+
}
318+
319+
const DEFAULT_SETTINGS: UserSettings = {
320+
scrollSpeed: 30,
321+
defaultFontSize: 13,
322+
defaultGapThreshold: 5,
323+
autoAnalyze: false
324+
};
325+
326+
let userSettings: UserSettings = { ...DEFAULT_SETTINGS };
327+
328+
function loadSettings(): void {
329+
try {
330+
const saved = localStorage.getItem('logan-settings');
331+
if (saved) {
332+
userSettings = { ...DEFAULT_SETTINGS, ...JSON.parse(saved) };
333+
}
334+
} catch {
335+
userSettings = { ...DEFAULT_SETTINGS };
336+
}
337+
}
338+
339+
function saveSettings(): void {
340+
localStorage.setItem('logan-settings', JSON.stringify(userSettings));
341+
}
342+
343+
function applySettings(): void {
344+
// Apply default font size to zoom level
345+
const fontSizeRatio = userSettings.defaultFontSize / BASE_FONT_SIZE;
346+
zoomLevel = Math.round(fontSizeRatio * 100);
347+
348+
// Update gap threshold input if it exists
349+
const gapInput = document.getElementById('gap-threshold') as HTMLInputElement;
350+
if (gapInput) {
351+
gapInput.value = userSettings.defaultGapThreshold.toString();
352+
}
353+
}
354+
311355
// Word wrap setting
312356
let wordWrapEnabled = false;
313357

@@ -435,6 +479,18 @@ const elements = {
435479
btnHelp: document.getElementById('btn-help') as HTMLButtonElement,
436480
helpModal: document.getElementById('help-modal') as HTMLDivElement,
437481
btnCloseHelp: document.getElementById('btn-close-help') as HTMLButtonElement,
482+
// Settings
483+
btnSettings: document.getElementById('btn-settings') as HTMLButtonElement,
484+
settingsModal: document.getElementById('settings-modal') as HTMLDivElement,
485+
scrollSpeedSlider: document.getElementById('scroll-speed') as HTMLInputElement,
486+
scrollSpeedValue: document.getElementById('scroll-speed-value') as HTMLSpanElement,
487+
defaultFontSizeSlider: document.getElementById('default-font-size') as HTMLInputElement,
488+
defaultFontSizeValue: document.getElementById('default-font-size-value') as HTMLSpanElement,
489+
defaultGapThresholdSlider: document.getElementById('default-gap-threshold') as HTMLInputElement,
490+
defaultGapThresholdValue: document.getElementById('default-gap-threshold-value') as HTMLSpanElement,
491+
autoAnalyzeCheckbox: document.getElementById('auto-analyze') as HTMLInputElement,
492+
btnResetSettings: document.getElementById('btn-reset-settings') as HTMLButtonElement,
493+
btnCloseSettings: document.getElementById('btn-close-settings') as HTMLButtonElement,
438494
// Bookmark modal
439495
bookmarkModal: document.getElementById('bookmark-modal') as HTMLDivElement,
440496
bookmarkModalTitle: document.getElementById('bookmark-modal-title') as HTMLHeadingElement,
@@ -695,9 +751,8 @@ function createLogViewer(): void {
695751
let delta = e.deltaY;
696752

697753
if (e.deltaMode === 0) {
698-
// Pixel-based scrolling (trackpad) - reduce speed significantly
699-
// Trackpads send high-frequency small deltas, reduce by ~3x
700-
delta = delta * 0.3;
754+
// Pixel-based scrolling (trackpad) - use user's scroll speed setting
755+
delta = delta * (userSettings.scrollSpeed / 100);
701756
} else if (e.deltaMode === 1) {
702757
// Line-based scrolling (mouse wheel) - convert to pixels
703758
delta = delta * getLineHeight();
@@ -2635,6 +2690,12 @@ async function loadFile(filePath: string, createNewTab: boolean = true): Promise
26352690
updateProgress(percent);
26362691
updateProgressText(`Building minimap... ${percent}%`);
26372692
});
2693+
2694+
// Auto-analyze if enabled in settings
2695+
if (userSettings.autoAnalyze && !isMarkdownFile) {
2696+
hideProgress();
2697+
await analyzeFile();
2698+
}
26382699
} else {
26392700
alert(`Failed to open file: ${result.error}`);
26402701
}
@@ -4975,6 +5036,10 @@ async function checkSearchEngine(): Promise<void> {
49755036
const GITHUB_URL = 'https://github.com/SolidKeyAB/logan';
49765037

49775038
function init(): void {
5039+
// Load user settings from localStorage
5040+
loadSettings();
5041+
applySettings();
5042+
49785043
// Check search engine on startup
49795044
checkSearchEngine();
49805045

@@ -5134,6 +5199,67 @@ function init(): void {
51345199
elements.helpModal.classList.add('hidden');
51355200
});
51365201

5202+
// Settings modal
5203+
elements.btnSettings.addEventListener('click', () => {
5204+
// Load current settings into UI
5205+
elements.scrollSpeedSlider.value = userSettings.scrollSpeed.toString();
5206+
elements.scrollSpeedValue.textContent = `${userSettings.scrollSpeed}%`;
5207+
elements.defaultFontSizeSlider.value = userSettings.defaultFontSize.toString();
5208+
elements.defaultFontSizeValue.textContent = `${userSettings.defaultFontSize}px`;
5209+
elements.defaultGapThresholdSlider.value = userSettings.defaultGapThreshold.toString();
5210+
elements.defaultGapThresholdValue.textContent = `${userSettings.defaultGapThreshold}s`;
5211+
elements.autoAnalyzeCheckbox.checked = userSettings.autoAnalyze;
5212+
elements.settingsModal.classList.remove('hidden');
5213+
});
5214+
5215+
elements.scrollSpeedSlider.addEventListener('input', () => {
5216+
const value = parseInt(elements.scrollSpeedSlider.value, 10);
5217+
elements.scrollSpeedValue.textContent = `${value}%`;
5218+
userSettings.scrollSpeed = value;
5219+
saveSettings();
5220+
});
5221+
5222+
elements.defaultFontSizeSlider.addEventListener('input', () => {
5223+
const value = parseInt(elements.defaultFontSizeSlider.value, 10);
5224+
elements.defaultFontSizeValue.textContent = `${value}px`;
5225+
userSettings.defaultFontSize = value;
5226+
saveSettings();
5227+
});
5228+
5229+
elements.defaultGapThresholdSlider.addEventListener('input', () => {
5230+
const value = parseInt(elements.defaultGapThresholdSlider.value, 10);
5231+
elements.defaultGapThresholdValue.textContent = `${value}s`;
5232+
userSettings.defaultGapThreshold = value;
5233+
saveSettings();
5234+
// Update the gap threshold input if visible
5235+
const gapInput = document.getElementById('gap-threshold') as HTMLInputElement;
5236+
if (gapInput) {
5237+
gapInput.value = value.toString();
5238+
}
5239+
});
5240+
5241+
elements.autoAnalyzeCheckbox.addEventListener('change', () => {
5242+
userSettings.autoAnalyze = elements.autoAnalyzeCheckbox.checked;
5243+
saveSettings();
5244+
});
5245+
5246+
elements.btnResetSettings.addEventListener('click', () => {
5247+
userSettings = { ...DEFAULT_SETTINGS };
5248+
saveSettings();
5249+
// Update UI
5250+
elements.scrollSpeedSlider.value = userSettings.scrollSpeed.toString();
5251+
elements.scrollSpeedValue.textContent = `${userSettings.scrollSpeed}%`;
5252+
elements.defaultFontSizeSlider.value = userSettings.defaultFontSize.toString();
5253+
elements.defaultFontSizeValue.textContent = `${userSettings.defaultFontSize}px`;
5254+
elements.defaultGapThresholdSlider.value = userSettings.defaultGapThreshold.toString();
5255+
elements.defaultGapThresholdValue.textContent = `${userSettings.defaultGapThreshold}s`;
5256+
elements.autoAnalyzeCheckbox.checked = userSettings.autoAnalyze;
5257+
});
5258+
5259+
elements.btnCloseSettings.addEventListener('click', () => {
5260+
elements.settingsModal.classList.add('hidden');
5261+
});
5262+
51375263
// Bookmark modal
51385264
elements.btnSaveBookmark.addEventListener('click', () => hideBookmarkModal(true));
51395265
elements.btnCancelBookmark.addEventListener('click', () => hideBookmarkModal(false));

src/renderer/styles.css

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,56 @@ body {
16471647
margin-top: 4px;
16481648
}
16491649

1650+
/* Settings styles */
1651+
.settings-group {
1652+
margin-bottom: 16px;
1653+
}
1654+
1655+
.settings-group label {
1656+
display: block;
1657+
font-weight: 600;
1658+
margin-bottom: 8px;
1659+
color: var(--text-primary);
1660+
}
1661+
1662+
.slider-container {
1663+
display: flex;
1664+
align-items: center;
1665+
gap: 12px;
1666+
}
1667+
1668+
.settings-slider {
1669+
flex: 1;
1670+
-webkit-appearance: none;
1671+
appearance: none;
1672+
height: 6px;
1673+
background: var(--bg-tertiary);
1674+
border-radius: 3px;
1675+
outline: none;
1676+
}
1677+
1678+
.settings-slider::-webkit-slider-thumb {
1679+
-webkit-appearance: none;
1680+
appearance: none;
1681+
width: 16px;
1682+
height: 16px;
1683+
background: var(--accent-color);
1684+
border-radius: 50%;
1685+
cursor: pointer;
1686+
}
1687+
1688+
.settings-slider::-webkit-slider-thumb:hover {
1689+
background: var(--accent-hover);
1690+
}
1691+
1692+
.slider-value {
1693+
min-width: 40px;
1694+
text-align: right;
1695+
font-size: 12px;
1696+
color: var(--text-secondary);
1697+
font-weight: 600;
1698+
}
1699+
16501700
.split-preview {
16511701
padding: 10px;
16521702
background-color: var(--bg-tertiary);

0 commit comments

Comments
 (0)