Add Note Width setting to cap editor content width (#137)

This commit is contained in:
Yuri Karamian
2026-06-20 15:01:01 +02:00
parent f0f19125d5
commit 1c46935cc5
8 changed files with 74 additions and 2 deletions
+8
View File
@@ -122,6 +122,14 @@ pub fn set_ui_scale(state: State<'_, AppState>, scale: f64) -> Result<(), String
Ok(())
}
#[tauri::command]
pub fn set_content_width(state: State<'_, AppState>, width: Option<u32>) -> Result<(), String> {
let mut config = state.config.lock().map_err(|e| e.to_string())?;
config.content_width = width;
save_app_config(&config)?;
Ok(())
}
// ── Notebooks ──
#[tauri::command]
+1
View File
@@ -106,6 +106,7 @@ pub fn run() {
commands::set_font_family,
commands::set_line_height,
commands::set_ui_scale,
commands::set_content_width,
commands::get_notebooks,
commands::count_root_notes,
commands::create_notebook,
+3
View File
@@ -72,6 +72,8 @@ pub struct AppConfig {
#[serde(default)]
pub ui_scale: Option<f64>,
#[serde(default)]
pub content_width: Option<u32>,
#[serde(default)]
pub compact_notes: bool,
#[serde(default = "default_true")]
pub show_note_dates: bool,
@@ -205,6 +207,7 @@ impl Default for AppConfig {
font_family: None,
line_height: None,
ui_scale: None,
content_width: None,
compact_notes: false,
show_note_dates: true,
time_format: "relative".to_string(),
+4
View File
@@ -52,6 +52,10 @@ export async function setUiScale(scale: number): Promise<void> {
return invoke("set_ui_scale", { scale });
}
export async function setContentWidth(width: number | null): Promise<void> {
return invoke("set_content_width", { width });
}
export async function getNotebooks(): Promise<NotebookEntry[]> {
return invoke("get_notebooks");
}
+11 -1
View File
@@ -7433,7 +7433,8 @@
resize: none;
outline: none;
padding: 0 0 var(--editor-scroll-past-end, 65vh);
margin: 0;
margin: 0 auto;
max-width: var(--editor-content-width, none);
user-select: text;
/* Wrap long lines instead of horizontal-scrolling (matches mobile). (issue #100) */
white-space: pre-wrap;
@@ -7447,6 +7448,9 @@
Keep no-wrap (horizontal scroll) whenever line numbers are on. (issue #100) */
white-space: pre;
overflow-x: auto;
/* The line-number gutter is pinned to the left edge, so don't center/cap here. (#137) */
max-width: none;
margin: 0;
}
.line-numbers-clip {
@@ -7479,6 +7483,12 @@
.tiptap-wrapper {
height: 100%;
user-select: text;
/* Optional reading-width cap (Settings > Styling > Note Width). Default `none` = full
width; when set, the text column is capped and centered. Scrollbar stays at the
panel edge because only the inner content is constrained, not .editor-body. (#137) */
max-width: var(--editor-content-width, none);
margin-left: auto;
margin-right: auto;
}
:global(.tiptap-wrapper .tiptap) {
+43 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { showSettings, theme, appConfig, updateAvailable as globalUpdateAvailable, updateObj as globalUpdateObj, installType, settingsTab, vaultReady, androidApkUrl, checkForUpdateMobile, notebookSortMode, isManagedInstall } from '$lib/stores/app';
import { setTheme, setAccentColor, setFontSize, setFontFamily, setLineHeight, setUiScale, setGeneralSettings, importObsidian, createBackup, listBackups, restoreBackup, deleteBackup, setBackupSettings, setAiSettings, testAiConnection, setSyncSettings, testSyncConnection, syncNow } from '$lib/api';
import { setTheme, setAccentColor, setFontSize, setFontFamily, setLineHeight, setUiScale, setContentWidth, setGeneralSettings, importObsidian, createBackup, listBackups, restoreBackup, deleteBackup, setBackupSettings, setAiSettings, testAiConnection, setSyncSettings, testSyncConnection, syncNow } from '$lib/api';
import { darkThemes } from '$lib/platform';
import { open as openDialog } from '@tauri-apps/plugin-dialog';
import { listen } from '@tauri-apps/api/event';
@@ -477,12 +477,20 @@
{ label: '200%', value: 2 },
];
const contentWidthPresets: { label: string; value: number | null }[] = [
{ label: 'Narrow', value: 600 },
{ label: 'Medium', value: 760 },
{ label: 'Wide', value: 960 },
{ label: 'Full', value: null },
];
let activeAccent = $state($appConfig?.accent_color ?? 'Indigo');
let customAccentColor = $state(($appConfig?.accent_color?.startsWith('#') ? $appConfig.accent_color : null) ?? '#5b6abf');
let activeFontSize = $state($appConfig?.font_size ?? 14);
let activeFontFamily = $state($appConfig?.font_family ?? 'system');
let activeLineHeight = $state($appConfig?.line_height ?? 1.6);
let activeUiScale = $state($appConfig?.ui_scale ?? 1);
let activeContentWidth = $state<number | null>($appConfig?.content_width ?? null);
let themeDropdownOpen = $state(false);
let accentDropdownOpen = $state(false);
let activeThemePreset = $derived(themePresets.find(p => p.id === $theme) ?? themePresets[0]);
@@ -681,6 +689,17 @@
}
}
function selectContentWidth(value: number | null) {
activeContentWidth = value;
applyContentWidth(value);
if ($appConfig) $appConfig.content_width = value;
setContentWidth(value).catch((e) => console.error('Failed to save content width:', e));
}
function applyContentWidth(value: number | null) {
document.documentElement.style.setProperty('--editor-content-width', value ? `${value}px` : 'none');
}
function clickOutside(node: HTMLElement, callback: () => void) {
function handle(e: MouseEvent) {
if (!node.contains(e.target as Node)) callback();
@@ -732,6 +751,11 @@
activeLineHeight = savedLineHeight;
applyLineHeight(savedLineHeight);
}
const savedContentWidth = $appConfig?.content_width;
if (savedContentWidth) {
activeContentWidth = savedContentWidth;
applyContentWidth(savedContentWidth);
}
// Sync general settings
if ($appConfig) {
compactNotes = $appConfig.compact_notes ?? false;
@@ -1185,6 +1209,24 @@
</div>
{/if}
{#if !isMobile}
<div class="settings-section">
<h3>Note Width</h3>
<div class="font-size-options">
{#each contentWidthPresets as preset}
<button
class="font-size-btn"
class:active={activeContentWidth === preset.value}
onclick={() => selectContentWidth(preset.value)}
>
<span class="font-size-label">{preset.label}</span>
</button>
{/each}
</div>
<p class="setting-hint">Caps the width of the note text for easier reading on wide screens; the column stays centered. Most noticeable in Focus Mode. "Full" uses the entire width.</p>
</div>
{/if}
<div class="settings-section">
<h3>Line Height</h3>
<div class="font-size-options">
+1
View File
@@ -55,6 +55,7 @@ export interface AppConfig {
font_family: string | null;
line_height: number | null;
ui_scale: number | null;
content_width: number | null;
compact_notes: boolean;
show_note_dates: boolean;
time_format: string;
+3
View File
@@ -138,6 +138,9 @@
if (config.line_height) {
document.documentElement.style.setProperty('--editor-line-height', String(config.line_height));
}
if (config.content_width) {
document.documentElement.style.setProperty('--editor-content-width', `${config.content_width}px`);
}
if (config.font_family) {
const fontStacks: Record<string, string> = {
system: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',