mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-09-20 01:47:29 +02:00
Add Markdown source highlighting (#173)
This commit is contained in:
@@ -1398,6 +1398,7 @@ pub fn set_general_settings(
|
|||||||
show_line_numbers: bool,
|
show_line_numbers: bool,
|
||||||
show_link_arrows: bool,
|
show_link_arrows: bool,
|
||||||
default_view_mode: bool,
|
default_view_mode: bool,
|
||||||
|
new_notes_in_source_mode: bool,
|
||||||
show_tray_icon: bool,
|
show_tray_icon: bool,
|
||||||
close_to_tray: bool,
|
close_to_tray: bool,
|
||||||
enable_wiki_links: bool,
|
enable_wiki_links: bool,
|
||||||
@@ -1432,6 +1433,7 @@ pub fn set_general_settings(
|
|||||||
config.show_line_numbers = show_line_numbers;
|
config.show_line_numbers = show_line_numbers;
|
||||||
config.show_link_arrows = show_link_arrows;
|
config.show_link_arrows = show_link_arrows;
|
||||||
config.default_view_mode = default_view_mode;
|
config.default_view_mode = default_view_mode;
|
||||||
|
config.new_notes_in_source_mode = new_notes_in_source_mode;
|
||||||
config.show_tray_icon = show_tray_icon;
|
config.show_tray_icon = show_tray_icon;
|
||||||
config.close_to_tray = close_to_tray;
|
config.close_to_tray = close_to_tray;
|
||||||
config.enable_wiki_links = enable_wiki_links;
|
config.enable_wiki_links = enable_wiki_links;
|
||||||
|
|||||||
@@ -197,6 +197,8 @@ pub struct AppConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub default_view_mode: bool,
|
pub default_view_mode: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub new_notes_in_source_mode: bool,
|
||||||
|
#[serde(default)]
|
||||||
pub show_tray_icon: bool,
|
pub show_tray_icon: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub close_to_tray: bool,
|
pub close_to_tray: bool,
|
||||||
@@ -315,6 +317,7 @@ impl Default for AppConfig {
|
|||||||
ai_model: "claude-sonnet-4-6".to_string(),
|
ai_model: "claude-sonnet-4-6".to_string(),
|
||||||
ai_writing_style: None,
|
ai_writing_style: None,
|
||||||
default_view_mode: false,
|
default_view_mode: false,
|
||||||
|
new_notes_in_source_mode: false,
|
||||||
show_tray_icon: false,
|
show_tray_icon: false,
|
||||||
close_to_tray: false,
|
close_to_tray: false,
|
||||||
enable_wiki_links: true,
|
enable_wiki_links: true,
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ export async function setGeneralSettings(
|
|||||||
showLineNumbers: boolean,
|
showLineNumbers: boolean,
|
||||||
showLinkArrows: boolean,
|
showLinkArrows: boolean,
|
||||||
defaultViewMode: boolean,
|
defaultViewMode: boolean,
|
||||||
|
newNotesInSourceMode: boolean,
|
||||||
showTrayIcon: boolean,
|
showTrayIcon: boolean,
|
||||||
closeToTray: boolean,
|
closeToTray: boolean,
|
||||||
enableWikiLinks: boolean,
|
enableWikiLinks: boolean,
|
||||||
@@ -291,6 +292,7 @@ export async function setGeneralSettings(
|
|||||||
showLineNumbers,
|
showLineNumbers,
|
||||||
showLinkArrows,
|
showLinkArrows,
|
||||||
defaultViewMode,
|
defaultViewMode,
|
||||||
|
newNotesInSourceMode,
|
||||||
showTrayIcon,
|
showTrayIcon,
|
||||||
closeToTray,
|
closeToTray,
|
||||||
enableWikiLinks,
|
enableWikiLinks,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
import TextAlign from '@tiptap/extension-text-align';
|
import TextAlign from '@tiptap/extension-text-align';
|
||||||
import { common, createLowlight } from 'lowlight';
|
import { common, createLowlight } from 'lowlight';
|
||||||
import powershell from 'highlight.js/lib/languages/powershell';
|
import powershell from 'highlight.js/lib/languages/powershell';
|
||||||
|
import hljs from 'highlight.js/lib/core';
|
||||||
|
import markdownLanguage from 'highlight.js/lib/languages/markdown';
|
||||||
import MarkdownIt from 'markdown-it';
|
import MarkdownIt from 'markdown-it';
|
||||||
import markdownItMark from 'markdown-it-mark';
|
import markdownItMark from 'markdown-it-mark';
|
||||||
import markdownItSup from 'markdown-it-sup';
|
import markdownItSup from 'markdown-it-sup';
|
||||||
@@ -56,6 +58,9 @@
|
|||||||
import ResizeHandle from './ResizeHandle.svelte';
|
import ResizeHandle from './ResizeHandle.svelte';
|
||||||
|
|
||||||
const modKey = navigator.platform.startsWith('Mac') ? '⌘' : 'Ctrl';
|
const modKey = navigator.platform.startsWith('Mac') ? '⌘' : 'Ctrl';
|
||||||
|
const sourceHighlighter = hljs.newInstance();
|
||||||
|
sourceHighlighter.registerLanguage('markdown', markdownLanguage);
|
||||||
|
const SOURCE_HIGHLIGHT_MAX_CHARS = 32_000;
|
||||||
|
|
||||||
// Track virtual keyboard height on mobile via visualViewport
|
// Track virtual keyboard height on mobile via visualViewport
|
||||||
let keyboardHeight = $state(0);
|
let keyboardHeight = $state(0);
|
||||||
@@ -68,6 +73,7 @@
|
|||||||
|
|
||||||
let editorElement = $state<HTMLDivElement>(null!);
|
let editorElement = $state<HTMLDivElement>(null!);
|
||||||
let sourceElement = $state<HTMLTextAreaElement>(null!);
|
let sourceElement = $state<HTMLTextAreaElement>(null!);
|
||||||
|
let sourceHighlightElement = $state<HTMLPreElement>(null!);
|
||||||
const LARGE_DOC_CHARS = 100_000;
|
const LARGE_DOC_CHARS = 100_000;
|
||||||
let isLargeDoc = $state(false);
|
let isLargeDoc = $state(false);
|
||||||
let editor: Editor | null = null;
|
let editor: Editor | null = null;
|
||||||
@@ -83,6 +89,10 @@
|
|||||||
});
|
});
|
||||||
let editorReady = $state(false);
|
let editorReady = $state(false);
|
||||||
let sourceContent = $state('');
|
let sourceContent = $state('');
|
||||||
|
let sourceHighlightHtml = $derived.by(() => {
|
||||||
|
if (sourceContent.length > SOURCE_HIGHLIGHT_MAX_CHARS) return escapeHtml(sourceContent);
|
||||||
|
return sourceHighlighter.highlight(sourceContent, { language: 'markdown', ignoreIllegals: true }).value;
|
||||||
|
});
|
||||||
let sourceHistory: Array<{ content: string; cursor: number }> = [];
|
let sourceHistory: Array<{ content: string; cursor: number }> = [];
|
||||||
let sourceHistoryIndex = -1;
|
let sourceHistoryIndex = -1;
|
||||||
let sourceHistoryTimer: ReturnType<typeof setTimeout> | null = null;
|
let sourceHistoryTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
@@ -123,6 +133,18 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncSourceEditorScroll() {
|
||||||
|
if (sourceHighlightElement && sourceElement) {
|
||||||
|
sourceHighlightElement.scrollTop = sourceElement.scrollTop;
|
||||||
|
sourceHighlightElement.scrollLeft = sourceElement.scrollLeft;
|
||||||
|
}
|
||||||
|
if ($appConfig?.show_line_numbers && sourceElement) {
|
||||||
|
const clip = sourceElement.closest('.editor-body')?.querySelector('.line-numbers-clip');
|
||||||
|
const gutter = clip?.firstElementChild as HTMLElement | null;
|
||||||
|
if (gutter) gutter.style.transform = `translateY(-${sourceElement.scrollTop}px)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clearTaskReveal() {
|
function clearTaskReveal() {
|
||||||
if (taskRevealTimer) clearTimeout(taskRevealTimer);
|
if (taskRevealTimer) clearTimeout(taskRevealTimer);
|
||||||
taskRevealTimer = null;
|
taskRevealTimer = null;
|
||||||
@@ -3271,13 +3293,16 @@
|
|||||||
const revealRequest = ++taskRevealRequest;
|
const revealRequest = ++taskRevealRequest;
|
||||||
const revealTarget = taskTarget ? resolveTaskTarget(taskTarget, content) : null;
|
const revealTarget = taskTarget ? resolveTaskTarget(taskTarget, content) : null;
|
||||||
loadedPath = path;
|
loadedPath = path;
|
||||||
lastSourceMode = $sourceMode;
|
|
||||||
isLoadingNote = true;
|
isLoadingNote = true;
|
||||||
isLargeDoc = content.length > LARGE_DOC_CHARS;
|
isLargeDoc = content.length > LARGE_DOC_CHARS;
|
||||||
// Apply default view mode when switching notes - but new notes always open in edit mode.
|
// Viewer mode (external file) always forces read-only. New notes stay editable and
|
||||||
// Viewer mode (external file) always forces read-only.
|
// can opt into source mode without changing how existing notes choose their mode.
|
||||||
const isViewer = !!get(viewerNote);
|
const isViewer = !!get(viewerNote);
|
||||||
const isNewNote = $activeNote?.meta.title === 'Untitled' && !content.replace(/^---[\s\S]*?---\s*/, '').trim();
|
const isNewNote = $activeNote?.meta.title === 'Untitled' && !content.replace(/^---[\s\S]*?---\s*/, '').trim();
|
||||||
|
if (isNewNote && ($appConfig?.new_notes_in_source_mode ?? false)) {
|
||||||
|
$sourceMode = true;
|
||||||
|
}
|
||||||
|
lastSourceMode = $sourceMode;
|
||||||
const shouldBeReadOnly = isViewer ? true : (isNewNote ? false : ($appConfig?.default_view_mode ?? false));
|
const shouldBeReadOnly = isViewer ? true : (isNewNote ? false : ($appConfig?.default_view_mode ?? false));
|
||||||
$readOnly = shouldBeReadOnly;
|
$readOnly = shouldBeReadOnly;
|
||||||
if (editor) editor.setEditable(!shouldBeReadOnly);
|
if (editor) editor.setEditable(!shouldBeReadOnly);
|
||||||
@@ -6240,9 +6265,10 @@
|
|||||||
<div class="editor-body">
|
<div class="editor-body">
|
||||||
{#if isMobile}
|
{#if isMobile}
|
||||||
<!-- Mobile: both views always in DOM, toggled via display to avoid slow editor re-creation -->
|
<!-- Mobile: both views always in DOM, toggled via display to avoid slow editor re-creation -->
|
||||||
|
<div class="source-editor-layer" style={$sourceMode ? '' : 'display:none'}>
|
||||||
|
<pre class="source-highlight" aria-hidden="true" bind:this={sourceHighlightElement}><code>{@html sourceHighlightHtml}</code></pre>
|
||||||
<textarea
|
<textarea
|
||||||
class="source-editor"
|
class="source-editor"
|
||||||
style={$sourceMode ? '' : 'display:none'}
|
|
||||||
bind:this={sourceElement}
|
bind:this={sourceElement}
|
||||||
bind:value={sourceContent}
|
bind:value={sourceContent}
|
||||||
readonly={$readOnly}
|
readonly={$readOnly}
|
||||||
@@ -6282,8 +6308,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onscroll={syncSourceEditorScroll}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
</div>
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div class="tiptap-wrapper" class:large-doc={isLargeDoc} style={$sourceMode ? 'display:none' : ''} spellcheck="false" bind:this={editorElement} onclick={(e) => { closeLinkContextMenu(); handleEditorClick(e); }}></div>
|
<div class="tiptap-wrapper" class:large-doc={isLargeDoc} style={$sourceMode ? 'display:none' : ''} spellcheck="false" bind:this={editorElement} onclick={(e) => { closeLinkContextMenu(); handleEditorClick(e); }}></div>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -6298,6 +6326,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="source-editor-layer" class:with-line-numbers={$appConfig?.show_line_numbers}>
|
||||||
|
<pre class="source-highlight" aria-hidden="true" bind:this={sourceHighlightElement}><code>{@html sourceHighlightHtml}</code></pre>
|
||||||
<textarea
|
<textarea
|
||||||
class="source-editor"
|
class="source-editor"
|
||||||
class:with-line-numbers={$appConfig?.show_line_numbers}
|
class:with-line-numbers={$appConfig?.show_line_numbers}
|
||||||
@@ -6381,17 +6411,10 @@
|
|||||||
autoSave();
|
autoSave();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onscroll={() => {
|
onscroll={syncSourceEditorScroll}
|
||||||
if ($appConfig?.show_line_numbers) {
|
|
||||||
const clip = sourceElement?.previousElementSibling as HTMLElement;
|
|
||||||
const gutter = clip?.firstElementChild as HTMLElement;
|
|
||||||
if (gutter) {
|
|
||||||
gutter.style.transform = `translateY(-${sourceElement.scrollTop}px)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div class="tiptap-wrapper" class:large-doc={isLargeDoc} spellcheck="false" bind:this={editorElement} onclick={(e) => { closeLinkContextMenu(); handleEditorClick(e); }} oncontextmenu={handleEditorContextMenu}></div>
|
<div class="tiptap-wrapper" class:large-doc={isLargeDoc} spellcheck="false" bind:this={editorElement} onclick={(e) => { closeLinkContextMenu(); handleEditorClick(e); }} oncontextmenu={handleEditorContextMenu}></div>
|
||||||
@@ -8587,36 +8610,74 @@
|
|||||||
.outline-level-5 { padding-left: 62px; }
|
.outline-level-5 { padding-left: 62px; }
|
||||||
.outline-level-6 { padding-left: 74px; }
|
.outline-level-6 { padding-left: 74px; }
|
||||||
|
|
||||||
|
.source-editor-layer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: var(--editor-content-width, none);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-highlight,
|
||||||
.source-editor {
|
.source-editor {
|
||||||
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
||||||
font-size: var(--editor-font-size, 14px);
|
font-size: var(--editor-font-size, 14px);
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
resize: none;
|
|
||||||
outline: none;
|
|
||||||
padding: 0 0 var(--editor-scroll-past-end, 65vh);
|
padding: 0 0 var(--editor-scroll-past-end, 65vh);
|
||||||
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;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-highlight {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-highlight code {
|
||||||
|
font: inherit;
|
||||||
|
white-space: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-editor {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background: transparent;
|
||||||
|
color: transparent;
|
||||||
|
caret-color: var(--text-primary);
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
user-select: text;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-editor.with-line-numbers {
|
.source-editor-layer.with-line-numbers {
|
||||||
|
max-width: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-editor.with-line-numbers,
|
||||||
|
.source-editor-layer.with-line-numbers .source-highlight {
|
||||||
padding-left: 48px;
|
padding-left: 48px;
|
||||||
/* The line-number gutter has one fixed row per line, so wrapping would desync it.
|
/* The line-number gutter has one fixed row per line, so wrapping would desync it.
|
||||||
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;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-editor.with-line-numbers {
|
||||||
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 {
|
||||||
@@ -8646,6 +8707,36 @@
|
|||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Markdown source highlighting stays color-only so glyph metrics match the textarea caret. */
|
||||||
|
:global(.source-highlight .hljs-section),
|
||||||
|
:global(.source-highlight .hljs-strong) {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.source-highlight .hljs-string),
|
||||||
|
:global(.source-highlight .hljs-link) {
|
||||||
|
color: color-mix(in srgb, var(--accent) 78%, var(--text-primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.source-highlight .hljs-bullet),
|
||||||
|
:global(.source-highlight .hljs-symbol),
|
||||||
|
:global(.source-highlight .hljs-meta),
|
||||||
|
:global(.source-highlight .hljs-attr) {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.source-highlight .hljs-quote),
|
||||||
|
:global(.source-highlight .hljs-code) {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.source-highlight .hljs-emphasis) {
|
||||||
|
color: color-mix(in srgb, var(--accent) 60%, var(--text-primary));
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.tiptap-wrapper {
|
.tiptap-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
@@ -11326,6 +11417,7 @@
|
|||||||
font-size: var(--editor-font-size, 16px) !important;
|
font-size: var(--editor-font-size, 16px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-container.mobile .source-highlight,
|
||||||
.editor-container.mobile .source-editor {
|
.editor-container.mobile .source-editor {
|
||||||
padding: 8px 16px 220px;
|
padding: 8px 16px 220px;
|
||||||
font-size: var(--editor-font-size, 15px);
|
font-size: var(--editor-font-size, 15px);
|
||||||
|
|||||||
@@ -717,6 +717,7 @@
|
|||||||
let showLineNumbers = $state($appConfig?.show_line_numbers ?? false);
|
let showLineNumbers = $state($appConfig?.show_line_numbers ?? false);
|
||||||
let showLinkArrows = $state($appConfig?.show_link_arrows ?? true);
|
let showLinkArrows = $state($appConfig?.show_link_arrows ?? true);
|
||||||
let defaultViewMode = $state($appConfig?.default_view_mode ?? false);
|
let defaultViewMode = $state($appConfig?.default_view_mode ?? false);
|
||||||
|
let newNotesInSourceMode = $state($appConfig?.new_notes_in_source_mode ?? false);
|
||||||
let showTrayIcon = $state($appConfig?.show_tray_icon ?? false);
|
let showTrayIcon = $state($appConfig?.show_tray_icon ?? false);
|
||||||
let closeToTray = $state($appConfig?.close_to_tray ?? false);
|
let closeToTray = $state($appConfig?.close_to_tray ?? false);
|
||||||
let enableWikiLinks = $state($appConfig?.enable_wiki_links ?? true);
|
let enableWikiLinks = $state($appConfig?.enable_wiki_links ?? true);
|
||||||
@@ -789,6 +790,7 @@
|
|||||||
$appConfig.show_line_numbers = showLineNumbers;
|
$appConfig.show_line_numbers = showLineNumbers;
|
||||||
$appConfig.show_link_arrows = showLinkArrows;
|
$appConfig.show_link_arrows = showLinkArrows;
|
||||||
$appConfig.default_view_mode = defaultViewMode;
|
$appConfig.default_view_mode = defaultViewMode;
|
||||||
|
$appConfig.new_notes_in_source_mode = newNotesInSourceMode;
|
||||||
$appConfig.show_tray_icon = showTrayIcon;
|
$appConfig.show_tray_icon = showTrayIcon;
|
||||||
$appConfig.close_to_tray = closeToTray;
|
$appConfig.close_to_tray = closeToTray;
|
||||||
$appConfig.enable_wiki_links = enableWikiLinks;
|
$appConfig.enable_wiki_links = enableWikiLinks;
|
||||||
@@ -798,7 +800,7 @@
|
|||||||
$appConfig.show_daily_notes = showDailyNotes;
|
$appConfig.show_daily_notes = showDailyNotes;
|
||||||
$appConfig.show_trash = showTrash;
|
$appConfig.show_trash = showTrash;
|
||||||
}
|
}
|
||||||
setGeneralSettings(compactNotes, timeFormat, weekStart, dailyTitleFormat, gpuAcceleration, autostart, pdfPreview, pdfHeight, titleMode, hideTitleInBody, showLineNumbers, showLinkArrows, defaultViewMode, showTrayIcon, closeToTray, enableWikiLinks, showNoteDates, startupView, restoreLastSession, showAllNotes, showQuickAccess, showTasks, showDailyNotes, showTrash)
|
setGeneralSettings(compactNotes, timeFormat, weekStart, dailyTitleFormat, gpuAcceleration, autostart, pdfPreview, pdfHeight, titleMode, hideTitleInBody, showLineNumbers, showLinkArrows, defaultViewMode, newNotesInSourceMode, showTrayIcon, closeToTray, enableWikiLinks, showNoteDates, startupView, restoreLastSession, showAllNotes, showQuickAccess, showTasks, showDailyNotes, showTrash)
|
||||||
.catch((e) => console.error('Failed to save general settings:', e));
|
.catch((e) => console.error('Failed to save general settings:', e));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,6 +990,7 @@
|
|||||||
pdfPreview = $appConfig.pdf_preview ?? false;
|
pdfPreview = $appConfig.pdf_preview ?? false;
|
||||||
pdfHeight = $appConfig.pdf_height ?? 600;
|
pdfHeight = $appConfig.pdf_height ?? 600;
|
||||||
titleMode = $appConfig.title_mode ?? 'input';
|
titleMode = $appConfig.title_mode ?? 'input';
|
||||||
|
newNotesInSourceMode = $appConfig.new_notes_in_source_mode ?? false;
|
||||||
showAllNotes = $appConfig.show_all_notes ?? true;
|
showAllNotes = $appConfig.show_all_notes ?? true;
|
||||||
showQuickAccess = $appConfig.show_quick_access ?? true;
|
showQuickAccess = $appConfig.show_quick_access ?? true;
|
||||||
showTasks = $appConfig.show_tasks ?? true;
|
showTasks = $appConfig.show_tasks ?? true;
|
||||||
@@ -1309,8 +1312,18 @@
|
|||||||
<span class="toggle-knob"></span>
|
<span class="toggle-knob"></span>
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="setting-toggle" style="margin-top: 12px;">
|
||||||
|
<span class="setting-label">
|
||||||
|
<span class="setting-name">Open new notes in source mode</span>
|
||||||
|
<span class="setting-desc">Start empty notes in the plain-text Markdown editor instead of rich-text mode</span>
|
||||||
|
</span>
|
||||||
|
<button class="toggle-switch" class:on={newNotesInSourceMode} onclick={() => { newNotesInSourceMode = !newNotesInSourceMode; saveGeneralSettings(); }}>
|
||||||
|
<span class="toggle-knob"></span>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<h3>Wiki Links & Graph</h3>
|
<h3>Wiki Links & Graph</h3>
|
||||||
<label class="setting-toggle">
|
<label class="setting-toggle">
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export interface AppConfig {
|
|||||||
ai_model: string;
|
ai_model: string;
|
||||||
ai_writing_style: string | null;
|
ai_writing_style: string | null;
|
||||||
default_view_mode: boolean;
|
default_view_mode: boolean;
|
||||||
|
new_notes_in_source_mode: boolean;
|
||||||
show_tray_icon: boolean;
|
show_tray_icon: boolean;
|
||||||
close_to_tray: boolean;
|
close_to_tray: boolean;
|
||||||
enable_wiki_links: boolean;
|
enable_wiki_links: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user