Compare commits

...
10 Commits
20 changed files with 126 additions and 80 deletions
+11 -9
View File
@@ -1,7 +1,7 @@
# HelixNotes # HelixNotes
[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://gitlab.com/ArkHost/HelixNotes/-/blob/main/LICENSE) [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://gitlab.com/ArkHost/HelixNotes/-/blob/main/LICENSE)
[![Latest Release](https://img.shields.io/badge/release-v1.3.3-green)](https://gitlab.com/ArkHost/HelixNotes/-/releases) [![Latest Release](https://img.shields.io/badge/release-v1.3.4-green)](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.4)
[![Website](https://img.shields.io/badge/web-helixnotes.com-purple)](https://helixnotes.com) [![Website](https://img.shields.io/badge/web-helixnotes.com-purple)](https://helixnotes.com)
[![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS%20%7C%20Android-lightgrey)]() [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS%20%7C%20Android-lightgrey)]()
@@ -10,7 +10,7 @@ A local markdown note-taking app built with Tauri, SvelteKit, and Rust.
Your notes are stored as standard Markdown files on your local filesystem. Your notes are stored as standard Markdown files on your local filesystem.
No cloud, no lock-in. No cloud, no lock-in.
## Download (v1.3.3) ## Download (v1.3.4)
### Linux ### Linux
@@ -92,21 +92,23 @@ sudo eopkg it helixnotes
#### AppImage (Arch, Fedora 43+, openSUSE Tumbleweed) #### AppImage (Arch, Fedora 43+, openSUSE Tumbleweed)
[Download AppImage](https://download.helixnotes.com/releases/v1.3.3/HelixNotes_1.3.3_amd64.AppImage) [Download AppImage](https://download.helixnotes.com/releases/v1.3.4/HelixNotes_1.3.4_amd64.AppImage)
#### .deb (manual) #### .deb (manual)
[Download .deb](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.3/downloads/HelixNotes_1.3.3_amd64.deb) (Ubuntu 22.04+) [Download .deb](https://download.helixnotes.com/releases/v1.3.4/HelixNotes_1.3.4_amd64.deb) (Ubuntu 22.04+)
#### .rpm (manual)
[Download .rpm](https://download.helixnotes.com/releases/v1.3.4/HelixNotes-1.3.4-1.x86_64.rpm)
### Windows ### Windows
[Download Installer](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.3/downloads/HelixNotes_1.3.3_x64-setup.exe) (Windows 10/11) [Download Installer](https://download.helixnotes.com/releases/v1.3.4/HelixNotes_1.3.4_x64-setup.exe) (Windows 10/11)
### macOS ### macOS
[Download .dmg (Apple Silicon)](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.3/downloads/HelixNotes_1.3.3_aarch64.dmg) (M-series Macs) [Download .dmg (Apple Silicon)](https://download.helixnotes.com/releases/v1.3.4/HelixNotes_1.3.4_aarch64.dmg) (M-series Macs)
[Download .dmg (Intel)](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.3/downloads/HelixNotes_1.3.3_x64.dmg) (older Macs, or Apple Silicon via Rosetta)
> **"HelixNotes is damaged and can't be opened"?** The app isn't damaged. The macOS build isn't notarized by Apple yet, so Gatekeeper blocks it on Apple Silicon. Run this once in Terminal, then open it normally (you'll need to redo it after each update): > **"HelixNotes is damaged and can't be opened"?** The app isn't damaged. The macOS build isn't notarized by Apple yet, so Gatekeeper blocks it on Apple Silicon. Run this once in Terminal, then open it normally (you'll need to redo it after each update):
> >
@@ -116,7 +118,7 @@ sudo eopkg it helixnotes
### Android ### Android
[Download APK](https://gitlab.com/ArkHost/HelixNotes/-/releases/v1.3.3/downloads/HelixNotes_1.3.3_android.apk) [Download APK](https://download.helixnotes.com/releases/v1.3.4/HelixNotes_1.3.4_android.apk)
--- ---
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "helixnotes", "name": "helixnotes",
"private": true, "private": true,
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"version": "1.3.3", "version": "1.3.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
+2 -1
View File
@@ -1850,7 +1850,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "helixnotes" name = "helixnotes"
version = "1.3.3" version = "1.3.4"
dependencies = [ dependencies = [
"arboard", "arboard",
"chrono", "chrono",
@@ -1860,6 +1860,7 @@ dependencies = [
"image", "image",
"log", "log",
"notify", "notify",
"objc2-app-kit",
"png 0.17.16", "png 0.17.16",
"quick-xml 0.36.2", "quick-xml 0.36.2",
"rayon", "rayon",
+4 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "helixnotes" name = "helixnotes"
version = "1.3.3" version = "1.3.4"
description = "Local markdown note-taking app" description = "Local markdown note-taking app"
authors = ["HelixNotes"] authors = ["HelixNotes"]
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@@ -42,6 +42,9 @@ quick-xml = "0.36"
urlencoding = "2" urlencoding = "2"
sha2 = "0.10" sha2 = "0.10"
rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"] } rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"] }
[target.'cfg(target_os = "macos")'.dependencies]
objc2-app-kit = "0.3.2"
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
tauri-plugin-ios-vault-access = { path = "plugins/ios-vault-access" } tauri-plugin-ios-vault-access = { path = "plugins/ios-vault-access" }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="ic_launcher_background">#fff</color> <color name="ic_launcher_background">#626ED4</color>
</resources> </resources>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(85.333 85.333) scale(0.666667)">
<line x1="178.45584" y1="178.45584" x2="333.54416" y2="333.54416" stroke="#ffffff" stroke-width="25" stroke-opacity="0.5" stroke-linecap="round"/>
<line x1="337.53708" y1="174.46292" x2="174.46294" y2="337.53708" stroke="#ffffff" stroke-width="25" stroke-opacity="0.5" stroke-linecap="round"/>
<circle cx="153" cy="153" r="36" fill="#ffffff"/>
<circle cx="359" cy="153" r="36" fill="#ffffff"/>
<circle cx="153" cy="359" r="36" fill="#ffffff"/>
<circle cx="359" cy="359" r="36" fill="#ffffff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 706 B

+4 -12
View File
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tauri::{ use tauri::{
plugin::{Builder, PluginApi, PluginHandle, TauriPlugin}, plugin::{Builder, PluginHandle, TauriPlugin},
AppHandle, Manager, Runtime, Manager, Runtime,
}; };
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@@ -73,19 +73,11 @@ impl<R: Runtime, T: Manager<R>> IosVaultAccessExt<R> for T {
} }
} }
fn initialize<R: Runtime>(
_app: &AppHandle<R>,
api: PluginApi<R, ()>,
) -> tauri::Result<IosVaultAccess<R>> {
let handle = api.register_ios_plugin(init_plugin_ios_vault_access)?;
Ok(IosVaultAccess(handle))
}
pub fn init<R: Runtime>() -> TauriPlugin<R> { pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("ios-vault-access") Builder::new("ios-vault-access")
.setup(|app, api| { .setup(|app, api| {
let access = initialize(app, api)?; let handle = api.register_ios_plugin(init_plugin_ios_vault_access)?;
app.manage(access); app.manage(IosVaultAccess(handle));
Ok(()) Ok(())
}) })
.build() .build()
+6 -2
View File
@@ -392,11 +392,15 @@ pub fn import_custom_themes(state: State<'_, AppState>, path: String) -> Result<
} }
#[tauri::command] #[tauri::command]
pub fn set_font_size(state: State<'_, AppState>, size: u32) -> Result<(), String> { pub fn set_font_size(app: AppHandle, state: State<'_, AppState>, size: u32) -> Result<(), String> {
let mut config = state.config.lock().map_err(|e| e.to_string())?; let mut config = state.config.lock().map_err(|e| e.to_string())?;
config.font_size = Some(size); config.font_size = Some(size);
save_app_config(&config)?; save_app_config(&config)?;
Ok(()) drop(config);
use tauri::Emitter;
app.emit("editor-font-size-changed", size)
.map_err(|e| e.to_string())
} }
#[tauri::command] #[tauri::command]
+40 -25
View File
@@ -56,6 +56,23 @@ pub fn run() {
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.on_page_load(|webview, payload| {
#[cfg(target_os = "macos")]
if webview.label() == "main"
&& matches!(
payload.event(),
tauri::webview::PageLoadEvent::Finished
)
{
let window = webview.window();
let window_on_main = window.clone();
let _ = window.run_on_main_thread(move || {
fix_macos_traffic_lights(&window_on_main);
});
}
#[cfg(not(target_os = "macos"))]
let _ = (webview, payload);
})
.setup(move |app| { .setup(move |app| {
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
app.handle().plugin(tauri_plugin_ios_vault_access::init())?; app.handle().plugin(tauri_plugin_ios_vault_access::init())?;
@@ -88,13 +105,6 @@ pub fn run() {
setup_tray(app)?; setup_tray(app)?;
} }
#[cfg(target_os = "macos")]
{
if let Some(window) = app.get_webview_window("main") {
fix_macos_traffic_lights(&window);
}
}
// Check CLI args for a .md file path on initial launch // Check CLI args for a .md file path on initial launch
#[cfg(desktop)] #[cfg(desktop)]
{ {
@@ -401,44 +411,49 @@ fn percent_decode(input: &str) -> String {
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn fix_macos_traffic_lights(window: &tauri::WebviewWindow) { fn fix_macos_traffic_lights(window: &tauri::Window) {
use objc2_app_kit::{NSView, NSWindow, NSWindowButton}; use objc2_app_kit::{NSView, NSWindow, NSWindowButton};
const TITLEBAR_HEIGHT: f64 = 34.0;
const LEFT_INSET: f64 = 12.0;
let ns_window_ptr = match window.ns_window() { let ns_window_ptr = match window.ns_window() {
Ok(ptr) => ptr, Ok(ptr) => ptr,
Err(_) => return, Err(_) => return,
}; };
let ns_window: &NSWindow = unsafe { &*(ns_window_ptr as *const NSWindow) }; let ns_window: &NSWindow = unsafe { &*(ns_window_ptr as *const NSWindow) };
let close = match ns_window.standardWindowButton(NSWindowButton::CloseButton) { let close = match ns_window.standardWindowButton(NSWindowButton::CloseButton) {
Some(btn) => btn, Some(button) => button,
None => return, None => return,
}; };
let miniaturize = match ns_window.standardWindowButton(NSWindowButton::MiniaturizeButton) { let miniaturize = match ns_window.standardWindowButton(NSWindowButton::MiniaturizeButton) {
Some(btn) => btn, Some(button) => button,
None => return, None => return,
}; };
let zoom = match ns_window.standardWindowButton(NSWindowButton::ZoomButton) { let zoom = match ns_window.standardWindowButton(NSWindowButton::ZoomButton) {
Some(btn) => btn, Some(button) => button,
None => return, None => return,
}; };
let superview = match close.superview() { let window_height = ns_window.frame().size.height;
Some(sv) => sv, if window_height <= 0.0 {
None => return,
};
let sv_height = NSView::frame(&superview).size.height;
let btn_height = NSView::frame(&close).size.height;
if sv_height <= 0.0 || btn_height <= 0.0 {
return; return;
} }
let centered_y = (sv_height - btn_height) / 2.0;
for btn in [&close, &miniaturize, &zoom] { let close_frame = NSView::frame(&close);
let mut frame = NSView::frame(btn); let button_spacing = NSView::frame(&miniaturize).origin.x - close_frame.origin.x;
frame.origin.y = centered_y; for (index, button) in [&close, &miniaturize, &zoom].into_iter().enumerate() {
btn.setFrameOrigin(frame.origin); let button_superview = match unsafe { button.superview() } {
Some(view) => view,
None => return,
};
let mut frame = NSView::frame(button);
let top_inset = ((TITLEBAR_HEIGHT - frame.size.height) / 2.0).max(0.0);
let mut target = frame.origin;
target.y = window_height - top_inset - frame.size.height;
frame.origin.y = button_superview.convertPoint_fromView(target, None).y;
frame.origin.x = LEFT_INSET + index as f64 * button_spacing;
button.setFrameOrigin(frame.origin);
} }
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "HelixNotes", "productName": "HelixNotes",
"version": "1.3.3", "version": "1.3.4",
"identifier": "com.helixnotes.app", "identifier": "com.helixnotes.app",
"build": { "build": {
"frontendDist": "../build", "frontendDist": "../build",
+1 -1
View File
@@ -9,7 +9,7 @@
"hiddenTitle": true, "hiddenTitle": true,
"trafficLightPosition": { "trafficLightPosition": {
"x": 12, "x": 12,
"y": 12 "y": 10
} }
} }
] ]
+21 -18
View File
@@ -614,25 +614,28 @@
// Run sidebar and note list refresh in parallel // Run sidebar and note list refresh in parallel
await Promise.all([sidebar?.refresh(), noteList?.refresh()]); await Promise.all([sidebar?.refresh(), noteList?.refresh()]);
// Auto-cleanup orphaned attachments in the background // Full-vault attachment scans cause navigation stalls on mobile storage.
setTimeout(async () => { // Mobile users can run the same cleanup explicitly from the Info panel.
if (get(editorDirty)) return; // skip if user is actively editing if (!isMobile) {
try { setTimeout(async () => {
const orphans = await findOrphanedAttachments(); if (get(editorDirty)) return; // skip if user is actively editing
if (orphans.length > 0) { try {
if (get(editorDirty)) return; // re-check after async scan const orphans = await findOrphanedAttachments();
const moved = await trashOrphanedAttachments(orphans.map((o) => o.name)); if (orphans.length > 0) {
if (moved > 0) { if (get(editorDirty)) return; // re-check after async scan
const toast = document.createElement('div'); const moved = await trashOrphanedAttachments(orphans.map((o) => o.name));
toast.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--bg-secondary);color:var(--text-primary);padding:10px 18px;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15);border:1px solid var(--border-color);font-size:13px;z-index:10000;opacity:0;transition:opacity .3s;pointer-events:none'; if (moved > 0) {
toast.textContent = `Moved ${moved} orphaned attachment${moved > 1 ? 's' : ''} to trash`; const toast = document.createElement('div');
document.body.appendChild(toast); toast.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--bg-secondary);color:var(--text-primary);padding:10px 18px;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15);border:1px solid var(--border-color);font-size:13px;z-index:10000;opacity:0;transition:opacity .3s;pointer-events:none';
requestAnimationFrame(() => (toast.style.opacity = '1')); toast.textContent = `Moved ${moved} orphaned attachment${moved > 1 ? 's' : ''} to trash`;
setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 4000); document.body.appendChild(toast);
requestAnimationFrame(() => (toast.style.opacity = '1'));
setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 4000);
}
} }
} } catch (_) {}
} catch (_) {} }, 3000);
}, 3000); }
// Restore the last session (view + open note) if enabled. // Restore the last session (view + open note) if enabled.
if ($appConfig?.restore_last_session) { if ($appConfig?.restore_last_session) {
+6 -1
View File
@@ -1112,7 +1112,7 @@
{/if} {/if}
<!-- svelte-ignore a11y_no_static_element_interactions --> <!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="list-content" bind:this={listContainer} onscroll={onListScroll} ondblclick={handleListDoubleClick}> <div class="list-content" class:tasks-mode={$viewMode === 'tasks'} bind:this={listContainer} onscroll={onListScroll} ondblclick={handleListDoubleClick}>
{#if $viewMode === 'tasks'} {#if $viewMode === 'tasks'}
<TasksView onOpenTask={openTask} onToggleTask={onToggleTask} onSetTaskPriority={onSetTaskPriority} onSetTaskDue={onSetTaskDue} /> <TasksView onOpenTask={openTask} onToggleTask={onToggleTask} onSetTaskPriority={onSetTaskPriority} onSetTaskDue={onSetTaskDue} />
{/if} {/if}
@@ -2213,6 +2213,11 @@
padding: 4px 8px 180px; padding: 4px 8px 180px;
} }
.note-list.mobile .list-content.tasks-mode {
padding-bottom: 0;
overflow: hidden;
}
.note-list.mobile .note-item { .note-list.mobile .note-item {
padding: 14px 16px; padding: 14px 16px;
min-height: 56px; min-height: 56px;
+1
View File
@@ -807,6 +807,7 @@
class="notebook-item" class="notebook-item"
class:active={$viewMode === 'notebook' && $activeNotebook?.relative_path === ''} class:active={$viewMode === 'notebook' && $activeNotebook?.relative_path === ''}
onclick={selectUnfiled} onclick={selectUnfiled}
style="padding-left: 4px"
> >
<span style="width:14px;flex-shrink:0"></span> <span style="width:14px;flex-shrink:0"></span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.6"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.6">
+16 -7
View File
@@ -4,6 +4,7 @@
import { getAppConfig, openVault, restoreExternalVault, setFontSize } from '$lib/api'; import { getAppConfig, openVault, restoreExternalVault, setFontSize } from '$lib/api';
import { darkThemes, isIOS } from '$lib/platform'; import { darkThemes, isIOS } from '$lib/platform';
import { getCurrentWebview } from '@tauri-apps/api/webview'; import { getCurrentWebview } from '@tauri-apps/api/webview';
import { listen } from '@tauri-apps/api/event';
import VaultPicker from '$lib/components/VaultPicker.svelte'; import VaultPicker from '$lib/components/VaultPicker.svelte';
import AppLayout from '$lib/components/AppLayout.svelte'; import AppLayout from '$lib/components/AppLayout.svelte';
import NoteWindow from '$lib/components/NoteWindow.svelte'; import NoteWindow from '$lib/components/NoteWindow.svelte';
@@ -13,6 +14,7 @@
let startupVaultError = $state(''); let startupVaultError = $state('');
let fontSizeSaveTimer: ReturnType<typeof setTimeout> | null = null; let fontSizeSaveTimer: ReturnType<typeof setTimeout> | null = null;
let removeEditorZoomShortcuts: (() => void) | null = null; let removeEditorZoomShortcuts: (() => void) | null = null;
let removeFontSizeListener: (() => void) | null = null;
const defaultEditorFontSize = 14; const defaultEditorFontSize = 14;
const minEditorFontSize = 10; const minEditorFontSize = 10;
@@ -56,12 +58,17 @@
}; };
} }
function setEditorFontSize(value: number) { function applyEditorFontSize(value: number) {
const nextSize = Math.max(minEditorFontSize, Math.min(maxEditorFontSize, Math.round(value))); const nextSize = Math.max(minEditorFontSize, Math.min(maxEditorFontSize, Math.round(value)));
const restoreEditorViewport = preserveEditorViewportAfterLayout(getEditorScrollSurface()); const restoreEditorViewport = preserveEditorViewportAfterLayout(getEditorScrollSurface());
if ($appConfig) $appConfig.font_size = nextSize; if ($appConfig) $appConfig.font_size = nextSize;
document.documentElement.style.setProperty('--editor-font-size', `${nextSize}px`); document.documentElement.style.setProperty('--editor-font-size', `${nextSize}px`);
restoreEditorViewport(); restoreEditorViewport();
return nextSize;
}
function setEditorFontSize(value: number) {
const nextSize = applyEditorFontSize(value);
if (fontSizeSaveTimer) clearTimeout(fontSizeSaveTimer); if (fontSizeSaveTimer) clearTimeout(fontSizeSaveTimer);
fontSizeSaveTimer = setTimeout(() => { fontSizeSaveTimer = setTimeout(() => {
setFontSize(nextSize).catch((e) => console.error('Failed to save font size:', e)); setFontSize(nextSize).catch((e) => console.error('Failed to save font size:', e));
@@ -117,6 +124,9 @@
try { try {
const config = await getAppConfig(); const config = await getAppConfig();
$appConfig = config; $appConfig = config;
removeFontSizeListener = await listen<number>('editor-font-size-changed', (event) => {
if ($appConfig?.font_size !== event.payload) applyEditorFontSize(event.payload);
});
$theme = config.theme || 'system'; $theme = config.theme || 'system';
// Apply theme immediately to prevent flash // Apply theme immediately to prevent flash
@@ -133,9 +143,7 @@
} }
// Apply saved font settings // Apply saved font settings
if (config.font_size) { if (config.font_size) applyEditorFontSize(config.font_size);
document.documentElement.style.setProperty('--editor-font-size', `${config.font_size}px`);
}
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));
} }
@@ -193,15 +201,15 @@
} }
} }
// Apply saved interface scale (desktop main window only; no-ops gracefully elsewhere) // Apply saved interface scale to every desktop window; no-ops gracefully elsewhere.
if (!noteWindowPath && config.ui_scale && config.ui_scale !== 1) { if (config.ui_scale && config.ui_scale !== 1) {
try { try {
await getCurrentWebview().setZoom(config.ui_scale); await getCurrentWebview().setZoom(config.ui_scale);
} catch (e) { } catch (e) {
console.error('Failed to apply interface scale:', e); console.error('Failed to apply interface scale:', e);
} }
} }
if (!noteWindowPath) removeEditorZoomShortcuts = installEditorZoomShortcuts(); removeEditorZoomShortcuts = installEditorZoomShortcuts();
// Auto-open last vault if available // Auto-open last vault if available
if (config.active_vault) { if (config.active_vault) {
@@ -240,6 +248,7 @@
onDestroy(() => { onDestroy(() => {
removeEditorZoomShortcuts?.(); removeEditorZoomShortcuts?.();
removeFontSizeListener?.();
if (fontSizeSaveTimer) clearTimeout(fontSizeSaveTimer); if (fontSizeSaveTimer) clearTimeout(fontSizeSaveTimer);
}); });
</script> </script>