refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
mod cache;
|
2026-03-07 12:35:52 +01:00
|
|
|
mod config_seed;
|
2026-03-17 15:07:29 +01:00
|
|
|
mod entry;
|
|
|
|
|
mod file;
|
|
|
|
|
mod frontmatter;
|
2026-02-28 12:49:17 +01:00
|
|
|
mod getting_started;
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
mod image;
|
2026-02-23 23:05:34 +01:00
|
|
|
mod migration;
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
mod parsing;
|
|
|
|
|
mod rename;
|
2026-03-17 16:45:30 +01:00
|
|
|
mod title_sync;
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
mod trash;
|
feat: add custom views backend — YAML parser, filter engine, Tauri commands
Introduce `.laputa/views/*.yml` for user-defined filtered note lists.
Rust backend: serde_yaml parsing, recursive AND/OR filter evaluation
with 10 operators (equals, contains, any_of, is_empty, before/after,
etc.), wikilink stem matching, and file CRUD. Three Tauri commands:
list_views, save_view_cmd, delete_view_cmd.
ADR 0040 documents the architecture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:57:22 +02:00
|
|
|
mod views;
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
2026-03-09 11:03:17 +01:00
|
|
|
pub use cache::{invalidate_cache, scan_vault_cached};
|
2026-03-07 12:35:52 +01:00
|
|
|
pub use config_seed::{migrate_agents_md, repair_config_files, seed_config_files};
|
2026-03-31 11:06:11 +02:00
|
|
|
pub use entry::{FolderNode, VaultEntry};
|
2026-03-17 15:07:29 +01:00
|
|
|
pub use file::{get_note_content, save_note_content};
|
2026-02-28 12:49:17 +01:00
|
|
|
pub use getting_started::{create_getting_started_vault, default_vault_path, vault_exists};
|
2026-02-28 21:31:47 +01:00
|
|
|
pub use image::{copy_image_to_vault, save_image};
|
2026-04-01 15:29:24 +02:00
|
|
|
pub use migration::migrate_is_a_to_type;
|
2026-03-31 13:54:18 +02:00
|
|
|
pub use rename::{
|
2026-04-06 13:14:43 +02:00
|
|
|
auto_rename_untitled, detect_renames, rename_note, update_wikilinks_for_renames,
|
|
|
|
|
DetectedRename, RenameResult,
|
2026-03-31 13:54:18 +02:00
|
|
|
};
|
2026-03-17 16:45:30 +01:00
|
|
|
pub use title_sync::{sync_title_on_open, SyncAction};
|
2026-04-06 12:21:56 +02:00
|
|
|
pub use trash::{batch_delete_notes, delete_note};
|
feat: add custom views backend — YAML parser, filter engine, Tauri commands
Introduce `.laputa/views/*.yml` for user-defined filtered note lists.
Rust backend: serde_yaml parsing, recursive AND/OR filter evaluation
with 10 operators (equals, contains, any_of, is_empty, before/after,
etc.), wikilink stem matching, and file CRUD. Three Tauri commands:
list_views, save_view_cmd, delete_view_cmd.
ADR 0040 documents the architecture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:57:22 +02:00
|
|
|
pub use views::{
|
|
|
|
|
delete_view, evaluate_view, save_view, scan_views, FilterCondition, FilterGroup, FilterNode,
|
|
|
|
|
FilterOp, ViewDefinition, ViewFile,
|
|
|
|
|
};
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
2026-03-17 15:07:29 +01:00
|
|
|
use file::read_file_metadata;
|
2026-03-18 01:56:33 +01:00
|
|
|
use frontmatter::{extract_fm_and_rels, resolve_is_a};
|
2026-03-17 17:08:07 +01:00
|
|
|
use parsing::{count_body_words, extract_outgoing_links, extract_snippet, extract_title};
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
|
|
|
|
use gray_matter::engine::YAML;
|
|
|
|
|
use gray_matter::Matter;
|
|
|
|
|
use std::fs;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
use walkdir::WalkDir;
|
|
|
|
|
|
|
|
|
|
/// Parse a single markdown file into a VaultEntry.
|
2026-04-02 15:00:22 +02:00
|
|
|
///
|
|
|
|
|
/// If `git_dates` is provided, those timestamps override filesystem metadata
|
|
|
|
|
/// for `modified_at` and `created_at`. Pass `None` to use filesystem dates
|
|
|
|
|
/// (appropriate for newly-saved files not yet committed, or non-git vaults).
|
2026-04-02 15:03:41 +02:00
|
|
|
pub fn parse_md_file(path: &Path, git_dates: Option<(u64, u64)>) -> Result<VaultEntry, String> {
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
let content = fs::read_to_string(path)
|
|
|
|
|
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
|
|
|
|
|
let filename = path
|
|
|
|
|
.file_name()
|
|
|
|
|
.map(|f| f.to_string_lossy().to_string())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let matter = Matter::<YAML>::new();
|
|
|
|
|
let parsed = matter.parse(&content);
|
2026-04-01 22:40:25 +02:00
|
|
|
let (frontmatter, mut relationships, properties) = extract_fm_and_rels(parsed.data, &content);
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
2026-03-18 01:56:33 +01:00
|
|
|
let title = extract_title(frontmatter.title.as_deref(), &content, &filename);
|
2026-04-06 13:08:17 +02:00
|
|
|
let has_h1 = parsing::extract_h1_title(&content).is_some();
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
let snippet = extract_snippet(&content);
|
2026-02-26 20:50:29 +01:00
|
|
|
let word_count = count_body_words(&content);
|
2026-02-26 03:35:18 +01:00
|
|
|
let outgoing_links = extract_outgoing_links(&parsed.content);
|
2026-04-02 15:00:22 +02:00
|
|
|
let (fs_modified, fs_created, file_size) = read_file_metadata(path)?;
|
|
|
|
|
let (modified_at, created_at) = match git_dates {
|
|
|
|
|
Some((git_mod, git_create)) => (Some(git_mod), Some(git_create)),
|
|
|
|
|
None => (fs_modified, fs_created),
|
|
|
|
|
};
|
2026-03-15 23:40:47 +01:00
|
|
|
let is_a = resolve_is_a(frontmatter.is_a);
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
|
|
|
|
// Add "Type" relationship: isA becomes a navigable link to the type document.
|
|
|
|
|
// Skip for type documents themselves (isA == "Type") to avoid self-referential links.
|
|
|
|
|
if let Some(ref type_name) = is_a {
|
|
|
|
|
if type_name != "Type" {
|
|
|
|
|
let type_link = if type_name.starts_with("[[") && type_name.ends_with("]]") {
|
|
|
|
|
type_name.clone()
|
|
|
|
|
} else {
|
2026-03-16 16:17:58 +01:00
|
|
|
format!("[[{}]]", type_name.to_lowercase())
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
};
|
|
|
|
|
relationships.insert("Type".to_string(), vec![type_link]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 20:20:58 +01:00
|
|
|
let belongs_to = relationships.get("Belongs to").cloned().unwrap_or_default();
|
|
|
|
|
let related_to = relationships.get("Related to").cloned().unwrap_or_default();
|
|
|
|
|
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
Ok(VaultEntry {
|
|
|
|
|
path: path.to_string_lossy().to_string(),
|
|
|
|
|
filename,
|
|
|
|
|
title,
|
|
|
|
|
is_a,
|
|
|
|
|
snippet,
|
|
|
|
|
relationships,
|
|
|
|
|
aliases: frontmatter
|
|
|
|
|
.aliases
|
|
|
|
|
.map(|a| a.into_vec())
|
|
|
|
|
.unwrap_or_default(),
|
2026-03-16 20:20:58 +01:00
|
|
|
belongs_to,
|
|
|
|
|
related_to,
|
fix: uniform StringOrList normalization for all frontmatter string fields
Replace per-field Option<String> with Option<StringOrList> for all string
fields in the Frontmatter struct (owner, cadence, status, icon, color,
sidebar_label, template, sort, view, trashed_at, created_at, created_time).
Previously, fields like Owner stored as YAML arrays (e.g. `Owner: [Luca]`)
caused serde to fail the entire Frontmatter deserialization, defaulting all
fields to None — including is_a, which broke the type badge display.
Now all string fields use StringOrList with into_scalar() normalization:
- Single-element array → unwrap to scalar
- Multi-element array → take first element
- Scalar → unchanged
- Empty array → None
- Absent → None
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:37:16 +01:00
|
|
|
status: frontmatter.status.and_then(|v| v.into_scalar()),
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
archived: frontmatter.archived.unwrap_or(false),
|
|
|
|
|
modified_at,
|
|
|
|
|
created_at,
|
|
|
|
|
file_size,
|
fix: uniform StringOrList normalization for all frontmatter string fields
Replace per-field Option<String> with Option<StringOrList> for all string
fields in the Frontmatter struct (owner, cadence, status, icon, color,
sidebar_label, template, sort, view, trashed_at, created_at, created_time).
Previously, fields like Owner stored as YAML arrays (e.g. `Owner: [Luca]`)
caused serde to fail the entire Frontmatter deserialization, defaulting all
fields to None — including is_a, which broke the type badge display.
Now all string fields use StringOrList with into_scalar() normalization:
- Single-element array → unwrap to scalar
- Multi-element array → take first element
- Scalar → unchanged
- Empty array → None
- Absent → None
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:37:16 +01:00
|
|
|
icon: frontmatter.icon.and_then(|v| v.into_scalar()),
|
|
|
|
|
color: frontmatter.color.and_then(|v| v.into_scalar()),
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
order: frontmatter.order,
|
fix: uniform StringOrList normalization for all frontmatter string fields
Replace per-field Option<String> with Option<StringOrList> for all string
fields in the Frontmatter struct (owner, cadence, status, icon, color,
sidebar_label, template, sort, view, trashed_at, created_at, created_time).
Previously, fields like Owner stored as YAML arrays (e.g. `Owner: [Luca]`)
caused serde to fail the entire Frontmatter deserialization, defaulting all
fields to None — including is_a, which broke the type badge display.
Now all string fields use StringOrList with into_scalar() normalization:
- Single-element array → unwrap to scalar
- Multi-element array → take first element
- Scalar → unchanged
- Empty array → None
- Absent → None
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:37:16 +01:00
|
|
|
sidebar_label: frontmatter.sidebar_label.and_then(|v| v.into_scalar()),
|
|
|
|
|
template: frontmatter.template.and_then(|v| v.into_scalar()),
|
|
|
|
|
sort: frontmatter.sort.and_then(|v| v.into_scalar()),
|
|
|
|
|
view: frontmatter.view.and_then(|v| v.into_scalar()),
|
2026-03-06 21:30:18 +01:00
|
|
|
visible: frontmatter.visible,
|
2026-04-04 20:45:25 +02:00
|
|
|
organized: frontmatter.organized.unwrap_or(false),
|
2026-04-02 14:44:23 +02:00
|
|
|
favorite: frontmatter.favorite.unwrap_or(false),
|
|
|
|
|
favorite_index: frontmatter.favorite_index,
|
2026-04-04 13:32:43 +02:00
|
|
|
list_properties_display: frontmatter.list_properties_display.unwrap_or_default(),
|
2026-02-26 20:50:29 +01:00
|
|
|
word_count,
|
2026-02-25 15:04:49 +01:00
|
|
|
outgoing_links,
|
2026-03-03 02:31:18 +01:00
|
|
|
properties,
|
2026-04-06 13:08:17 +02:00
|
|
|
has_h1,
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
file_kind: "markdown".to_string(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parse a non-markdown file into a minimal VaultEntry.
|
2026-04-05 04:19:14 +02:00
|
|
|
/// Uses filename as title, except for `.yml` files where the YAML `name` field is used.
|
2026-04-02 17:39:55 +02:00
|
|
|
pub(crate) fn parse_non_md_file(
|
|
|
|
|
path: &Path,
|
|
|
|
|
git_dates: Option<(u64, u64)>,
|
|
|
|
|
) -> Result<VaultEntry, String> {
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
let filename = path
|
|
|
|
|
.file_name()
|
|
|
|
|
.map(|f| f.to_string_lossy().to_string())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
let (fs_modified, fs_created, file_size) = read_file_metadata(path)?;
|
|
|
|
|
let (modified_at, created_at) = match git_dates {
|
|
|
|
|
Some((git_mod, git_create)) => (Some(git_mod), Some(git_create)),
|
|
|
|
|
None => (fs_modified, fs_created),
|
|
|
|
|
};
|
|
|
|
|
let file_kind = classify_file_kind(path).to_string();
|
2026-04-05 04:19:14 +02:00
|
|
|
let title = extract_yml_name(path).unwrap_or_else(|| filename.clone());
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
|
|
|
|
|
Ok(VaultEntry {
|
|
|
|
|
path: path.to_string_lossy().to_string(),
|
|
|
|
|
filename: filename.clone(),
|
2026-04-05 04:19:14 +02:00
|
|
|
title,
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
file_kind,
|
|
|
|
|
modified_at,
|
|
|
|
|
created_at,
|
|
|
|
|
file_size,
|
|
|
|
|
..VaultEntry::default()
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 04:19:14 +02:00
|
|
|
/// For `.yml` files, try to extract the `name` field from the YAML content.
|
|
|
|
|
fn extract_yml_name(path: &Path) -> Option<String> {
|
|
|
|
|
let ext = path.extension()?.to_str()?;
|
|
|
|
|
if ext != "yml" && ext != "yaml" {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let content = std::fs::read_to_string(path).ok()?;
|
|
|
|
|
let mapping: serde_yaml::Value = serde_yaml::from_str(&content).ok()?;
|
|
|
|
|
mapping.get("name")?.as_str().map(|s| s.to_string())
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 00:30:28 +01:00
|
|
|
/// Re-read a single file from disk and return a fresh VaultEntry.
|
2026-04-02 15:00:22 +02:00
|
|
|
/// Uses filesystem dates (no git lookup) since the file was likely just saved.
|
2026-03-09 00:30:28 +01:00
|
|
|
pub fn reload_entry(path: &Path) -> Result<VaultEntry, String> {
|
|
|
|
|
if !path.exists() {
|
|
|
|
|
return Err(format!("File does not exist: {}", path.display()));
|
|
|
|
|
}
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
if is_md_file(path) {
|
|
|
|
|
parse_md_file(path, None)
|
|
|
|
|
} else {
|
|
|
|
|
parse_non_md_file(path, None)
|
|
|
|
|
}
|
2026-03-09 00:30:28 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 11:06:11 +02:00
|
|
|
/// Directories that are never shown in the folder tree or scanned for notes.
|
|
|
|
|
const HIDDEN_DIRS: &[&str] = &[".git", ".laputa", ".DS_Store"];
|
|
|
|
|
|
|
|
|
|
fn is_hidden_dir(name: &str) -> bool {
|
|
|
|
|
name.starts_with('.') || HIDDEN_DIRS.contains(&name)
|
|
|
|
|
}
|
2026-03-16 04:34:46 +01:00
|
|
|
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
pub(crate) fn is_md_file(path: &Path) -> bool {
|
2026-03-17 12:07:33 +01:00
|
|
|
path.is_file() && path.extension().is_some_and(|ext| ext == "md")
|
|
|
|
|
}
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
/// Extensions recognized as editable text files (opened in raw editor).
|
|
|
|
|
const TEXT_EXTENSIONS: &[&str] = &[
|
2026-04-02 17:39:55 +02:00
|
|
|
"yml",
|
|
|
|
|
"yaml",
|
|
|
|
|
"json",
|
|
|
|
|
"txt",
|
|
|
|
|
"toml",
|
|
|
|
|
"csv",
|
|
|
|
|
"xml",
|
|
|
|
|
"html",
|
|
|
|
|
"htm",
|
|
|
|
|
"css",
|
|
|
|
|
"scss",
|
|
|
|
|
"less",
|
|
|
|
|
"ts",
|
|
|
|
|
"tsx",
|
|
|
|
|
"js",
|
|
|
|
|
"jsx",
|
|
|
|
|
"py",
|
|
|
|
|
"rs",
|
|
|
|
|
"sh",
|
|
|
|
|
"bash",
|
|
|
|
|
"zsh",
|
|
|
|
|
"fish",
|
|
|
|
|
"rb",
|
|
|
|
|
"go",
|
|
|
|
|
"java",
|
|
|
|
|
"kt",
|
|
|
|
|
"c",
|
|
|
|
|
"cpp",
|
|
|
|
|
"h",
|
|
|
|
|
"hpp",
|
|
|
|
|
"swift",
|
|
|
|
|
"lua",
|
|
|
|
|
"sql",
|
|
|
|
|
"graphql",
|
|
|
|
|
"env",
|
|
|
|
|
"ini",
|
|
|
|
|
"cfg",
|
|
|
|
|
"conf",
|
|
|
|
|
"properties",
|
|
|
|
|
"makefile",
|
|
|
|
|
"dockerfile",
|
|
|
|
|
"gitignore",
|
|
|
|
|
"editorconfig",
|
|
|
|
|
"mdx",
|
|
|
|
|
"svelte",
|
|
|
|
|
"vue",
|
|
|
|
|
"astro",
|
|
|
|
|
"tf",
|
|
|
|
|
"hcl",
|
|
|
|
|
"nix",
|
|
|
|
|
"zig",
|
|
|
|
|
"hs",
|
|
|
|
|
"ml",
|
|
|
|
|
"ex",
|
|
|
|
|
"exs",
|
|
|
|
|
"erl",
|
|
|
|
|
"clj",
|
|
|
|
|
"lisp",
|
|
|
|
|
"el",
|
|
|
|
|
"vim",
|
|
|
|
|
"r",
|
|
|
|
|
"jl",
|
|
|
|
|
"ps1",
|
|
|
|
|
"bat",
|
|
|
|
|
"cmd",
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/// Classify a file extension into "markdown", "text", or "binary".
|
|
|
|
|
pub(crate) fn classify_file_kind(path: &Path) -> &'static str {
|
|
|
|
|
let ext = match path.extension() {
|
|
|
|
|
Some(e) => e.to_string_lossy().to_lowercase(),
|
|
|
|
|
None => {
|
|
|
|
|
// Files without extension: check if name itself is a known text file
|
2026-04-02 17:39:55 +02:00
|
|
|
let name = path
|
|
|
|
|
.file_name()
|
|
|
|
|
.map(|n| n.to_string_lossy().to_lowercase())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
return if [
|
|
|
|
|
"makefile",
|
|
|
|
|
"dockerfile",
|
|
|
|
|
"rakefile",
|
|
|
|
|
"gemfile",
|
|
|
|
|
"procfile",
|
|
|
|
|
"brewfile",
|
|
|
|
|
".gitignore",
|
|
|
|
|
".gitattributes",
|
|
|
|
|
".editorconfig",
|
|
|
|
|
".env",
|
|
|
|
|
]
|
|
|
|
|
.contains(&name.as_str())
|
|
|
|
|
{
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
"text"
|
|
|
|
|
} else {
|
|
|
|
|
"binary"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if ext == "md" || ext == "markdown" {
|
|
|
|
|
"markdown"
|
|
|
|
|
} else if TEXT_EXTENSIONS.contains(&ext.as_str()) {
|
|
|
|
|
"text"
|
|
|
|
|
} else {
|
|
|
|
|
"binary"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 15:00:22 +02:00
|
|
|
use crate::git::GitDates;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
|
|
fn lookup_git_dates(
|
|
|
|
|
path: &Path,
|
|
|
|
|
vault_path: &Path,
|
|
|
|
|
git_dates: &HashMap<String, GitDates>,
|
|
|
|
|
) -> Option<(u64, u64)> {
|
|
|
|
|
let rel = path
|
|
|
|
|
.strip_prefix(vault_path)
|
|
|
|
|
.ok()?
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
.to_string();
|
2026-04-02 15:03:41 +02:00
|
|
|
git_dates.get(&rel).map(|d| (d.modified_at, d.created_at))
|
2026-04-02 15:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
fn try_parse_file(
|
2026-04-02 15:00:22 +02:00
|
|
|
path: &Path,
|
|
|
|
|
vault_path: &Path,
|
|
|
|
|
git_dates: &HashMap<String, GitDates>,
|
|
|
|
|
entries: &mut Vec<VaultEntry>,
|
|
|
|
|
) {
|
|
|
|
|
let dates = lookup_git_dates(path, vault_path, git_dates);
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
let result = if is_md_file(path) {
|
|
|
|
|
parse_md_file(path, dates)
|
|
|
|
|
} else {
|
|
|
|
|
parse_non_md_file(path, dates)
|
|
|
|
|
};
|
|
|
|
|
match result {
|
2026-03-17 12:07:33 +01:00
|
|
|
Ok(vault_entry) => entries.push(vault_entry),
|
|
|
|
|
Err(e) => log::warn!("Skipping file: {}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-16 04:34:46 +01:00
|
|
|
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
/// Scan all files in the vault, including subdirectories.
|
2026-03-31 11:06:11 +02:00
|
|
|
/// Hidden directories (starting with `.`) are excluded.
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
fn scan_all_files(
|
2026-04-02 15:00:22 +02:00
|
|
|
vault_path: &Path,
|
|
|
|
|
git_dates: &HashMap<String, GitDates>,
|
|
|
|
|
entries: &mut Vec<VaultEntry>,
|
|
|
|
|
) {
|
2026-03-31 11:06:11 +02:00
|
|
|
let walker = WalkDir::new(vault_path)
|
|
|
|
|
.follow_links(true)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_entry(|e| {
|
|
|
|
|
if e.file_type().is_dir() {
|
|
|
|
|
let name = e.file_name().to_string_lossy();
|
|
|
|
|
// Skip the vault root itself (depth 0) — we only filter subdirs
|
|
|
|
|
if e.depth() == 0 {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return !is_hidden_dir(&name);
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
});
|
|
|
|
|
for entry in walker.filter_map(|e| e.ok()) {
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
if entry.path().is_file() {
|
|
|
|
|
// Skip hidden files (starting with '.') — e.g. .gitignore, .DS_Store
|
|
|
|
|
let fname = entry.file_name().to_string_lossy();
|
|
|
|
|
if fname.starts_with('.') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
try_parse_file(entry.path(), vault_path, git_dates, entries);
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-17 12:07:33 +01:00
|
|
|
}
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
/// Scan a directory recursively for all files and return VaultEntry for each.
|
2026-04-02 15:00:22 +02:00
|
|
|
/// Pass an empty map for `git_dates` to use filesystem dates only.
|
|
|
|
|
pub fn scan_vault(
|
|
|
|
|
vault_path: &Path,
|
|
|
|
|
git_dates: &HashMap<String, GitDates>,
|
|
|
|
|
) -> Result<Vec<VaultEntry>, String> {
|
2026-03-17 12:07:33 +01:00
|
|
|
if !vault_path.exists() {
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"Vault path does not exist: {}",
|
|
|
|
|
vault_path.display()
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if !vault_path.is_dir() {
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"Vault path is not a directory: {}",
|
|
|
|
|
vault_path.display()
|
|
|
|
|
));
|
|
|
|
|
}
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
|
2026-03-17 12:07:33 +01:00
|
|
|
let mut entries = Vec::new();
|
feat: show all files in folder view, open text files in raw editor, gray out binary
Vault scanner now includes all files (not just .md). Each VaultEntry has a
fileKind field ("markdown", "text", or "binary") that controls how the
frontend handles it:
- Folder view shows all file kinds; other views (All Notes, type sections)
continue to show only markdown files
- Text files (.yml, .json, .txt, .py, etc.) open in the raw CodeMirror editor
- Binary files (.png, .jpg, .pdf, etc.) appear grayed out and are not clickable
- Non-markdown files use filename as title, skip frontmatter parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:38:09 +02:00
|
|
|
scan_all_files(vault_path, git_dates, &mut entries);
|
2026-03-17 12:07:33 +01:00
|
|
|
|
|
|
|
|
entries.sort_by(|a, b| b.modified_at.cmp(&a.modified_at));
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
Ok(entries)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 11:06:11 +02:00
|
|
|
/// Build a tree of visible folders in the vault.
|
|
|
|
|
/// Excludes hidden directories (starting with `.`).
|
|
|
|
|
pub fn scan_vault_folders(vault_path: &Path) -> Result<Vec<FolderNode>, String> {
|
|
|
|
|
if !vault_path.is_dir() {
|
|
|
|
|
return Err(format!("Not a directory: {}", vault_path.display()));
|
|
|
|
|
}
|
|
|
|
|
fn build_tree(dir: &Path, vault_root: &Path) -> Vec<FolderNode> {
|
|
|
|
|
let mut nodes: Vec<FolderNode> = Vec::new();
|
|
|
|
|
let entries = match fs::read_dir(dir) {
|
|
|
|
|
Ok(d) => d,
|
|
|
|
|
Err(_) => return nodes,
|
|
|
|
|
};
|
|
|
|
|
for entry in entries.flatten() {
|
|
|
|
|
let path = entry.path();
|
|
|
|
|
if !path.is_dir() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let name = entry.file_name().to_string_lossy().to_string();
|
|
|
|
|
if is_hidden_dir(&name) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let rel_path = path
|
|
|
|
|
.strip_prefix(vault_root)
|
|
|
|
|
.unwrap_or(&path)
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
.replace('\\', "/");
|
|
|
|
|
let children = build_tree(&path, vault_root);
|
|
|
|
|
nodes.push(FolderNode {
|
|
|
|
|
name,
|
|
|
|
|
path: rel_path,
|
|
|
|
|
children,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
nodes.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
|
|
|
|
nodes
|
|
|
|
|
}
|
|
|
|
|
Ok(build_tree(vault_path, vault_path))
|
|
|
|
|
}
|
|
|
|
|
|
refactor: split vault.rs into focused submodules
Break the 2338-line vault.rs into vault/ directory with 6 focused modules:
- mod.rs: core types (VaultEntry, Frontmatter), parse_md_file, scan_vault
- parsing.rs: text processing (snippet extraction, markdown stripping, date parsing)
- cache.rs: git-based incremental vault caching
- trash.rs: purge_trash with flat iterator chain
- rename.rs: note renaming with wikilink updates
- image.rs: attachment saving with filename sanitization
Also moves frontmatter update/delete wrappers to frontmatter.rs where they
belong, and converts public functions to accept &Path instead of &str.
CodeScene scores: mod.rs 10.0, parsing.rs 9.68, cache.rs 9.68,
trash.rs 9.38, rename.rs 9.68, image.rs 10.0 (all above 9.0 target).
All 169 Rust tests pass, 86.28% line coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:16:21 +01:00
|
|
|
#[cfg(test)]
|
2026-03-17 15:07:29 +01:00
|
|
|
#[path = "mod_tests.rs"]
|
|
|
|
|
mod tests;
|