The backlinks panel shows which files in the workspace link to the currently active file. It detects both [[wikilinks]] and standard [text](file.md) markdown links, providing a reverse-link graph for navigating between related documents.
The panel is accessible as a "Links" tab within the existing outline panel (alongside Outline, Statistics, and Hub tabs).
| File | Purpose |
|---|---|
src/ui/backlinks_panel.rs |
BacklinksPanel UI component — renders the backlink list, handles click-to-navigate |
src/state.rs |
BacklinkIndex, BacklinkEntry — in-memory reverse-link index with build/update/scan methods |
src/ui/outline_panel.rs |
Hosts the "Links" tab in OutlinePanelTab enum, passes through to BacklinksPanel |
src/app/navigation.rs |
refresh_backlinks() — smart refresh strategy based on workspace size |
src/app/file_ops.rs |
Triggers incremental index update on file save, clears index on workspace close |
src/app/mod.rs |
backlinks_panel field on FerriteApp, tab-switch detection via last_active_tab_for_backlinks |
The indexing approach scales with workspace size:
| Workspace Size | Strategy | Trigger |
|---|---|---|
| ≤50 files | On-demand scan via BacklinkIndex::scan_on_demand() |
Every tab switch |
| >50 files | Full HashMap<filename, Vec<BacklinkEntry>> cached in AppState.backlink_index |
Built on first access, updated incrementally on file save |
| Single-file mode | Scans all markdown files in the current file's parent directory | Every tab switch |
extract_links_from_content() in state.rs performs lightweight regex-free text scanning:
- Wikilinks:
[[target]]and[[target|display text]]— extracts the target portion - Standard links:
[text](file.md)— only matches local.md/.markdownfiles (ignoreshttp://,https://, and#anchorlinks) - Filename normalization:
normalize_filename()strips.md/.markdownextensions and lowercases for case-insensitive matching
Backlinks refresh on two events:
- Tab switch — detected by comparing
last_active_tab_for_backlinkswith the current active tab index - File save —
handle_save_file()andhandle_save_as_file()setbacklinks_need_refresh = trueand callbacklink_index.update_file()for incremental updates
The backlinks panel is rendered as a tab inside the existing OutlinePanel:
OutlinePanelTab::Backlinks— new enum variant- Tab bar shows "🔗 Links" between Statistics and Hub
BacklinksPanelOutput.navigate_topropagated throughOutlinePanelOutput.backlink_navigate_to→ file open inapp/mod.rs- Click on a backlink entry opens that file as a tab via
state.open_file()
No new crate dependencies. Uses only standard library (HashMap, PathBuf, fs::read_to_string).
- Open a workspace containing markdown files with
[[wikilinks]]or[text](file.md)links - Open the outline panel (toggle via ribbon or keyboard shortcut)
- Click the "🔗 Links" tab
- The panel shows all files that link to the currently active file
- Click any backlink entry to navigate to that file
11 unit tests in src/state.rs:
test_normalize_filename— case-insensitive extension strippingtest_extract_wikilinks_from_content—[[target]]and[[target|display]]extractiontest_extract_standard_links_from_content—[text](file.md)extractiontest_extract_links_ignores_urls— HTTP/HTTPS URLs filtered outtest_extract_links_ignores_anchors—#anchorlinks filtered outtest_extract_mixed_links— combined wikilink + standard link extractiontest_extract_unclosed_wikilink— malformed[[handled gracefullytest_extract_empty_wikilink— empty[[]]produces no linktest_backlink_index_get_and_build— full index build from test filestest_backlink_index_update_file— incremental update after file modificationtest_backlink_scan_on_demand— on-demand scanning for small workspaces