mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-07-23 23:35:57 +02:00
Add Note Width setting to cap editor content width (#137)
This commit is contained in:
@@ -122,6 +122,14 @@ pub fn set_ui_scale(state: State<'_, AppState>, scale: f64) -> Result<(), String
|
|||||||
Ok(())
|
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 ──
|
// ── Notebooks ──
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ pub fn run() {
|
|||||||
commands::set_font_family,
|
commands::set_font_family,
|
||||||
commands::set_line_height,
|
commands::set_line_height,
|
||||||
commands::set_ui_scale,
|
commands::set_ui_scale,
|
||||||
|
commands::set_content_width,
|
||||||
commands::get_notebooks,
|
commands::get_notebooks,
|
||||||
commands::count_root_notes,
|
commands::count_root_notes,
|
||||||
commands::create_notebook,
|
commands::create_notebook,
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ pub struct AppConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub ui_scale: Option<f64>,
|
pub ui_scale: Option<f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub content_width: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
pub compact_notes: bool,
|
pub compact_notes: bool,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub show_note_dates: bool,
|
pub show_note_dates: bool,
|
||||||
@@ -205,6 +207,7 @@ impl Default for AppConfig {
|
|||||||
font_family: None,
|
font_family: None,
|
||||||
line_height: None,
|
line_height: None,
|
||||||
ui_scale: None,
|
ui_scale: None,
|
||||||
|
content_width: None,
|
||||||
compact_notes: false,
|
compact_notes: false,
|
||||||
show_note_dates: true,
|
show_note_dates: true,
|
||||||
time_format: "relative".to_string(),
|
time_format: "relative".to_string(),
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ export async function setUiScale(scale: number): Promise<void> {
|
|||||||
return invoke("set_ui_scale", { scale });
|
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[]> {
|
export async function getNotebooks(): Promise<NotebookEntry[]> {
|
||||||
return invoke("get_notebooks");
|
return invoke("get_notebooks");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7433,7 +7433,8 @@
|
|||||||
resize: none;
|
resize: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0 0 var(--editor-scroll-past-end, 65vh);
|
padding: 0 0 var(--editor-scroll-past-end, 65vh);
|
||||||
margin: 0;
|
margin: 0 auto;
|
||||||
|
max-width: var(--editor-content-width, none);
|
||||||
user-select: text;
|
user-select: text;
|
||||||
/* Wrap long lines instead of horizontal-scrolling (matches mobile). (issue #100) */
|
/* Wrap long lines instead of horizontal-scrolling (matches mobile). (issue #100) */
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -7447,6 +7448,9 @@
|
|||||||
Keep no-wrap (horizontal scroll) whenever line numbers are on. (issue #100) */
|
Keep no-wrap (horizontal scroll) whenever line numbers are on. (issue #100) */
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
overflow-x: auto;
|
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 {
|
.line-numbers-clip {
|
||||||
@@ -7479,6 +7483,12 @@
|
|||||||
.tiptap-wrapper {
|
.tiptap-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
user-select: text;
|
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) {
|
:global(.tiptap-wrapper .tiptap) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<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 { 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 { darkThemes } from '$lib/platform';
|
||||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
@@ -477,12 +477,20 @@
|
|||||||
{ label: '200%', value: 2 },
|
{ 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 activeAccent = $state($appConfig?.accent_color ?? 'Indigo');
|
||||||
let customAccentColor = $state(($appConfig?.accent_color?.startsWith('#') ? $appConfig.accent_color : null) ?? '#5b6abf');
|
let customAccentColor = $state(($appConfig?.accent_color?.startsWith('#') ? $appConfig.accent_color : null) ?? '#5b6abf');
|
||||||
let activeFontSize = $state($appConfig?.font_size ?? 14);
|
let activeFontSize = $state($appConfig?.font_size ?? 14);
|
||||||
let activeFontFamily = $state($appConfig?.font_family ?? 'system');
|
let activeFontFamily = $state($appConfig?.font_family ?? 'system');
|
||||||
let activeLineHeight = $state($appConfig?.line_height ?? 1.6);
|
let activeLineHeight = $state($appConfig?.line_height ?? 1.6);
|
||||||
let activeUiScale = $state($appConfig?.ui_scale ?? 1);
|
let activeUiScale = $state($appConfig?.ui_scale ?? 1);
|
||||||
|
let activeContentWidth = $state<number | null>($appConfig?.content_width ?? null);
|
||||||
let themeDropdownOpen = $state(false);
|
let themeDropdownOpen = $state(false);
|
||||||
let accentDropdownOpen = $state(false);
|
let accentDropdownOpen = $state(false);
|
||||||
let activeThemePreset = $derived(themePresets.find(p => p.id === $theme) ?? themePresets[0]);
|
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 clickOutside(node: HTMLElement, callback: () => void) {
|
||||||
function handle(e: MouseEvent) {
|
function handle(e: MouseEvent) {
|
||||||
if (!node.contains(e.target as Node)) callback();
|
if (!node.contains(e.target as Node)) callback();
|
||||||
@@ -732,6 +751,11 @@
|
|||||||
activeLineHeight = savedLineHeight;
|
activeLineHeight = savedLineHeight;
|
||||||
applyLineHeight(savedLineHeight);
|
applyLineHeight(savedLineHeight);
|
||||||
}
|
}
|
||||||
|
const savedContentWidth = $appConfig?.content_width;
|
||||||
|
if (savedContentWidth) {
|
||||||
|
activeContentWidth = savedContentWidth;
|
||||||
|
applyContentWidth(savedContentWidth);
|
||||||
|
}
|
||||||
// Sync general settings
|
// Sync general settings
|
||||||
if ($appConfig) {
|
if ($appConfig) {
|
||||||
compactNotes = $appConfig.compact_notes ?? false;
|
compactNotes = $appConfig.compact_notes ?? false;
|
||||||
@@ -1185,6 +1209,24 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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">
|
<div class="settings-section">
|
||||||
<h3>Line Height</h3>
|
<h3>Line Height</h3>
|
||||||
<div class="font-size-options">
|
<div class="font-size-options">
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export interface AppConfig {
|
|||||||
font_family: string | null;
|
font_family: string | null;
|
||||||
line_height: number | null;
|
line_height: number | null;
|
||||||
ui_scale: number | null;
|
ui_scale: number | null;
|
||||||
|
content_width: number | null;
|
||||||
compact_notes: boolean;
|
compact_notes: boolean;
|
||||||
show_note_dates: boolean;
|
show_note_dates: boolean;
|
||||||
time_format: string;
|
time_format: string;
|
||||||
|
|||||||
@@ -138,6 +138,9 @@
|
|||||||
if (config.line_height) {
|
if (config.line_height) {
|
||||||
document.documentElement.style.setProperty('--editor-line-height', String(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) {
|
if (config.font_family) {
|
||||||
const fontStacks: Record<string, string> = {
|
const fontStacks: Record<string, string> = {
|
||||||
system: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
system: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||||
|
|||||||
Reference in New Issue
Block a user