Editable Links in Rendered Mode
January 17, 2026 ยท View on GitHub
Overview
Task 45 implements a hover-based link editing system in the rendered WYSIWYG view. This allows users to view and edit link text and URLs through a popup menu, while keeping links non-clickable to prevent accidental navigation during editing.
Architecture
Components
-
RenderedLinkState(src/markdown/widgets.rs)- Manages link editing state
- Tracks popup open/closed state
- Stores temporary edit values
- Provides modification detection and commit/reset
-
RenderedLinkWidget(src/markdown/widgets.rs)- Renders link text with underline styling
- Shows settings icon (โ) on hover
- Displays edit popup with text/URL fields and action buttons
- Follows the EditableCodeBlock widget pattern
-
render_link(src/markdown/editor.rs)- Updated to use
RenderedLinkWidget - Creates stable IDs based on line position and URL
- Stores widget state in egui's memory
- Triggers source updates on change
- Updated to use
-
update_link_in_source(src/markdown/editor.rs)- Finds and replaces link syntax in markdown source
- Handles various link formats with/without titles
- Preserves surrounding content
User Interaction
Viewing Links
- Links display with link color (blue) and underline styling
- Links are non-clickable - clicking does nothing
- Hovering shows a tooltip with the URL
Editing Links
- Hover over a link to reveal the settings icon (โ)
- Click the settings icon to open the edit popup
- The popup contains:
- Text field: Edit the display text
- URL field: Edit the URL
- Open button (๐): Opens URL in browser (http/https only)
- Copy button (๐): Copies URL to clipboard
- Click outside the popup to close and save changes
Workflow
Hover โ Settings Icon โ Click โ Popup Opens โ Edit โ Click Outside โ Changes Saved
Important: No Separate Metadata
This implementation only edits the actual markdown content. No separate metadata or data layer is created. This ensures full compatibility with other applications and file formats.
Autolink Support (Safe Mode)
Bare URLs (autolinks) like https://example.com are handled safely:
For autolinks (bare URLs):
- Text field is hidden - there's no separate text in the source
- Only the URL field is shown and editable
- Changes update only the URL in the source
- No markdown syntax is injected - a bare URL stays a bare URL
For markdown links [text](url):
- Both Text and URL fields are shown and editable
- Changes update the markdown syntax directly
This safe approach ensures:
- Files are never corrupted by injecting markdown where it wasn't intended
- Works correctly for non-markdown files (JSON, plain text, etc.)
- User data is always respected
Implementation Details
State Management
pub struct RenderedLinkState {
pub popup_open: bool, // Is popup currently shown
pub edit_text: String, // Current text being edited
pub edit_url: String, // Current URL being edited
original_text: String, // For change detection
original_url: String, // For change detection
}
State is stored in egui's memory with a stable ID based on:
- Line number in source
- Original URL
Hover Zone
To prevent flickering when moving between the link and settings icon, a unified hover zone is used:
let hover_zone = egui::Rect::from_min_max(
link_rect.min,
link_rect.max + egui::vec2(26.0, 0.0), // Extend to include button
);
The settings icon appears when the mouse is anywhere in this zone, not just over the link text.
Click-Outside-to-Close
The popup closes automatically when clicking outside both the popup and the hover zone. Changes are committed when closing.
Line Number Handling
The parser may return 0 for inline elements (like autolinks within paragraphs). The source update function handles this by treating 0 as line 1:
let effective_start = if start_line == 0 { 1 } else { start_line };
Change Synchronization
When the user clicks "Done":
RenderedLinkState::commit()is calledupdate_link_in_source()replaces the old[text](url)with new values- Edit state is marked as modified
- Changes propagate to markdown source
URL Opening
- Only
http://andhttps://URLs can be opened - Uses the
opencrate for cross-platform browser opening - Non-web URLs show disabled button with tooltip explanation
Testing
Unit Tests
Located in src/markdown/widgets.rs and src/markdown/editor.rs:
test_rendered_link_state_newtest_rendered_link_state_modification_detectiontest_rendered_link_state_url_modificationtest_rendered_link_state_committest_rendered_link_state_resettest_rendered_link_output_fieldstest_update_link_in_source_simpletest_update_link_in_source_text_onlytest_update_link_in_source_url_onlytest_update_link_in_source_multilinetest_update_link_in_source_preserves_other_linestest_update_link_in_source_multiple_links_same_line
Manual Testing
- Open a markdown file with links in Rendered mode
- Hover over a link โ settings icon appears
- Click icon โ popup opens with correct values
- Edit text/URL, click Done โ link updates
- Open Link button works for http/https URLs
- Copy URL button copies to clipboard
- Switch to Raw mode โ changes preserved
- Multiple links work independently
Out of Scope
The following features were intentionally excluded:
- โ Ctrl+Click to open links
- โ Inline text editing (sync issues)
- โ Link validation/warnings
- โ Relative file navigation
- โ Image link previews
- โ Ctrl+K shortcut for creating links
Related Files
src/markdown/widgets.rs- RenderedLinkWidget, RenderedLinkStatesrc/markdown/editor.rs- render_link, update_link_in_sourcesrc/markdown/parser.rs- Link node parsing (MarkdownNodeType::Link)
Dependencies
opencrate - for opening URLs in browseregui- for UI components and state management