diff --git a/frontend/app.js b/frontend/app.js index fda8b46..f36450e 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -11,6 +11,22 @@ const CONFIG = { DEFAULT_SIDEBAR_WIDTH: 256, // px - Default sidebar width (w-64 in Tailwind) }; +// localStorage settings configuration - centralized definition of all persisted settings +const LOCAL_SETTINGS = { + // Boolean settings + syntaxHighlightEnabled: { key: 'syntaxHighlightEnabled', type: 'boolean', default: false }, + readableLineLength: { key: 'readableLineLength', type: 'boolean', default: true }, + favoritesExpanded: { key: 'favoritesExpanded', type: 'boolean', default: true }, + tagsExpanded: { key: 'tagsExpanded', type: 'boolean', default: false }, + // Number settings with validation + sidebarWidth: { key: 'sidebarWidth', type: 'number', default: CONFIG.DEFAULT_SIDEBAR_WIDTH, min: 200, max: 600 }, + editorWidth: { key: 'editorWidth', type: 'number', default: 50, min: 20, max: 80 }, + // String settings with validation + viewMode: { key: 'viewMode', type: 'string', default: 'split', valid: ['edit', 'split', 'preview'] }, + // JSON settings + favorites: { key: 'noteFavorites', type: 'json', default: [] }, +}; + // Centralized error handling const ErrorHandler = { /** @@ -190,6 +206,9 @@ function noteApp() { syntaxHighlightEnabled: false, syntaxHighlightTimeout: null, + // Readable line length (preview max-width) + readableLineLength: true, + // Icon rail / panel state activePanel: 'files', // 'files', 'search', 'tags', 'settings' @@ -458,13 +477,7 @@ function noteApp() { await this.loadSharedNotePaths(); await this.loadTemplates(); await this.checkStatsPlugin(); - this.loadSidebarWidth(); - this.loadEditorWidth(); - this.loadViewMode(); - this.loadTagsExpanded(); - this.loadFavorites(); - this.loadFavoritesExpanded(); - this.loadSyntaxHighlightSetting(); + this.loadLocalSettings(); // Parse URL and load specific note if provided this.loadItemFromURL(); @@ -745,13 +758,51 @@ function noteApp() { } }, - loadSyntaxHighlightSetting() { - try { - const saved = localStorage.getItem('syntaxHighlightEnabled'); - this.syntaxHighlightEnabled = saved === 'true'; - } catch (error) { - console.error('Error loading syntax highlight setting:', error); + // Load all localStorage settings at once using centralized config + loadLocalSettings() { + for (const [prop, config] of Object.entries(LOCAL_SETTINGS)) { + try { + const saved = localStorage.getItem(config.key); + + if (saved === null) { + // Use default value if not set + this[prop] = config.default; + } else if (config.type === 'boolean') { + this[prop] = saved === 'true'; + } else if (config.type === 'number') { + const num = parseFloat(saved); + // Validate range if specified + if (!isNaN(num) && + (config.min === undefined || num >= config.min) && + (config.max === undefined || num <= config.max)) { + this[prop] = num; + } else { + this[prop] = config.default; + } + } else if (config.type === 'string') { + // Validate against allowed values if specified + if (!config.valid || config.valid.includes(saved)) { + this[prop] = saved; + } else { + this[prop] = config.default; + } + } else if (config.type === 'json') { + this[prop] = JSON.parse(saved); + } + } catch (error) { + console.error(`Error loading setting ${prop}:`, error); + this[prop] = config.default; + } } + + // Special case: favorites also needs to update the Set for O(1) lookups + this.favoritesSet = new Set(this.favorites); + }, + + // Readable line length toggle (for preview max-width) + toggleReadableLineLength() { + this.readableLineLength = !this.readableLineLength; + localStorage.setItem('readableLineLength', this.readableLineLength); }, // Update syntax highlight overlay (debounced, called on input) @@ -1439,20 +1490,6 @@ function noteApp() { // ==================== FAVORITES ==================== - // Load favorites from localStorage - loadFavorites() { - try { - const stored = localStorage.getItem('noteFavorites'); - if (stored) { - this.favorites = JSON.parse(stored); - this.favoritesSet = new Set(this.favorites); - } - } catch (e) { - this.favorites = []; - this.favoritesSet = new Set(); - } - }, - // Save favorites to localStorage saveFavorites() { try { @@ -1503,17 +1540,6 @@ function noteApp() { .filter(Boolean); // Remove nulls (deleted notes) }, - loadFavoritesExpanded() { - try { - const saved = localStorage.getItem('favoritesExpanded'); - if (saved !== null) { - this.favoritesExpanded = saved === 'true'; - } - } catch (e) { - console.error('Error loading favorites expanded state:', e); - } - }, - saveFavoritesExpanded() { try { localStorage.setItem('favoritesExpanded', this.favoritesExpanded.toString()); @@ -4770,34 +4796,11 @@ function noteApp() { return Array.isArray(this.noteMetadata.tags) ? this.noteMetadata.tags : [this.noteMetadata.tags]; }, - // Load sidebar width from localStorage - loadSidebarWidth() { - const saved = localStorage.getItem('sidebarWidth'); - if (saved) { - const width = parseInt(saved, 10); - if (width >= 200 && width <= 600) { - this.sidebarWidth = width; - } - } - }, - // Save sidebar width to localStorage saveSidebarWidth() { localStorage.setItem('sidebarWidth', this.sidebarWidth.toString()); }, - // Load view mode from localStorage - loadViewMode() { - try { - const saved = localStorage.getItem('viewMode'); - if (saved && ['edit', 'split', 'preview'].includes(saved)) { - this.viewMode = saved; - } - } catch (error) { - console.error('Error loading view mode:', error); - } - }, - // Save view mode to localStorage saveViewMode() { try { @@ -4807,17 +4810,6 @@ function noteApp() { } }, - loadTagsExpanded() { - try { - const saved = localStorage.getItem('tagsExpanded'); - if (saved !== null) { - this.tagsExpanded = saved === 'true'; - } - } catch (error) { - console.error('Error loading tags expanded state:', error); - } - }, - saveTagsExpanded() { try { localStorage.setItem('tagsExpanded', this.tagsExpanded.toString()); @@ -4916,17 +4908,6 @@ function noteApp() { } }, - // Load editor width from localStorage - loadEditorWidth() { - const saved = localStorage.getItem('editorWidth'); - if (saved) { - const width = parseFloat(saved); - if (width >= 20 && width <= 80) { - this.editorWidth = width; - } - } - }, - // Save editor width to localStorage saveEditorWidth() { localStorage.setItem('editorWidth', this.editorWidth.toString()); diff --git a/frontend/index.html b/frontend/index.html index f915c1c..0f0ea01 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -170,6 +170,13 @@ font-size: 16px; } + /* Readable line length - constrain text width for better readability */ + .markdown-preview.readable-width { + max-width: 750px; + margin-left: auto; + margin-right: auto; + } + /* Remove top margin from first element */ .markdown-preview > *:first-child { margin-top: 0 !important; @@ -1829,6 +1836,24 @@

+ +
+ +

+
+
@@ -2576,7 +2601,7 @@
diff --git a/locales/de-DE.json b/locales/de-DE.json index c1c89f6..d1ee9c8 100644 --- a/locales/de-DE.json +++ b/locales/de-DE.json @@ -235,7 +235,9 @@ "settings": { "account": "Konto", - "logout": "Abmelden" + "logout": "Abmelden", + "readable_line_length": "Lesbare Zeilenlänge", + "readable_line_length_desc": "Vorschaubreite für bessere Lesbarkeit begrenzen" }, "homepage": { diff --git a/locales/en-GB.json b/locales/en-GB.json index 72317dd..40ec7c0 100644 --- a/locales/en-GB.json +++ b/locales/en-GB.json @@ -234,7 +234,9 @@ "settings": { "account": "Account", - "logout": "Logout" + "logout": "Logout", + "readable_line_length": "Readable line length", + "readable_line_length_desc": "Limit preview width for easier reading" }, "homepage": { diff --git a/locales/en-US.json b/locales/en-US.json index a691505..303881f 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -235,7 +235,9 @@ "settings": { "account": "Account", - "logout": "Logout" + "logout": "Logout", + "readable_line_length": "Readable line length", + "readable_line_length_desc": "Limit preview width for easier reading" }, "homepage": { diff --git a/locales/es-ES.json b/locales/es-ES.json index e0d10f6..193ca8e 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -235,7 +235,9 @@ "settings": { "account": "Cuenta", - "logout": "Cerrar sesión" + "logout": "Cerrar sesión", + "readable_line_length": "Longitud de línea legible", + "readable_line_length_desc": "Limitar el ancho de vista previa para facilitar la lectura" }, "homepage": { diff --git a/locales/fr-FR.json b/locales/fr-FR.json index 690261c..08914c7 100644 --- a/locales/fr-FR.json +++ b/locales/fr-FR.json @@ -235,7 +235,9 @@ "settings": { "account": "Compte", - "logout": "Déconnexion" + "logout": "Déconnexion", + "readable_line_length": "Longueur de ligne lisible", + "readable_line_length_desc": "Limiter la largeur de l'aperçu pour une lecture plus facile" }, "homepage": { diff --git a/locales/it-IT.json b/locales/it-IT.json index ccb67c2..93cbb7c 100644 --- a/locales/it-IT.json +++ b/locales/it-IT.json @@ -234,7 +234,9 @@ "settings": { "account": "Account", - "logout": "Esci" + "logout": "Esci", + "readable_line_length": "Larghezza riga leggibile", + "readable_line_length_desc": "Limita la larghezza dell'anteprima per una lettura più facile" }, "homepage": { diff --git a/locales/ja-JP.json b/locales/ja-JP.json index 504c034..bcb34d6 100644 --- a/locales/ja-JP.json +++ b/locales/ja-JP.json @@ -234,7 +234,9 @@ "settings": { "account": "アカウント", - "logout": "ログアウト" + "logout": "ログアウト", + "readable_line_length": "読みやすい行幅", + "readable_line_length_desc": "プレビューの幅を制限して読みやすくする" }, "homepage": { diff --git a/locales/ru-RU.json b/locales/ru-RU.json index 380def7..85b8b2c 100644 --- a/locales/ru-RU.json +++ b/locales/ru-RU.json @@ -234,7 +234,9 @@ "settings": { "account": "Аккаунт", - "logout": "Выйти" + "logout": "Выйти", + "readable_line_length": "Удобная длина строки", + "readable_line_length_desc": "Ограничить ширину предпросмотра для удобства чтения" }, "homepage": { diff --git a/locales/sl-SI.json b/locales/sl-SI.json index be25f2b..3a7f9a8 100644 --- a/locales/sl-SI.json +++ b/locales/sl-SI.json @@ -234,7 +234,9 @@ "settings": { "account": "Račun", - "logout": "Odjava" + "logout": "Odjava", + "readable_line_length": "Berljiva dolžina vrstice", + "readable_line_length_desc": "Omejite širino predogleda za lažje branje" }, "homepage": { diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 82954b8..5f4b4bf 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -234,7 +234,9 @@ "settings": { "account": "账户", - "logout": "登出" + "logout": "登出", + "readable_line_length": "可读行宽", + "readable_line_length_desc": "限制预览宽度以便于阅读" }, "homepage": {