Add WYSIWYG editor mode with formatting toolbar#1924
Add WYSIWYG editor mode with formatting toolbar#1924gjouret wants to merge 4 commits intoglushchenko:masterfrom
Conversation
- Add FormattingToolbar with buttons for bold, italic, underline, strikethrough, H1-H3, quote, bullet/numbered lists, checkbox, link, wiki-link, image, table, code block, and horizontal rule - All toolbar buttons route through first responder chain to EditTextView @IBAction methods (refusesFirstResponder keeps editor focused) - Enable hideSyntax by default (wysiwygMode user preference) - Cmd+/ toggles between WYSIWYG and Source mode instead of editor/WKWebView preview swap - Fix header ATX regex to match empty headers (`.+?` -> `.*?`) so # syntax hides immediately when creating new headers - Fix header opening syntax range — remove erroneous +1 that was eating the first content character of every header - Fix LayoutManager.font(for:) to find largest font in glyph range instead of using first character's font, preventing line height collapse when lines start with hidden syntax - Add horizontal rule rendering via LayoutManager custom drawing - Add blockquote left border rendering via LayoutManager - Hide inline code backticks in WYSIWYG mode - Add custom NSAttributedString.Key: .horizontalRule, .blockquote - Add TextFormatter methods: numberedList(), horizontalRule(), insertTable() - Return key after header exits header mode (no # continuation) - Wire FormattingToolbar into EditorViewController and NoteViewController view hierarchies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Table Editor: - Add TableEditorViewController with grid UI for creating/editing markdown tables (column/row steppers, header + data rows) - Insert Table toolbar button opens popup sheet instead of inserting raw markdown template - Parse existing tables and edit in-place Blockquote fixes: - Fix persistent blockquote bar: clear .blockquote and .horizontalRule attributes at start of each highlightMarkdown() pass so they only exist when regex matches - Fix bar overlapping text: draw bar at fixed left margin position instead of relative to text bounding rect - Fix blockquote > visibility: use clear foreground color with normal font size instead of 0.1pt hidden font, so cursor and layout work correctly while > is invisible - Fix quote text color: normal color in WYSIWYG, gray in source - Fix quote exit: rewrite matchChars() empty-line detection to strip prefix and check for empty content instead of fragile length arithmetic - Blockquote bar and HR only render in WYSIWYG mode, not source Other fixes: - Fix underline: detect <u>...</u> tags in highlightMarkdown(), apply .underlineStyle attribute, hide tags in WYSIWYG mode - Fix code block cursor: place at content area (+4) not language specifier (+3) to avoid accidental autocomplete - Fix focus border: EditTextView.becomeFirstResponder() now calls showFocusBorder() so clicking in editor shows blue frame - Fix wiki-link brackets: hide [[ and ]] via hideSyntaxIfNecessary using correct capture groups (1 and 3, not 0 and 2) - Hide code block fence lines (```) in WYSIWYG mode via TextStorageProcessor Navigation: - Add Back/Forward buttons (chevron.left/right) to toolbar - Track note navigation history in ViewController with browser-style back/forward stack (50 entries max) - Buttons enable/disable based on available history Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mermaid/code block rendering: - Add BlockRenderer for inline mermaid diagram rendering via WKWebView snapshot - Click-to-edit: click rendered diagram to restore source, edit, click away to re-render - Retain BlockRenderer instances during async render to prevent use-after-free crash - Fix WKWebView file access by writing temp HTML inside MPreview.bundle Code review fixes (critical): - Restore background dispatch in NotesTableView.reloadRow to prevent main-thread hangs - Retain TableEditorViewController during sheet display to prevent stepper target crash - Fix insertThumbnailCard race: capture note ref, verify identity in async callback - Optimize LayoutManager.font(for:) from O(n) enumeration to O(1) lookup - Change wysiwygMode default to false (users opt in) - Revert unrelated defaults: noteContainer back to .none, useTextBundleToStoreDates to false Code review fixes (important): - Fix underline toggle: strip only outer <u></u> via string slicing, not global replace - Fix navigateToHistoryNote flag race: clear isNavigatingHistory via async dispatch - Make underline regex static on NotesTextProcessor (was recompiled every highlight call) - Add updateLayer() to FormattingToolbar for dark/light mode color updates AI chat panel: - Add AIChatPanelView with streaming support and quick actions - Add AIService for Anthropic/OpenAI API integration - Add PDF export via PDFExporter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the 0.1pt hidden font approach for hiding markdown syntax with clear foreground color + negative kern. The 0.1pt font caused cascading cursor bugs: invisible cursor after pressing H1/H2/H3, wrong cursor position after Return on header lines, and miscalculated line heights. The new approach preserves the existing font (so the cursor inherits correct height) while making characters invisible via Color.clear and collapsing their width via negative .kern per character. Key changes: - hideSyntaxIfNecessary() now uses clear color + negative kern instead of 0.1pt font, applied globally to all hidden syntax (**, *, ~~, `, # , [](), [[]], code fences, HR, mailto, autolinks) - TextStorageProcessor.hideSyntaxRange() mirrors the same approach for code fence hiding - LayoutManager.font(for:) enumerates to find the largest font when the first character is hidden, ensuring correct line fragment height for header lines - Header Return key resets typingAttributes to body font before insertNewline so the new line doesn't inherit header styling - applyHeader sets typingAttributes to header font for correct cursor height after pressing H1/H2/H3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I really don't want a visual markdown editor by default, will there be a way to disable this in the settings? |
|
Yes. There will be a feature to start up FSNotes in either WYSIWYG mode or original markdown mode. Even in WYSIWYG mode, you can still type in markdown commands and the editor will accept them and instantly show you the result |
|
Also this may be confusing as now there are three views : (1) visual markdown editor (2) raw markdown editor (3) markdown view (not editor). Perhaps (1) and (3) should be merged if (1) can replace its functionality — but maybe things like math and others will not visualise in (1)? |
|
there's only 1) WYSIWYG mode and ordinary 2) markdown mode. #2 is the current mode in FSNotes. In 1) the experience is more like Apple Notes or GitHub, where you have a toolbar to add formatting, etc. In #1, you can also type the mermaid commands directly (e.g. '## ' to create a new subheader), just keep typing, and it will format what you're typing as a subheader. You don't have to do this-but it's handy if you already know markdown well. |
|
Closing - this work is for a private fork, not intended for upstream. |
Summary
#,**,*,~~,`,[](),>) is hidden, showing only formatted text---) render as visual separator linesKey fixes
.+?to.*?to match empty headers so#hides immediately+1on header opening syntax range that was eating the first character of every headerLayoutManager.font(for:)now finds the largest font in a glyph range instead of using the first character's font, preventing line height collapse when lines start with hidden 0.1pt syntax charactersFiles changed (11)
FSNotes/View/FormattingToolbar.swift— NEW: toolbar UIFSNotes/EditorViewController.swift— toolbar setup, WYSIWYG/Source toggle rewriteFSNotes/LayoutManager.swift— HR/blockquote drawing, line height fixFSNotesCore/NotesTextProcessor.swift— regex fix, HR/blockquote/code-span hidingFSNotesCore/TextFormatter.swift— numberedList(), horizontalRule(), insertTable()FSNotesCore/UserDefaultsManagement.swift— wysiwygMode preferenceFSNotes/View/EditTextView.swift— toolbar @IBAction methods, header behaviorFSNotesCore/Extensions/NSAttributedStringKey+.swift— .horizontalRule, .blockquoteFSNotes/ViewController.swift— toolbar wiring, hideSyntax initFSNotes/NoteViewController.swift— toolbar wiring for separate windowsTest plan
#hidden, typing works, Return exits header[[]]with autocomplete---on its own line renders as horizontal rule> quoteshows blue left border🤖 Generated with Claude Code