From 08668854b93e539ccc90ed350e2df7158be2d66e Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 16 Mar 2026 16:17:58 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20flat=20vault=20type=20resolution=20?= =?UTF-8?q?=E2=80=94=20remove=20type/=20prefix=20from=20links=20and=20prot?= =?UTF-8?q?ected=20folders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Type relationship links: [[type/essay]] → [[essay]] (matches flat vault structure) - PROTECTED_FOLDERS / KEEP_FOLDERS: only attachments, _themes, assets - TypeSelector navigation: type/slug → slug - Updated all 14 affected test files - All 612 Rust tests + 2151 frontend tests pass --- demo-vault-v2/default-theme.md | 21 ++++ demo-vault-v2/default.md | 54 ---------- demo-vault-v2/enter-rename-test.md | 8 ++ demo-vault-v2/final-renamed.md | 10 ++ demo-vault-v2/final-title-r-renamed.md | 12 +++ demo-vault-v2/final-title-r.md | 8 ++ demo-vault-v2/final-title-renamed-renamed.md | 12 +++ demo-vault-v2/my-renamed-note.md | 8 ++ demo-vault-v2/note/final-title-r-renamed.md | 10 ++ demo-vault-v2/note/final-title-r.md | 8 ++ .../note/final-title-renamed-renamed.md | 10 ++ .../note/untitled-save-no-rename-te.md | 8 ++ .../note/untitled-save-no-rename-test-2.md | 6 ++ .../note/untitled-save-no-rename-test.md | 6 ++ demo-vault-v2/note/untitled-tes-renamed.md | 10 ++ .../note/untitled-test-note-abc-2.md | 6 ++ .../note/untitled-test-note-abc-3.md | 6 ++ .../note/untitled-test-note-abc-renamed.md | 10 ++ demo-vault-v2/save-no-rename-test.md | 6 ++ demo-vault-v2/should-be-r-renamed.md | 8 ++ demo-vault-v2/test-note-a.md | 8 ++ demo-vault-v2/test-note-abc.md | 6 ++ demo-vault-v2/theme/default-theme.md | 20 ++++ demo-vault-v2/untitled-experiment-2.md | 22 ++++ demo-vault-v2/untitled-experiment.md | 16 +++ demo-vault-v2/untitled-not.md | 8 ++ demo-vault-v2/untitled-save-no-rename-test.md | 6 ++ .../untitled-test-note-abc-2-renamed.md | 6 ++ demo-vault-v2/untitled-test-note-abc-2.md | 6 ++ demo-vault-v2/untitled-test-note-abc.md | 6 ++ src-tauri/src/commands.rs | 2 +- src-tauri/src/lib.rs | 2 +- src-tauri/src/theme/seed.rs | 24 ++--- src-tauri/src/vault/cache.rs | 14 +-- src-tauri/src/vault/config_seed.rs | 28 ++--- src-tauri/src/vault/getting_started.rs | 28 ++--- src-tauri/src/vault/migration.rs | 34 ++---- src-tauri/src/vault/mod.rs | 91 +++++++--------- src-tauri/src/vault_config.rs | 22 ++-- .../DynamicPropertiesPanel.test.tsx | 8 +- src/components/Inspector.test.tsx | 18 ++-- src/components/InspectorPanels.test.tsx | 4 +- src/components/NoteAutocomplete.test.tsx | 2 +- src/components/Sidebar.test.tsx | 18 ++-- src/components/TypeSelector.tsx | 2 +- src/components/useNoteListSort.test.tsx | 12 +-- src/hooks/useEntryActions.test.ts | 100 +++++++++--------- src/hooks/useNoteActions.test.ts | 8 +- src/hooks/useNoteActions.ts | 2 +- src/mock-tauri/mock-content.ts | 26 ++--- src/mock-tauri/mock-entries.ts | 76 ++++++------- src/utils/noteListHelpers.test.ts | 4 +- src/utils/wikilinkColors.test.ts | 12 +-- 53 files changed, 522 insertions(+), 346 deletions(-) create mode 100644 demo-vault-v2/default-theme.md delete mode 100644 demo-vault-v2/default.md create mode 100644 demo-vault-v2/enter-rename-test.md create mode 100644 demo-vault-v2/final-renamed.md create mode 100644 demo-vault-v2/final-title-r-renamed.md create mode 100644 demo-vault-v2/final-title-r.md create mode 100644 demo-vault-v2/final-title-renamed-renamed.md create mode 100644 demo-vault-v2/my-renamed-note.md create mode 100644 demo-vault-v2/note/final-title-r-renamed.md create mode 100644 demo-vault-v2/note/final-title-r.md create mode 100644 demo-vault-v2/note/final-title-renamed-renamed.md create mode 100644 demo-vault-v2/note/untitled-save-no-rename-te.md create mode 100644 demo-vault-v2/note/untitled-save-no-rename-test-2.md create mode 100644 demo-vault-v2/note/untitled-save-no-rename-test.md create mode 100644 demo-vault-v2/note/untitled-tes-renamed.md create mode 100644 demo-vault-v2/note/untitled-test-note-abc-2.md create mode 100644 demo-vault-v2/note/untitled-test-note-abc-3.md create mode 100644 demo-vault-v2/note/untitled-test-note-abc-renamed.md create mode 100644 demo-vault-v2/save-no-rename-test.md create mode 100644 demo-vault-v2/should-be-r-renamed.md create mode 100644 demo-vault-v2/test-note-a.md create mode 100644 demo-vault-v2/test-note-abc.md create mode 100644 demo-vault-v2/theme/default-theme.md create mode 100644 demo-vault-v2/untitled-experiment-2.md create mode 100644 demo-vault-v2/untitled-experiment.md create mode 100644 demo-vault-v2/untitled-not.md create mode 100644 demo-vault-v2/untitled-save-no-rename-test.md create mode 100644 demo-vault-v2/untitled-test-note-abc-2-renamed.md create mode 100644 demo-vault-v2/untitled-test-note-abc-2.md create mode 100644 demo-vault-v2/untitled-test-note-abc.md diff --git a/demo-vault-v2/default-theme.md b/demo-vault-v2/default-theme.md new file mode 100644 index 00000000..450957e2 --- /dev/null +++ b/demo-vault-v2/default-theme.md @@ -0,0 +1,21 @@ +--- +type: Theme +title: Default Theme +primary: "#155DFF" +background: "#1a1a2e" +foreground: "#37352F" +sidebar: "#2a2a3e" +border: "#E9E9E7" +muted: "#F0F0EF" +muted-foreground: "#9B9A97" +accent: "#F0F7FF" +accent-foreground: "#0A3B8F" +font-family: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif" +font-size-base: 14 +line-height-base: 1.6 +--- +# Default Theme + +Light theme with warm, paper-like tones. + +[[Ma diff --git a/demo-vault-v2/default.md b/demo-vault-v2/default.md deleted file mode 100644 index 0852e797..00000000 --- a/demo-vault-v2/default.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -Is A: Theme -Description: Light theme with warm, paper-like tones -background: "#FFFFFF" -foreground: "#37352F" -card: "#FFFFFF" -popover: "#FFFFFF" -primary: "#155DFF" -primary-foreground: "#FFFFFF" -secondary: "#EBEBEA" -secondary-foreground: "#37352F" -muted: "#F0F0EF" -muted-foreground: "#787774" -accent: "#EBEBEA" -accent-foreground: "#37352F" -destructive: "#E03E3E" -border: "#E9E9E7" -input: "#E9E9E7" -ring: "#155DFF" -sidebar: "#F7F6F3" -sidebar-foreground: "#37352F" -sidebar-border: "#E9E9E7" -sidebar-accent: "#EBEBEA" -text-primary: "#37352F" -text-secondary: "#787774" -text-muted: "#B4B4B4" -text-heading: "#37352F" -bg-primary: "#FFFFFF" -bg-sidebar: "#F7F6F3" -bg-hover: "#EBEBEA" -bg-hover-subtle: "#F0F0EF" -bg-selected: "#E8F4FE" -border-primary: "#E9E9E7" -accent-blue: "#155DFF" -accent-green: "#00B38B" -accent-orange: "#D9730D" -accent-red: "#E03E3E" -accent-purple: "#A932FF" -accent-yellow: "#F0B100" -accent-blue-light: "#155DFF14" -accent-green-light: "#00B38B14" -accent-purple-light: "#A932FF14" -accent-red-light: "#E03E3E14" -accent-yellow-light: "#F0B10014" -font-family: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif" -font-size-base: 14px -editor-font-size: 16 -editor-line-height: 1.5 -editor-max-width: 720 ---- - -# Default Theme - -The default light theme for Laputa. Clean and warm, inspired by Notion. diff --git a/demo-vault-v2/enter-rename-test.md b/demo-vault-v2/enter-rename-test.md new file mode 100644 index 00000000..94913fd4 --- /dev/null +++ b/demo-vault-v2/enter-rename-test.md @@ -0,0 +1,8 @@ +--- +title: Untitled note 4 +type: Note +status: Active +--- + +# Enter Rename Test + diff --git a/demo-vault-v2/final-renamed.md b/demo-vault-v2/final-renamed.md new file mode 100644 index 00000000..b449dc77 --- /dev/null +++ b/demo-vault-v2/final-renamed.md @@ -0,0 +1,10 @@ +--- +title: Final Renamed +type: Note +status: Active +--- +# Final Renamed + +Title + +Snippet test content 1773604558993 diff --git a/demo-vault-v2/final-title-r-renamed.md b/demo-vault-v2/final-title-r-renamed.md new file mode 100644 index 00000000..1ff5bbe9 --- /dev/null +++ b/demo-vault-v2/final-title-r-renamed.md @@ -0,0 +1,12 @@ +--- +title: Final Title R Renamed +type: Note +status: Active +--- +# Final Title R Renamed + +Snippet test content 1773607529878enamed + +Snippet test content 1773607534349 + +[[Ma diff --git a/demo-vault-v2/final-title-r.md b/demo-vault-v2/final-title-r.md new file mode 100644 index 00000000..a54a40dc --- /dev/null +++ b/demo-vault-v2/final-title-r.md @@ -0,0 +1,8 @@ +--- +title: Final Title R +type: Note +status: Active +--- +# Final Title R + +[[Maenamed diff --git a/demo-vault-v2/final-title-renamed-renamed.md b/demo-vault-v2/final-title-renamed-renamed.md new file mode 100644 index 00000000..586e51db --- /dev/null +++ b/demo-vault-v2/final-title-renamed-renamed.md @@ -0,0 +1,12 @@ +--- +title: Final Title Renamed Renamed +type: Note +status: Active +--- +# Final Title Renamed Renamed + +[[MaSnippet test content 1773614740678 + +Snippet test content 1773614745082 + +[[Ma diff --git a/demo-vault-v2/my-renamed-note.md b/demo-vault-v2/my-renamed-note.md new file mode 100644 index 00000000..9c47d8bf --- /dev/null +++ b/demo-vault-v2/my-renamed-note.md @@ -0,0 +1,8 @@ +--- +title: Untitled note +type: Note +status: Active +--- + +# My Renamed Note + diff --git a/demo-vault-v2/note/final-title-r-renamed.md b/demo-vault-v2/note/final-title-r-renamed.md new file mode 100644 index 00000000..e784cc98 --- /dev/null +++ b/demo-vault-v2/note/final-title-r-renamed.md @@ -0,0 +1,10 @@ +--- +title: Final Title R Renamed +type: Note +status: Active +Trashed: true +Trashed at: '2026-03-13' +--- +# Final Title R Renamed + +[[Maenamed diff --git a/demo-vault-v2/note/final-title-r.md b/demo-vault-v2/note/final-title-r.md new file mode 100644 index 00000000..a54a40dc --- /dev/null +++ b/demo-vault-v2/note/final-title-r.md @@ -0,0 +1,8 @@ +--- +title: Final Title R +type: Note +status: Active +--- +# Final Title R + +[[Maenamed diff --git a/demo-vault-v2/note/final-title-renamed-renamed.md b/demo-vault-v2/note/final-title-renamed-renamed.md new file mode 100644 index 00000000..20c448c7 --- /dev/null +++ b/demo-vault-v2/note/final-title-renamed-renamed.md @@ -0,0 +1,10 @@ +--- +title: Final Title Renamed +type: Note +status: Active +--- +# Final Title Renamed Renamed + +Snippet test content 1773606979013 + +Snippet test content 1773607012674 diff --git a/demo-vault-v2/note/untitled-save-no-rename-te.md b/demo-vault-v2/note/untitled-save-no-rename-te.md new file mode 100644 index 00000000..51a3e604 --- /dev/null +++ b/demo-vault-v2/note/untitled-save-no-rename-te.md @@ -0,0 +1,8 @@ +--- +title: Untitled Save No Rename Te +type: Note +status: Active +--- +# Untitled Save No Rename Te + +[[Mast 2 diff --git a/demo-vault-v2/note/untitled-save-no-rename-test-2.md b/demo-vault-v2/note/untitled-save-no-rename-test-2.md new file mode 100644 index 00000000..fb1cbf1f --- /dev/null +++ b/demo-vault-v2/note/untitled-save-no-rename-test-2.md @@ -0,0 +1,6 @@ +--- +title: Untitled Save No Rename Test 2 +type: Note +status: Active +--- +# Untitled Save No Rename Test 2 diff --git a/demo-vault-v2/note/untitled-save-no-rename-test.md b/demo-vault-v2/note/untitled-save-no-rename-test.md new file mode 100644 index 00000000..48f3ee39 --- /dev/null +++ b/demo-vault-v2/note/untitled-save-no-rename-test.md @@ -0,0 +1,6 @@ +--- +title: Untitled Save No Rename Test +type: Note +status: Active +--- +# Untitled Save No Rename Test diff --git a/demo-vault-v2/note/untitled-tes-renamed.md b/demo-vault-v2/note/untitled-tes-renamed.md new file mode 100644 index 00000000..9498d7c1 --- /dev/null +++ b/demo-vault-v2/note/untitled-tes-renamed.md @@ -0,0 +1,10 @@ +--- +title: Untitled Tes Renamed +type: Note +status: Active +--- +# Untitled Tes Renamed + +[[Mat Note ABC + +[[Ma diff --git a/demo-vault-v2/note/untitled-test-note-abc-2.md b/demo-vault-v2/note/untitled-test-note-abc-2.md new file mode 100644 index 00000000..76b75530 --- /dev/null +++ b/demo-vault-v2/note/untitled-test-note-abc-2.md @@ -0,0 +1,6 @@ +--- +title: Untitled Test Note ABC 2 +type: Note +status: Active +--- +# Untitled Test Note ABC 2 diff --git a/demo-vault-v2/note/untitled-test-note-abc-3.md b/demo-vault-v2/note/untitled-test-note-abc-3.md new file mode 100644 index 00000000..0bf7612d --- /dev/null +++ b/demo-vault-v2/note/untitled-test-note-abc-3.md @@ -0,0 +1,6 @@ +--- +title: Untitled Test Note ABC 3 +type: Note +status: Active +--- +# Untitled Test Note ABC 3 diff --git a/demo-vault-v2/note/untitled-test-note-abc-renamed.md b/demo-vault-v2/note/untitled-test-note-abc-renamed.md new file mode 100644 index 00000000..768f5b39 --- /dev/null +++ b/demo-vault-v2/note/untitled-test-note-abc-renamed.md @@ -0,0 +1,10 @@ +--- +title: Untitled Test Note ABC Renamed +type: Note +status: Active +Trashed: true +Trashed at: '2026-03-13' +--- +# Untitled Test Note ABC Renamed + +[[Ma diff --git a/demo-vault-v2/save-no-rename-test.md b/demo-vault-v2/save-no-rename-test.md new file mode 100644 index 00000000..dce2c691 --- /dev/null +++ b/demo-vault-v2/save-no-rename-test.md @@ -0,0 +1,6 @@ +--- +title: Save No Rename Test +type: Note +status: Active +--- +# Save No Rename Test diff --git a/demo-vault-v2/should-be-r-renamed.md b/demo-vault-v2/should-be-r-renamed.md new file mode 100644 index 00000000..88d59f5a --- /dev/null +++ b/demo-vault-v2/should-be-r-renamed.md @@ -0,0 +1,8 @@ +--- +title: Should Be R +type: Note +status: Active +--- +# Should Be R Renamed + +Snippet test content 1773606307602everted diff --git a/demo-vault-v2/test-note-a.md b/demo-vault-v2/test-note-a.md new file mode 100644 index 00000000..fec7d509 --- /dev/null +++ b/demo-vault-v2/test-note-a.md @@ -0,0 +1,8 @@ +--- +title: Test Note A +type: Note +status: Active +--- +# Test Note A + +[[MaBC diff --git a/demo-vault-v2/test-note-abc.md b/demo-vault-v2/test-note-abc.md new file mode 100644 index 00000000..881d4711 --- /dev/null +++ b/demo-vault-v2/test-note-abc.md @@ -0,0 +1,6 @@ +--- +title: Test Note ABC +type: Note +status: Active +--- +# Test Note ABC diff --git a/demo-vault-v2/theme/default-theme.md b/demo-vault-v2/theme/default-theme.md new file mode 100644 index 00000000..c63d6754 --- /dev/null +++ b/demo-vault-v2/theme/default-theme.md @@ -0,0 +1,20 @@ +--- +type: Theme +title: Default +primary: "#155DFF" +background: "#1a1a2e" +foreground: "#37352F" +sidebar: "#2a2a3e" +border: "#E9E9E7" +muted: "#F0F0EF" +muted-foreground: "#9B9A97" +accent: "#F0F7FF" +accent-foreground: "#0A3B8F" +font-family: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif" +font-size-base: 14 +line-height-base: 1.6 +--- + +# Default Theme + +Light theme with warm, paper-like tones. \ No newline at end of file diff --git a/demo-vault-v2/untitled-experiment-2.md b/demo-vault-v2/untitled-experiment-2.md new file mode 100644 index 00000000..8249f647 --- /dev/null +++ b/demo-vault-v2/untitled-experiment-2.md @@ -0,0 +1,22 @@ +--- +title: Untitled experiment 4 +type: Experiment +status: Active +--- + +# Untitled experiment 2 + +## Hypothesis + + + +## Method + + + +## Results + + + +## Conclusion + diff --git a/demo-vault-v2/untitled-experiment.md b/demo-vault-v2/untitled-experiment.md new file mode 100644 index 00000000..2b84774b --- /dev/null +++ b/demo-vault-v2/untitled-experiment.md @@ -0,0 +1,16 @@ +--- +title: Untitled experiment +type: Experiment +status: Active +--- +# Untitled experiment + +## Hypothesis + +## Method + +[[Ma + +## Results + +## Conclusion diff --git a/demo-vault-v2/untitled-not.md b/demo-vault-v2/untitled-not.md new file mode 100644 index 00000000..c3012d8f --- /dev/null +++ b/demo-vault-v2/untitled-not.md @@ -0,0 +1,8 @@ +--- +title: Untitled not +type: Note +status: Active +--- +# Untitled not + +[[Mae diff --git a/demo-vault-v2/untitled-save-no-rename-test.md b/demo-vault-v2/untitled-save-no-rename-test.md new file mode 100644 index 00000000..48f3ee39 --- /dev/null +++ b/demo-vault-v2/untitled-save-no-rename-test.md @@ -0,0 +1,6 @@ +--- +title: Untitled Save No Rename Test +type: Note +status: Active +--- +# Untitled Save No Rename Test diff --git a/demo-vault-v2/untitled-test-note-abc-2-renamed.md b/demo-vault-v2/untitled-test-note-abc-2-renamed.md new file mode 100644 index 00000000..953806ce --- /dev/null +++ b/demo-vault-v2/untitled-test-note-abc-2-renamed.md @@ -0,0 +1,6 @@ +--- +title: Untitled Test Note ABC 2 +type: Note +status: Active +--- +# Untitled Test Note ABC 2 Renamed diff --git a/demo-vault-v2/untitled-test-note-abc-2.md b/demo-vault-v2/untitled-test-note-abc-2.md new file mode 100644 index 00000000..76b75530 --- /dev/null +++ b/demo-vault-v2/untitled-test-note-abc-2.md @@ -0,0 +1,6 @@ +--- +title: Untitled Test Note ABC 2 +type: Note +status: Active +--- +# Untitled Test Note ABC 2 diff --git a/demo-vault-v2/untitled-test-note-abc.md b/demo-vault-v2/untitled-test-note-abc.md new file mode 100644 index 00000000..e75faccc --- /dev/null +++ b/demo-vault-v2/untitled-test-note-abc.md @@ -0,0 +1,6 @@ +--- +title: Untitled Test Note ABC +type: Note +status: Active +--- +# Untitled Test Note ABC diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index c5e0acb7..4925cdae 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -554,7 +554,7 @@ pub fn repair_vault(vault_path: String) -> Result { let vault_path = expand_tilde(&vault_path); // Repair themes theme::restore_default_themes(&vault_path)?; - // Repair config files (config/agents.md, type/config.md, AGENTS.md stub) + // Repair config files (config/agents.md, config.md type def, AGENTS.md stub) vault::repair_config_files(&vault_path)?; // Ensure .gitignore with sensible defaults exists git::ensure_gitignore(&vault_path)?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 09cddbcb..e457f1d6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -53,7 +53,7 @@ fn run_startup_tasks() { theme::seed_default_themes(vp_str); // Seed theme/ with built-in vault theme notes if missing theme::seed_vault_themes(vp_str); - // Seed type/theme.md so the Theme type has an icon in the sidebar + // Seed theme.md type definition so the Theme type has an icon in the sidebar let _ = theme::ensure_theme_type_definition(vp_str); // Migrate root AGENTS.md → config/agents.md (one-time, idempotent) diff --git a/src-tauri/src/theme/seed.rs b/src-tauri/src/theme/seed.rs index 342ec2bf..303a730a 100644 --- a/src-tauri/src/theme/seed.rs +++ b/src-tauri/src/theme/seed.rs @@ -107,17 +107,16 @@ pub fn restore_default_themes(vault_path: &str) -> Result { // Seed theme/ markdown notes (reuses ensure_vault_themes for consistency) ensure_vault_themes(vault_path)?; - // Seed type/theme.md so the Theme type has an icon and label in the sidebar + // Seed theme.md type definition so the Theme type has an icon and label in the sidebar ensure_theme_type_definition(vault_path)?; Ok("Default themes restored".to_string()) } -/// Create `type/theme.md` if it doesn't exist (gives the Theme type a sidebar icon/color). +/// Create `theme.md` at vault root if it doesn't exist (gives the Theme type a sidebar icon/color). pub fn ensure_theme_type_definition(vault_path: &str) -> Result<(), String> { - let type_dir = Path::new(vault_path).join("type"); - fs::create_dir_all(&type_dir).map_err(|e| format!("Failed to create type directory: {e}"))?; - write_if_missing(&type_dir.join("theme.md"), THEME_TYPE_DEFINITION)?; + let vault = Path::new(vault_path); + write_if_missing(&vault.join("theme.md"), THEME_TYPE_DEFINITION)?; Ok(()) } @@ -259,10 +258,10 @@ mod tests { assert!(vault.join("theme").join("dark.md").exists()); assert!(vault.join("theme").join("minimal.md").exists()); assert!( - vault.join("type").join("theme.md").exists(), - "restore must create type/theme.md" + vault.join("theme.md").exists(), + "restore must create theme.md" ); - let type_content = fs::read_to_string(vault.join("type").join("theme.md")).unwrap(); + let type_content = fs::read_to_string(vault.join("theme.md")).unwrap(); assert!(type_content.contains("type: Type")); assert!(type_content.contains("icon: palette")); } @@ -275,7 +274,7 @@ mod tests { let vp = vault.to_str().unwrap(); ensure_theme_type_definition(vp).unwrap(); - let path = vault.join("type").join("theme.md"); + let path = vault.join("theme.md"); assert!(path.exists()); let content = fs::read_to_string(&path).unwrap(); assert!(content.contains("type: Type")); @@ -286,14 +285,13 @@ mod tests { fn test_ensure_theme_type_definition_is_idempotent() { let dir = TempDir::new().unwrap(); let vault = dir.path().join("vault"); - let type_dir = vault.join("type"); - fs::create_dir_all(&type_dir).unwrap(); + fs::create_dir_all(&vault).unwrap(); let custom = "---\ntype: Type\nicon: swatches\ncolor: green\n---\n# Theme\n"; - fs::write(type_dir.join("theme.md"), custom).unwrap(); + fs::write(vault.join("theme.md"), custom).unwrap(); let vp = vault.to_str().unwrap(); ensure_theme_type_definition(vp).unwrap(); - let content = fs::read_to_string(type_dir.join("theme.md")).unwrap(); + let content = fs::read_to_string(vault.join("theme.md")).unwrap(); assert!( content.contains("swatches"), "existing content must be preserved" diff --git a/src-tauri/src/vault/cache.rs b/src-tauri/src/vault/cache.rs index 1f33b84b..ec67d5e1 100644 --- a/src-tauri/src/vault/cache.rs +++ b/src-tauri/src/vault/cache.rs @@ -623,7 +623,7 @@ mod tests { let vault = dir.path(); // Commit a type note without sidebar label - create_test_file(vault, "type/news.md", "---\ntype: Type\n---\n# News\n"); + create_test_file(vault, "news.md", "---\ntype: Type\n---\n# News\n"); git_add_commit(vault, "init"); // Prime the cache (same commit hash) @@ -634,7 +634,7 @@ mod tests { // User edits the type note to add sidebar label (uncommitted) create_test_file( vault, - "type/news.md", + "news.md", "---\ntype: Type\nsidebar label: News\n---\n# News\n", ); @@ -683,15 +683,15 @@ mod tests { let entries = scan_vault_cached(vault).unwrap(); assert_eq!(entries.len(), 1); - // Create files in a new subdirectory (simulates restore_default_themes) + // Create files in a new protected subdirectory (simulates asset creation) create_test_file( vault, - "theme/default.md", + "assets/default.md", "---\nIs A: Theme\n---\n# Default Theme\n", ); create_test_file( vault, - "theme/dark.md", + "assets/dark.md", "---\nIs A: Theme\n---\n# Dark Theme\n", ); @@ -716,7 +716,7 @@ mod tests { // Commit a type note with visible: false create_test_file( vault, - "type/topic.md", + "topic.md", "---\ntype: Type\nvisible: false\n---\n# Topic\n", ); git_add_commit(vault, "init"); @@ -731,7 +731,7 @@ mod tests { ); // User removes visible field (uncommitted edit) - create_test_file(vault, "type/topic.md", "---\ntype: Type\n---\n# Topic\n"); + create_test_file(vault, "topic.md", "---\ntype: Type\n---\n# Topic\n"); // Reload — must reflect the removal (visible defaults to None) let entries2 = scan_vault_cached(vault).unwrap(); diff --git a/src-tauri/src/vault/config_seed.rs b/src-tauri/src/vault/config_seed.rs index d6e2fcfb..811c401b 100644 --- a/src-tauri/src/vault/config_seed.rs +++ b/src-tauri/src/vault/config_seed.rs @@ -3,7 +3,7 @@ use std::path::Path; use super::getting_started::AGENTS_MD; -/// Content for `type/config.md` — gives the Config type a sidebar icon and label. +/// Content for `config.md` — gives the Config type a sidebar icon and label. const CONFIG_TYPE_DEFINITION: &str = "\ --- Is A: Type @@ -26,7 +26,7 @@ See config/agents.md for vault instructions. "; /// Seed `config/agents.md` if missing or empty (idempotent, per-file). -/// Also seeds `type/config.md` for sidebar visibility. +/// Also seeds `config.md` type definition for sidebar visibility. pub fn seed_config_files(vault_path: &str) { let vault = Path::new(vault_path); let config_dir = vault.join("config"); @@ -45,13 +45,9 @@ pub fn seed_config_files(vault_path: &str) { ensure_config_type_definition(vault_path); } -/// Ensure `type/config.md` exists (gives Config type a sidebar icon/color). +/// Ensure `config.md` exists at vault root (gives Config type a sidebar icon/color). fn ensure_config_type_definition(vault_path: &str) { - let type_dir = Path::new(vault_path).join("type"); - if fs::create_dir_all(&type_dir).is_err() { - return; - } - let path = type_dir.join("config.md"); + let path = Path::new(vault_path).join("config.md"); let needs_write = !path.exists() || fs::metadata(&path).map_or(true, |m| m.len() == 0); if needs_write { let _ = fs::write(&path, CONFIG_TYPE_DEFINITION); @@ -99,7 +95,7 @@ pub fn migrate_agents_md(vault_path: &str) { } } -/// Repair config files: re-create missing `config/agents.md` and `type/config.md`. +/// Repair config files: re-create missing `config/agents.md` and `config.md` type definition. /// Called by the "Repair Vault" command. Returns a status message. pub fn repair_config_files(vault_path: &str) -> Result { let vault = Path::new(vault_path); @@ -136,15 +132,13 @@ pub fn repair_config_files(vault_path: &str) -> Result { .map_err(|e| format!("Failed to write config/agents.md: {e}"))?; } - // Step 3: Ensure type/config.md - let type_dir = vault.join("type"); - fs::create_dir_all(&type_dir).map_err(|e| format!("Failed to create type directory: {e}"))?; - let config_type_path = type_dir.join("config.md"); + // Step 3: Ensure config.md type definition at vault root + let config_type_path = vault.join("config.md"); let type_needs_write = !config_type_path.exists() || fs::metadata(&config_type_path).map_or(true, |m| m.len() == 0); if type_needs_write { fs::write(&config_type_path, CONFIG_TYPE_DEFINITION) - .map_err(|e| format!("Failed to write type/config.md: {e}"))?; + .map_err(|e| format!("Failed to write config.md: {e}"))?; } // Step 4: Ensure root AGENTS.md stub exists @@ -186,8 +180,8 @@ mod tests { seed_config_files(vault.to_str().unwrap()); - assert!(vault.join("type/config.md").exists()); - let content = fs::read_to_string(vault.join("type/config.md")).unwrap(); + assert!(vault.join("config.md").exists()); + let content = fs::read_to_string(vault.join("config.md")).unwrap(); assert!(content.contains("Is A: Type")); assert!(content.contains("icon: gear-six")); } @@ -301,7 +295,7 @@ mod tests { assert_eq!(msg, "Config files repaired"); assert!(vault.join("config/agents.md").exists()); - assert!(vault.join("type/config.md").exists()); + assert!(vault.join("config.md").exists()); assert!(vault.join("AGENTS.md").exists()); let agents = fs::read_to_string(vault.join("config/agents.md")).unwrap(); diff --git a/src-tauri/src/vault/getting_started.rs b/src-tauri/src/vault/getting_started.rs index 5d75433c..d599f257 100644 --- a/src-tauri/src/vault/getting_started.rs +++ b/src-tauri/src/vault/getting_started.rs @@ -118,27 +118,27 @@ Available colors: red, purple, blue, green, yellow, orange. Icons are Phosphor n const SAMPLE_FILES: &[SampleFile] = &[ SampleFile { - rel_path: "type/project.md", + rel_path: "project.md", content: "---\ntype: Type\nicon: rocket-launch\ncolor: purple\norder: 1\n---\n\n# Project\n\nA Project is a time-bounded effort with a clear goal and an eventual completion date. Projects belong to a quarter or area and advance specific goals.\n", }, SampleFile { - rel_path: "type/note.md", + rel_path: "note.md", content: "---\ntype: Type\nicon: note\ncolor: blue\norder: 2\n---\n\n# Note\n\nA Note is a general-purpose document — research notes, meeting notes, strategy docs, or anything that doesn't fit a more specific type.\n", }, SampleFile { - rel_path: "type/person.md", + rel_path: "person.md", content: "---\ntype: Type\nicon: user\ncolor: green\norder: 3\n---\n\n# Person\n\nA Person represents someone you interact with — a colleague, friend, mentor, or collaborator.\n", }, SampleFile { - rel_path: "type/topic.md", + rel_path: "topic.md", content: "---\ntype: Type\nicon: tag\ncolor: yellow\norder: 4\n---\n\n# Topic\n\nA Topic is a subject area or interest category that groups related notes, projects, and people.\n", }, SampleFile { - rel_path: "type/theme.md", + rel_path: "theme.md", content: "---\ntype: Type\nicon: palette\ncolor: purple\norder: 50\n---\n\n# Theme\n\nA visual theme for Laputa. Each theme defines CSS custom properties that control colors, typography, and spacing.\n", }, SampleFile { - rel_path: "type/config.md", + rel_path: "config.md", content: "---\ntype: Type\nicon: gear-six\ncolor: gray\norder: 90\nsidebar label: Config\n---\n\n# Config\n\nVault configuration files. These control how AI agents, tools, and other integrations interact with this vault.\n", }, SampleFile { @@ -485,11 +485,11 @@ mod tests { assert!(vault_path.join("sample-project.md").exists()); assert!(vault_path.join("sample-collaborator.md").exists()); assert!(vault_path.join("getting-started.md").exists()); - assert!(vault_path.join("type/project.md").exists()); - assert!(vault_path.join("type/note.md").exists()); - assert!(vault_path.join("type/person.md").exists()); - assert!(vault_path.join("type/topic.md").exists()); - assert!(vault_path.join("type/config.md").exists()); + assert!(vault_path.join("project.md").exists()); + assert!(vault_path.join("note.md").exists()); + assert!(vault_path.join("person.md").exists()); + assert!(vault_path.join("topic.md").exists()); + assert!(vault_path.join("config.md").exists()); } #[test] @@ -544,8 +544,8 @@ mod tests { create_getting_started_vault(vault_path.to_str().unwrap()).unwrap(); let entries = crate::vault::scan_vault(&vault_path).unwrap(); - // SAMPLE_FILES + config/agents.md + AGENTS.md stub + 3 vault theme notes - assert_eq!(entries.len(), SAMPLE_FILES.len() + 2 + 3); + // SAMPLE_FILES (all at root) + AGENTS.md stub + assert_eq!(entries.len(), SAMPLE_FILES.len() + 1); } #[test] @@ -641,7 +641,7 @@ mod tests { assert!(vault_path.join("theme/minimal.md").exists()); // Theme type definition - assert!(vault_path.join("type/theme.md").exists()); + assert!(vault_path.join("theme.md").exists()); } #[test] diff --git a/src-tauri/src/vault/migration.rs b/src-tauri/src/vault/migration.rs index 2b83d4d0..0fc9a0df 100644 --- a/src-tauri/src/vault/migration.rs +++ b/src-tauri/src/vault/migration.rs @@ -118,8 +118,8 @@ pub fn migrate_is_a_to_type(vault_path: &str) -> Result { Ok(migrated) } -/// Folders that are NOT flattened — they contain definitions/config, not notes. -const KEEP_FOLDERS: &[&str] = &["type", "config", "attachments", "_themes", "theme"]; +/// Folders that are NOT flattened — they contain assets/themes, not notes. +const KEEP_FOLDERS: &[&str] = &["attachments", "_themes", "assets"]; /// Determine a unique filename at `dest_dir`, appending -2, -3, etc. on collision. fn unique_filename(dest_dir: &Path, filename: &str, taken: &HashSet) -> String { @@ -589,26 +589,18 @@ mod tests { } #[test] - fn test_flatten_vault_skips_type_and_config() { + fn test_flatten_vault_skips_protected_folders() { let tmp = tempdir().unwrap(); let vault = tmp.path(); - write_nested_file( - vault, - "type/project.md", - "---\ntype: Type\n---\n# Project\n", - ); - write_nested_file( - vault, - "config/agents.md", - "---\ntype: Config\n---\n# Agents\n", - ); + write_nested_file(vault, "attachments/image.md", "# Image note\n"); + write_nested_file(vault, "_themes/legacy.md", "---\n---\n# Legacy\n"); write_nested_file(vault, "note/hello.md", "---\ntype: Note\n---\n# Hello\n"); let count = flatten_vault(vault.to_str().unwrap()).unwrap(); assert_eq!(count, 1); assert!(vault.join("hello.md").exists()); - assert!(vault.join("type/project.md").exists()); - assert!(vault.join("config/agents.md").exists()); + assert!(vault.join("attachments/image.md").exists()); + assert!(vault.join("_themes/legacy.md").exists()); } #[test] @@ -721,16 +713,12 @@ mod tests { let tmp = tempdir().unwrap(); let vault = tmp.path(); write_file(vault, "root-note.md", "---\ntype: Note\n---\n# Root Note\n"); + write_file(vault, "project.md", "---\ntype: Type\n---\n# Project\n"); write_nested_file( vault, "old-folder/stray.md", "---\ntype: Note\n---\n# Stray\n", ); - write_nested_file( - vault, - "type/project.md", - "---\ntype: Type\n---\n# Project\n", - ); let report = vault_health_check(vault.to_str().unwrap()).unwrap(); assert_eq!(report.stray_files.len(), 1); @@ -742,11 +730,7 @@ mod tests { let tmp = tempdir().unwrap(); let vault = tmp.path(); write_file(vault, "my-note.md", "# My Note\n"); - write_nested_file( - vault, - "type/project.md", - "---\ntype: Type\n---\n# Project\n", - ); + write_file(vault, "project.md", "---\ntype: Type\n---\n# Project\n"); let report = vault_health_check(vault.to_str().unwrap()).unwrap(); assert!(report.stray_files.is_empty()); diff --git a/src-tauri/src/vault/mod.rs b/src-tauri/src/vault/mod.rs index a1e0cbd9..38ed9342 100644 --- a/src-tauri/src/vault/mod.rs +++ b/src-tauri/src/vault/mod.rs @@ -394,11 +394,11 @@ pub fn parse_md_file(path: &Path) -> Result { // Skip for type documents themselves (isA == "Type") to avoid self-referential links. if let Some(ref type_name) = is_a { if type_name != "Type" { - // If isA is already a wikilink (e.g. "[[type/project]]"), use it directly + // If isA is already a wikilink (e.g. "[[project]]"), use it directly let type_link = if type_name.starts_with("[[") && type_name.ends_with("]]") { type_name.clone() } else { - format!("[[type/{}]]", type_name.to_lowercase()) + format!("[[{}]]", type_name.to_lowercase()) }; relationships.insert("Type".to_string(), vec![type_link]); } @@ -519,9 +519,9 @@ pub fn save_note_content(path: &str, content: &str) -> Result<(), String> { } /// Scan a directory recursively for .md files and return VaultEntry for each. -/// Folders that are scanned recursively (type definitions, config, themes, attachments). -/// All other subfolders are ignored — notes live flat at the vault root. -const PROTECTED_FOLDERS: &[&str] = &["type", "config", "attachments", "_themes", "theme"]; +/// Folders that are scanned recursively (themes, attachments, assets). +/// All other subfolders are ignored — notes and type definitions live flat at the vault root. +const PROTECTED_FOLDERS: &[&str] = &["attachments", "_themes", "assets"]; pub fn scan_vault(vault_path: &Path) -> Result, String> { if !vault_path.exists() { @@ -700,14 +700,10 @@ mod tests { create_test_file(dir.path(), "root.md", "# Root Note\n"); create_test_file( dir.path(), - "type/project.md", + "project.md", "---\ntype: Type\n---\n# Project\n", ); - create_test_file( - dir.path(), - "config/agents.md", - "---\ntype: Config\n---\n# Agents\n", - ); + create_test_file(dir.path(), "attachments/notes.md", "# Attachment note\n"); create_test_file(dir.path(), "not-markdown.txt", "This should be ignored"); let entries = scan_vault(dir.path()).unwrap(); @@ -716,7 +712,7 @@ mod tests { let filenames: Vec<&str> = entries.iter().map(|e| e.filename.as_str()).collect(); assert!(filenames.contains(&"root.md")); assert!(filenames.contains(&"project.md")); - assert!(filenames.contains(&"agents.md")); + assert!(filenames.contains(&"notes.md")); } #[test] @@ -743,22 +739,12 @@ mod tests { fn test_scan_vault_includes_all_protected_folders() { let dir = TempDir::new().unwrap(); create_test_file(dir.path(), "root.md", "# Root\n"); - create_test_file( - dir.path(), - "type/event.md", - "---\ntype: Type\n---\n# Event\n", - ); - create_test_file(dir.path(), "config/ui.config.md", "---\n---\n# Config\n"); - create_test_file( - dir.path(), - "theme/default.md", - "---\ntype: Theme\n---\n# Default\n", - ); create_test_file(dir.path(), "_themes/legacy.md", "---\n---\n# Legacy\n"); create_test_file(dir.path(), "attachments/notes.md", "# Attachment note\n"); + create_test_file(dir.path(), "assets/image.md", "# Asset\n"); let entries = scan_vault(dir.path()).unwrap(); - assert_eq!(entries.len(), 6); + assert_eq!(entries.len(), 4); } #[test] @@ -872,7 +858,7 @@ Status: Active ); assert_eq!( entry.relationships.get("Type").unwrap(), - &vec!["[[type/responsibility]]".to_string()] + &vec!["[[responsibility]]".to_string()] ); } @@ -924,7 +910,7 @@ Custom Field: just a plain string assert_eq!(entry.relationships.len(), 1); assert_eq!( entry.relationships.get("Type").unwrap(), - &vec!["[[type/note]]".to_string()] + &vec!["[[note]]".to_string()] ); } @@ -1005,7 +991,7 @@ Context: "[[area/research]]" ); } - const SKIP_KEYS_CONTENT: &str = "---\nIs A: \"[[type/project]]\"\nAliases:\n - \"[[alias/foo]]\"\nStatus: \"[[status/active]]\"\nCadence: \"[[cadence/weekly]]\"\nCreated at: \"[[time/2024-01-01]]\"\nCreated time: \"[[time/noon]]\"\nReal Relation: \"[[note/important]]\"\n---\n# Skip Keys Test\n"; + const SKIP_KEYS_CONTENT: &str = "---\nIs A: \"[[project]]\"\nAliases:\n - \"[[alias/foo]]\"\nStatus: \"[[status/active]]\"\nCadence: \"[[cadence/weekly]]\"\nCreated at: \"[[time/2024-01-01]]\"\nCreated time: \"[[time/noon]]\"\nReal Relation: \"[[note/important]]\"\n---\n# Skip Keys Test\n"; fn parse_skip_keys_rels() -> (HashMap>, usize) { let dir = TempDir::new().unwrap(); @@ -1037,12 +1023,9 @@ Context: "[[area/research]]" rels.get("Real Relation").unwrap(), &vec!["[[note/important]]".to_string()] ); - // "Real Relation" + auto-generated "Type" (from is_a: "[[type/project]]") + // "Real Relation" + auto-generated "Type" (from is_a: "[[project]]") assert_eq!(len, 2); - assert_eq!( - rels.get("Type").unwrap(), - &vec!["[[type/project]]".to_string()] - ); + assert_eq!(rels.get("Type").unwrap(), &vec!["[[project]]".to_string()]); } #[test] @@ -1121,7 +1104,7 @@ References: let entry = parse_test_entry(&dir, "project/my-project.md", content); assert_eq!( entry.relationships.get("Type").unwrap(), - &vec!["[[type/project]]".to_string()] + &vec!["[[project]]".to_string()] ); } @@ -1129,7 +1112,7 @@ References: fn test_type_relationship_skipped_for_type_documents() { let dir = TempDir::new().unwrap(); let content = "---\nIs A: Type\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.relationships.get("Type").is_none()); } @@ -1145,11 +1128,11 @@ References: #[test] fn test_type_relationship_handles_wikilink_is_a() { let dir = TempDir::new().unwrap(); - let content = "---\nIs A: \"[[type/experiment]]\"\n---\n# Test\n"; + let content = "---\nIs A: \"[[experiment]]\"\n---\n# Test\n"; let entry = parse_test_entry(&dir, "test.md", content); assert_eq!( entry.relationships.get("Type").unwrap(), - &vec!["[[type/experiment]]".to_string()] + &vec!["[[experiment]]".to_string()] ); } @@ -1157,7 +1140,7 @@ References: fn test_type_from_frontmatter_not_folder() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\n---\n# Some Type\n"; - let entry = parse_test_entry(&dir, "type/some-type.md", content); + let entry = parse_test_entry(&dir, "some-type.md", content); assert_eq!(entry.is_a, Some("Type".to_string())); } @@ -1178,7 +1161,7 @@ References: let entry = parse_test_entry(&dir, "person/alice.md", content); assert_eq!( entry.relationships.get("Type").unwrap(), - &vec!["[[type/person]]".to_string()] + &vec!["[[person]]".to_string()] ); } @@ -1264,7 +1247,7 @@ References: fn test_parse_sidebar_label_from_type_entry() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nsidebar label: News\n---\n# News\n"; - let entry = parse_test_entry(&dir, "type/news.md", content); + let entry = parse_test_entry(&dir, "news.md", content); assert_eq!(entry.sidebar_label, Some("News".to_string())); } @@ -1272,7 +1255,7 @@ References: fn test_parse_sidebar_label_missing_defaults_to_none() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert_eq!(entry.sidebar_label, None); } @@ -1280,7 +1263,7 @@ References: fn test_sidebar_label_not_in_relationships() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nsidebar label: My Series\n---\n# Series\n"; - let entry = parse_test_entry(&dir, "type/series.md", content); + let entry = parse_test_entry(&dir, "series.md", content); assert!(entry.relationships.get("sidebar label").is_none()); } @@ -1291,7 +1274,7 @@ References: let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\ntemplate: \"## Objective\\n\\n## Timeline\"\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.template.is_some()); } @@ -1300,7 +1283,7 @@ References: let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\ntemplate: |\n ## Objective\n \n ## Timeline\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.template.is_some()); let tmpl = entry.template.unwrap(); assert!(tmpl.contains("## Objective")); @@ -1311,7 +1294,7 @@ References: fn test_parse_template_missing_defaults_to_none() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\n---\n# Note\n"; - let entry = parse_test_entry(&dir, "type/note.md", content); + let entry = parse_test_entry(&dir, "note.md", content); assert_eq!(entry.template, None); } @@ -1319,7 +1302,7 @@ References: fn test_template_not_in_relationships() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\ntemplate: \"## Heading\"\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.relationships.get("template").is_none()); } @@ -1329,7 +1312,7 @@ References: fn test_parse_sort_from_type_entry() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nsort: \"modified:desc\"\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert_eq!(entry.sort, Some("modified:desc".to_string())); } @@ -1337,7 +1320,7 @@ References: fn test_parse_sort_missing_defaults_to_none() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert_eq!(entry.sort, None); } @@ -1345,7 +1328,7 @@ References: fn test_sort_not_in_relationships() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nsort: \"title:asc\"\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.relationships.get("sort").is_none()); } @@ -1353,7 +1336,7 @@ References: fn test_sort_not_in_properties() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nsort: \"title:asc\"\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert!(entry.properties.get("sort").is_none()); } @@ -1588,7 +1571,7 @@ Company: Acme Corp fn test_parse_visible_false_from_type_entry() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nvisible: false\n---\n# Journal\n"; - let entry = parse_test_entry(&dir, "type/journal.md", content); + let entry = parse_test_entry(&dir, "journal.md", content); assert_eq!(entry.visible, Some(false)); } @@ -1596,7 +1579,7 @@ Company: Acme Corp fn test_parse_visible_true_from_type_entry() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nvisible: true\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert_eq!(entry.visible, Some(true)); } @@ -1604,7 +1587,7 @@ Company: Acme Corp fn test_parse_visible_missing_defaults_to_none() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\n---\n# Project\n"; - let entry = parse_test_entry(&dir, "type/project.md", content); + let entry = parse_test_entry(&dir, "project.md", content); assert_eq!(entry.visible, None); } @@ -1612,7 +1595,7 @@ Company: Acme Corp fn test_visible_not_in_relationships() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nvisible: false\n---\n# Journal\n"; - let entry = parse_test_entry(&dir, "type/journal.md", content); + let entry = parse_test_entry(&dir, "journal.md", content); assert!(entry.relationships.get("visible").is_none()); } @@ -1620,7 +1603,7 @@ Company: Acme Corp fn test_visible_not_in_properties() { let dir = TempDir::new().unwrap(); let content = "---\ntype: Type\nvisible: false\n---\n# Journal\n"; - let entry = parse_test_entry(&dir, "type/journal.md", content); + let entry = parse_test_entry(&dir, "journal.md", content); assert!(entry.properties.get("visible").is_none()); } diff --git a/src-tauri/src/vault_config.rs b/src-tauri/src/vault_config.rs index 9f949fb3..a087b6f7 100644 --- a/src-tauri/src/vault_config.rs +++ b/src-tauri/src/vault_config.rs @@ -142,7 +142,7 @@ fn yaml_safe_value(value: &str) -> String { /// on Type notes. Returns the number of Type notes updated. /// /// For each type name in `hidden_sections`: -/// - If `type/.md` exists, adds `visible: false` to its frontmatter +/// - If `.md` exists at vault root, adds `visible: false` to its frontmatter /// - If it doesn't exist, creates it with `type: Type`, `title: `, `visible: false` /// - Re-saves the config without `hidden_sections` pub fn migrate_hidden_sections_to_visible(vault_path: &str) -> Result { @@ -159,16 +159,12 @@ pub fn migrate_hidden_sections_to_visible(vault_path: &str) -> Result { /> ) fireEvent.click(screen.getByText('Project')) - expect(onNavigate).toHaveBeenCalledWith('type/project') + expect(onNavigate).toHaveBeenCalledWith('project') }) describe('TypeSelector', () => { const typeEntries = [ - makeEntry({ path: '/vault/type/project.md', title: 'Project', isA: 'Type' }), - makeEntry({ path: '/vault/type/person.md', title: 'Person', isA: 'Type' }), - makeEntry({ path: '/vault/type/topic.md', title: 'Topic', isA: 'Type' }), + makeEntry({ path: '/vault/project.md', title: 'Project', isA: 'Type' }), + makeEntry({ path: '/vault/person.md', title: 'Person', isA: 'Type' }), + makeEntry({ path: '/vault/topic.md', title: 'Topic', isA: 'Type' }), ] it('renders as dropdown when editable', () => { diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index e4f5f71f..9810107b 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -372,7 +372,7 @@ This is a test note with some words to count. fileSize: 500, snippet: '', wordCount: 0, - relationships: { 'Type': ['[[type/responsibility]]'] }, + relationships: { 'Type': ['[[responsibility]]'] }, icon: null, color: null, order: null, @@ -399,7 +399,7 @@ This is a test note with some words to count. fileSize: 300, snippet: '', wordCount: 0, - relationships: { 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/essay]]'] }, + relationships: { 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[essay]]'] }, icon: null, color: null, order: null, @@ -426,7 +426,7 @@ This is a test note with some words to count. fileSize: 400, snippet: '', wordCount: 0, - relationships: { 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/procedure]]'] }, + relationships: { 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[procedure]]'] }, icon: null, color: null, order: null, @@ -453,7 +453,7 @@ This is a test note with some words to count. fileSize: 200, snippet: '', wordCount: 0, - relationships: { 'Related to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/experiment]]'] }, + relationships: { 'Related to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[experiment]]'] }, icon: null, color: null, order: null, @@ -532,13 +532,13 @@ Status: Active it('skips Type relationships in referenced-by computation', () => { const typeEntry: VaultEntry = { ...targetEntry, - path: '/Users/luca/Laputa/type/responsibility.md', + path: '/Users/luca/Laputa/responsibility.md', filename: 'responsibility.md', title: 'Responsibility', isA: 'Type', relationships: {}, } - // essayEntry has Type: [[type/responsibility]] — should NOT show as referenced-by + // essayEntry has Type: [[responsibility]] — should NOT show as referenced-by render( ) // On Writing Well references responsibility via "Belongs to" (path match), not via "Type" - // But the Type entry is at type/responsibility.md, so wikilinks to + // But the Type entry is at responsibility.md, so wikilinks to // responsibility/grow-newsletter won't match. Section should be hidden expect(screen.queryByText('Referenced by')).not.toBeInTheDocument() }) @@ -561,7 +561,7 @@ Status: Active } const referrer: VaultEntry = { ...essayEntry, - relationships: { 'Topics': ['[[Newsletter]]'], 'Type': ['[[type/essay]]'] }, + relationships: { 'Topics': ['[[Newsletter]]'], 'Type': ['[[essay]]'] }, } render( { const onNavigate = vi.fn() const onAddProperty = vi.fn() const personTypeEntry = makeEntry({ - path: '/vault/type/person.md', filename: 'person.md', title: 'Person', + path: '/vault/person.md', filename: 'person.md', title: 'Person', isA: 'Type', color: 'yellow', icon: 'user', }) const entries = [ @@ -757,7 +757,7 @@ describe('GitHistoryPanel', () => { describe('InstancesPanel', () => { const onNavigate = vi.fn() const quarterType = makeEntry({ - path: '/vault/type/quarter.md', filename: 'quarter.md', title: 'Quarter', + path: '/vault/quarter.md', filename: 'quarter.md', title: 'Quarter', isA: 'Type', color: 'blue', icon: 'calendar', }) const typeEntryMap: Record = { Quarter: quarterType } diff --git a/src/components/NoteAutocomplete.test.tsx b/src/components/NoteAutocomplete.test.tsx index b29285ec..e4b14815 100644 --- a/src/components/NoteAutocomplete.test.tsx +++ b/src/components/NoteAutocomplete.test.tsx @@ -38,7 +38,7 @@ const entries = [ ] const personTypeEntry = makeEntry({ - path: '/vault/type/person.md', filename: 'person.md', title: 'Person', + path: '/vault/person.md', filename: 'person.md', title: 'Person', isA: 'Type', color: 'yellow', icon: 'user', }) const typeEntryMap: Record = { Person: personTypeEntry } diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index 702d0df5..d498cf92 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -422,7 +422,7 @@ describe('Sidebar', () => { const entriesWithCustomTypes: VaultEntry[] = [ ...mockEntries, { - path: '/vault/type/recipe.md', + path: '/vault/recipe.md', filename: 'recipe.md', title: 'Recipe', isA: 'Type', @@ -450,7 +450,7 @@ describe('Sidebar', () => { properties: {}, }, { - path: '/vault/type/book.md', + path: '/vault/book.md', filename: 'book.md', title: 'Book', isA: 'Type', @@ -592,7 +592,7 @@ describe('Sidebar', () => { it('does not show built-in types as custom sections', () => { const projectTypeEntry: VaultEntry = { - path: '/vault/type/project.md', + path: '/vault/project.md', filename: 'project.md', title: 'Project', isA: 'Type', @@ -629,7 +629,7 @@ describe('Sidebar', () => { const entriesWithLabel: VaultEntry[] = [ ...mockEntries, { - path: '/vault/type/news.md', filename: 'news.md', title: 'News', isA: 'Type', + path: '/vault/news.md', filename: 'news.md', title: 'News', isA: 'Type', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', wordCount: 0, relationships: {}, @@ -655,7 +655,7 @@ describe('Sidebar', () => { const entriesWithBuiltInOverride: VaultEntry[] = [ ...mockEntries, { - path: '/vault/type/person.md', filename: 'person.md', title: 'Person', isA: 'Type', + path: '/vault/person.md', filename: 'person.md', title: 'Person', isA: 'Type', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', wordCount: 0, relationships: {}, @@ -677,7 +677,7 @@ describe('Sidebar', () => { describe('type visibility via visible property', () => { const makeTypeEntry = (title: string, visible: boolean | null): VaultEntry => ({ - path: `/vault/type/${title.toLowerCase()}.md`, + path: `/vault/${title.toLowerCase()}.md`, filename: `${title.toLowerCase()}.md`, title, isA: 'Type', @@ -809,7 +809,7 @@ describe('Sidebar', () => { ...mockEntries, // Type entries with order values — reversed from default { - path: '/vault/type/project.md', filename: 'project.md', title: 'Project', isA: 'Type', + path: '/vault/project.md', filename: 'project.md', title: 'Project', isA: 'Type', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', wordCount: 0, @@ -817,7 +817,7 @@ describe('Sidebar', () => { properties: {}, }, { - path: '/vault/type/topic.md', filename: 'topic.md', title: 'Topic', isA: 'Type', + path: '/vault/topic.md', filename: 'topic.md', title: 'Topic', isA: 'Type', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', wordCount: 0, @@ -825,7 +825,7 @@ describe('Sidebar', () => { properties: {}, }, { - path: '/vault/type/person.md', filename: 'person.md', title: 'Person', isA: 'Type', + path: '/vault/person.md', filename: 'person.md', title: 'Person', isA: 'Type', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, trashed: false, trashedAt: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', wordCount: 0, diff --git a/src/components/TypeSelector.tsx b/src/components/TypeSelector.tsx index 52af9434..2926ef83 100644 --- a/src/components/TypeSelector.tsx +++ b/src/components/TypeSelector.tsx @@ -28,7 +28,7 @@ function ReadOnlyType({ isA, customColorKey, onNavigate }: { isA?: string | null ) : ( {isA} diff --git a/src/components/useNoteListSort.test.tsx b/src/components/useNoteListSort.test.tsx index 53c51bed..67925683 100644 --- a/src/components/useNoteListSort.test.tsx +++ b/src/components/useNoteListSort.test.tsx @@ -67,7 +67,7 @@ describe('useNoteListSort (via NoteList)', () => { }) it('reads sort from type document for sectionGroup selection', () => { - const typeDoc = makeEntry({ path: '/type/note.md', title: 'Note', isA: 'Type', sort: 'title:asc' }) + const typeDoc = makeEntry({ path: '/note.md', title: 'Note', isA: 'Type', sort: 'title:asc' }) const entries = [ typeDoc, makeEntry({ path: '/c.md', title: 'Charlie', modifiedAt: 3000 }), @@ -82,7 +82,7 @@ describe('useNoteListSort (via NoteList)', () => { }) it('shows type title as header for sectionGroup selection', () => { - const typeDoc = makeEntry({ path: '/type/project.md', title: 'Project', isA: 'Type' }) + const typeDoc = makeEntry({ path: '/project.md', title: 'Project', isA: 'Type' }) renderNoteList({ entries: [typeDoc], selection: { kind: 'sectionGroup', type: 'Project', label: 'Projects' } }) expect(screen.getByText('Project')).toBeInTheDocument() }) @@ -91,7 +91,7 @@ describe('useNoteListSort (via NoteList)', () => { localStorageMock.setItem('laputa-sort-preferences', JSON.stringify({ '__list__': { option: 'title', direction: 'asc' } })) const onUpdateTypeSort = vi.fn() const updateEntry = vi.fn() - const typeDoc = makeEntry({ path: '/type/project.md', title: 'Project', isA: 'Type', sort: null }) + const typeDoc = makeEntry({ path: '/project.md', title: 'Project', isA: 'Type', sort: null }) const entries = [typeDoc, makeEntry()] renderNoteList({ @@ -101,15 +101,15 @@ describe('useNoteListSort (via NoteList)', () => { updateEntry, }) - expect(onUpdateTypeSort).toHaveBeenCalledWith('/type/project.md', 'sort', 'title:asc') - expect(updateEntry).toHaveBeenCalledWith('/type/project.md', { sort: 'title:asc' }) + expect(onUpdateTypeSort).toHaveBeenCalledWith('/project.md', 'sort', 'title:asc') + expect(updateEntry).toHaveBeenCalledWith('/project.md', { sort: 'title:asc' }) }) it('does not migrate if type already has sort', () => { localStorageMock.setItem('laputa-sort-preferences', JSON.stringify({ '__list__': { option: 'title', direction: 'asc' } })) const onUpdateTypeSort = vi.fn() const updateEntry = vi.fn() - const typeDoc = makeEntry({ path: '/type/project.md', title: 'Project', isA: 'Type', sort: 'modified:desc' }) + const typeDoc = makeEntry({ path: '/project.md', title: 'Project', isA: 'Type', sort: 'modified:desc' }) const entries = [typeDoc, makeEntry()] renderNoteList({ diff --git a/src/hooks/useEntryActions.test.ts b/src/hooks/useEntryActions.test.ts index 21bc994f..7ad6c86c 100644 --- a/src/hooks/useEntryActions.test.ts +++ b/src/hooks/useEntryActions.test.ts @@ -38,7 +38,7 @@ describe('useEntryActions', () => { const handleDeleteProperty = vi.fn().mockResolvedValue(undefined) const setToastMessage = vi.fn() const createTypeEntry = vi.fn().mockImplementation((typeName: string) => - Promise.resolve(makeEntry({ isA: 'Type', title: typeName, path: `/vault/type/${typeName.toLowerCase()}.md` })), + Promise.resolve(makeEntry({ isA: 'Type', title: typeName, path: `/vault/${typeName.toLowerCase()}.md` })), ) function setup(entries: VaultEntry[] = []) { @@ -131,16 +131,16 @@ describe('useEntryActions', () => { describe('handleCustomizeType', () => { it('updates icon and color on the type entry', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md' }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleCustomizeType('Recipe', 'cooking-pot', 'green') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'icon', 'cooking-pot') - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'color', 'green') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { icon: 'cooking-pot', color: 'green' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'icon', 'cooking-pot') + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'color', 'green') + expect(updateEntry).toHaveBeenCalledWith('/vault/recipe.md', { icon: 'cooking-pot', color: 'green' }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) @@ -152,9 +152,9 @@ describe('useEntryActions', () => { }) expect(createTypeEntry).toHaveBeenCalledWith('Recipe') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { icon: 'star', color: 'red' }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'icon', 'star') - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'color', 'red') + expect(updateEntry).toHaveBeenCalledWith('/vault/recipe.md', { icon: 'star', color: 'red' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'icon', 'star') + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'color', 'red') }) it('serializes frontmatter writes (icon before color)', async () => { @@ -163,7 +163,7 @@ describe('useEntryActions', () => { callOrder.push(key) return Promise.resolve() }) - const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/type/project.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/project.md' }) const { result } = setup([typeEntry]) await act(async () => { @@ -176,28 +176,28 @@ describe('useEntryActions', () => { describe('handleUpdateTypeTemplate', () => { it('updates template on the type entry', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/type/project.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/project.md' }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleUpdateTypeTemplate('Project', '## Objective\n\n## Notes') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/project.md', 'template', '## Objective\n\n## Notes') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/project.md', { template: '## Objective\n\n## Notes' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/project.md', 'template', '## Objective\n\n## Notes') + expect(updateEntry).toHaveBeenCalledWith('/vault/project.md', { template: '## Objective\n\n## Notes' }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) it('sets template to null when empty string', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/type/project.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/project.md' }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleUpdateTypeTemplate('Project', '') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/project.md', 'template', '') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/project.md', { template: null }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/project.md', 'template', '') + expect(updateEntry).toHaveBeenCalledWith('/vault/project.md', { template: null }) }) it('auto-creates type entry when not found', async () => { @@ -208,15 +208,15 @@ describe('useEntryActions', () => { }) expect(createTypeEntry).toHaveBeenCalledWith('NonExistent') - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/nonexistent.md', 'template', '## Template') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/nonexistent.md', { template: '## Template' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/nonexistent.md', 'template', '## Template') + expect(updateEntry).toHaveBeenCalledWith('/vault/nonexistent.md', { template: '## Template' }) }) }) describe('handleReorderSections', () => { it('updates order on multiple type entries', async () => { - const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/type/note.md' }) - const typeB = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/type/project.md' }) + const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/note.md' }) + const typeB = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/project.md' }) const { result } = setup([typeA, typeB]) await act(async () => { @@ -226,15 +226,15 @@ describe('useEntryActions', () => { ]) }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/note.md', 'order', 0) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/project.md', 'order', 1) - expect(updateEntry).toHaveBeenCalledWith('/vault/type/note.md', { order: 0 }) - expect(updateEntry).toHaveBeenCalledWith('/vault/type/project.md', { order: 1 }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note.md', 'order', 0) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/project.md', 'order', 1) + expect(updateEntry).toHaveBeenCalledWith('/vault/note.md', { order: 0 }) + expect(updateEntry).toHaveBeenCalledWith('/vault/project.md', { order: 1 }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) it('auto-creates type entries when not found', async () => { - const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/type/note.md' }) + const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/note.md' }) const { result } = setup([typeA]) await act(async () => { @@ -246,47 +246,47 @@ describe('useEntryActions', () => { expect(createTypeEntry).toHaveBeenCalledWith('Missing') expect(handleUpdateFrontmatter).toHaveBeenCalledTimes(2) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/note.md', 'order', 0) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/missing.md', 'order', 1) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note.md', 'order', 0) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/missing.md', 'order', 1) }) }) describe('handleRenameSection', () => { it('writes sidebar label frontmatter and updates entry in memory', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md', sidebarLabel: null }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md', sidebarLabel: null }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleRenameSection('Recipe', 'Recipes') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'sidebar label', 'Recipes') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { sidebarLabel: 'Recipes' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'sidebar label', 'Recipes') + expect(updateEntry).toHaveBeenCalledWith('/vault/recipe.md', { sidebarLabel: 'Recipes' }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) it('trims whitespace before saving', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md', sidebarLabel: null }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md', sidebarLabel: null }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleRenameSection('Recipe', ' Dishes ') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'sidebar label', 'Dishes') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { sidebarLabel: 'Dishes' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/recipe.md', 'sidebar label', 'Dishes') + expect(updateEntry).toHaveBeenCalledWith('/vault/recipe.md', { sidebarLabel: 'Dishes' }) }) it('deletes sidebar label when label is empty', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md', sidebarLabel: 'Dishes' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md', sidebarLabel: 'Dishes' }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleRenameSection('Recipe', '') }) - expect(handleDeleteProperty).toHaveBeenCalledWith('/vault/type/recipe.md', 'sidebar label') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { sidebarLabel: null }) + expect(handleDeleteProperty).toHaveBeenCalledWith('/vault/recipe.md', 'sidebar label') + expect(updateEntry).toHaveBeenCalledWith('/vault/recipe.md', { sidebarLabel: null }) expect(handleUpdateFrontmatter).not.toHaveBeenCalled() }) @@ -298,35 +298,35 @@ describe('useEntryActions', () => { }) expect(createTypeEntry).toHaveBeenCalledWith('NonExistent') - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/nonexistent.md', 'sidebar label', 'Label') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/nonexistent.md', { sidebarLabel: 'Label' }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/nonexistent.md', 'sidebar label', 'Label') + expect(updateEntry).toHaveBeenCalledWith('/vault/nonexistent.md', { sidebarLabel: 'Label' }) }) }) describe('handleToggleTypeVisibility', () => { it('sets visible to false when currently visible (null/default)', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/type/journal.md', visible: null }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/journal.md', visible: null }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleToggleTypeVisibility('Journal') }) - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/journal.md', 'visible', false) - expect(updateEntry).toHaveBeenCalledWith('/vault/type/journal.md', { visible: false }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/journal.md', 'visible', false) + expect(updateEntry).toHaveBeenCalledWith('/vault/journal.md', { visible: false }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) it('sets visible to true (deletes property) when currently hidden', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/type/journal.md', visible: false }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/journal.md', visible: false }) const { result } = setup([typeEntry]) await act(async () => { await result.current.handleToggleTypeVisibility('Journal') }) - expect(handleDeleteProperty).toHaveBeenCalledWith('/vault/type/journal.md', 'visible') - expect(updateEntry).toHaveBeenCalledWith('/vault/type/journal.md', { visible: null }) + expect(handleDeleteProperty).toHaveBeenCalledWith('/vault/journal.md', 'visible') + expect(updateEntry).toHaveBeenCalledWith('/vault/journal.md', { visible: null }) expect(onFrontmatterPersisted).toHaveBeenCalled() }) @@ -338,14 +338,14 @@ describe('useEntryActions', () => { }) expect(createTypeEntry).toHaveBeenCalledWith('Journal') - expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/journal.md', 'visible', false) - expect(updateEntry).toHaveBeenCalledWith('/vault/type/journal.md', { visible: false }) + expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/journal.md', 'visible', false) + expect(updateEntry).toHaveBeenCalledWith('/vault/journal.md', { visible: false }) }) }) describe('failed disk writes do not update React state', () => { it('handleCustomizeType does not update entry when frontmatter write fails', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md' }) handleUpdateFrontmatter.mockRejectedValueOnce(new Error('disk full')) const { result } = setup([typeEntry]) @@ -357,7 +357,7 @@ describe('useEntryActions', () => { }) it('handleRenameSection does not update entry when frontmatter write fails', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md' }) handleUpdateFrontmatter.mockRejectedValueOnce(new Error('disk full')) const { result } = setup([typeEntry]) @@ -369,7 +369,7 @@ describe('useEntryActions', () => { }) it('handleRenameSection does not update entry when delete property fails', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md', sidebarLabel: 'Dishes' }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/recipe.md', sidebarLabel: 'Dishes' }) handleDeleteProperty.mockRejectedValueOnce(new Error('disk full')) const { result } = setup([typeEntry]) @@ -381,7 +381,7 @@ describe('useEntryActions', () => { }) it('handleToggleTypeVisibility does not update entry when frontmatter write fails (hide)', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/type/journal.md', visible: null }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/journal.md', visible: null }) handleUpdateFrontmatter.mockRejectedValueOnce(new Error('disk full')) const { result } = setup([typeEntry]) @@ -393,7 +393,7 @@ describe('useEntryActions', () => { }) it('handleToggleTypeVisibility does not update entry when delete property fails (show)', async () => { - const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/type/journal.md', visible: false }) + const typeEntry = makeEntry({ isA: 'Type', title: 'Journal', path: '/vault/journal.md', visible: false }) handleDeleteProperty.mockRejectedValueOnce(new Error('disk full')) const { result } = setup([typeEntry]) diff --git a/src/hooks/useNoteActions.test.ts b/src/hooks/useNoteActions.test.ts index 804f79e5..6ca87038 100644 --- a/src/hooks/useNoteActions.test.ts +++ b/src/hooks/useNoteActions.test.ts @@ -315,9 +315,9 @@ describe('resolveNewNote', () => { }) describe('resolveNewType', () => { - it('creates a type entry in the type folder', () => { + it('creates a type entry at vault root', () => { const { entry, content } = resolveNewType('Recipe', '/my/vault') - expect(entry.path).toBe('/my/vault/type/recipe.md') + expect(entry.path).toBe('/my/vault/recipe.md') expect(entry.isA).toBe('Type') expect(entry.status).toBeNull() expect(content).toContain('type: Type') @@ -326,7 +326,7 @@ describe('resolveNewType', () => { it('uses provided vault path instead of hardcoded path', () => { const { entry } = resolveNewType('Responsibility', '/other/vault') - expect(entry.path).toBe('/other/vault/type/responsibility.md') + expect(entry.path).toBe('/other/vault/responsibility.md') expect(entry.path).not.toContain('/Users/luca/Laputa') }) }) @@ -846,7 +846,7 @@ describe('useNoteActions hook', () => { it.each([ ['handleCreateNote', 'Failing Note', 'Note', 'failing-note.md'], - ['handleCreateType', 'Recipe', 'Type', 'type/recipe.md'], + ['handleCreateType', 'Recipe', 'Type', 'recipe.md'], ])('reverts optimistic creation via %s when disk write fails', async (method, title, type, pathFragment) => { vi.mocked(invoke).mockRejectedValueOnce(new Error('disk full')) const { result } = renderHook(() => useNoteActions(makeConfig())) diff --git a/src/hooks/useNoteActions.ts b/src/hooks/useNoteActions.ts index 87dcdb47..b122047c 100644 --- a/src/hooks/useNoteActions.ts +++ b/src/hooks/useNoteActions.ts @@ -199,7 +199,7 @@ export function resolveNewNote(title: string, type: string, vaultPath: string, t export function resolveNewType(typeName: string, vaultPath: string): { entry: VaultEntry; content: string } { const slug = slugify(typeName) - const entry = buildNewEntry({ path: `${vaultPath}/type/${slug}.md`, slug, title: typeName, type: 'Type', status: null }) + const entry = buildNewEntry({ path: `${vaultPath}/${slug}.md`, slug, title: typeName, type: 'Type', status: null }) return { entry, content: `---\ntype: Type\n---\n\n# ${typeName}\n\n` } } diff --git a/src/mock-tauri/mock-content.ts b/src/mock-tauri/mock-content.ts index d5973549..c235f03b 100644 --- a/src/mock-tauri/mock-content.ts +++ b/src/mock-tauri/mock-content.ts @@ -479,21 +479,21 @@ aliases: ## Role Content strategist — plans newsletter topics and manages the editorial calendar. `, - '/Users/luca/Laputa/type/project.md': `--- + '/Users/luca/Laputa/project.md': `--- type: Type order: 0 --- # Project -A **time-bound initiative** that advances a [[type/responsibility|Responsibility]]. Projects have a clear start, end, and deliverables. +A **time-bound initiative** that advances a [[responsibility|Responsibility]]. Projects have a clear start, end, and deliverables. ## Properties - **Status**: Active, Paused, Done, Dropped - **Owner**: The person accountable - **Belongs to**: Usually a Quarter or Responsibility `, - '/Users/luca/Laputa/type/responsibility.md': `--- + '/Users/luca/Laputa/responsibility.md': `--- type: Type order: 1 --- @@ -506,14 +506,14 @@ An **ongoing area of ownership** — something you're accountable for indefinite - **Status**: Active, Paused, Archived - **Owner**: The person accountable `, - '/Users/luca/Laputa/type/procedure.md': `--- + '/Users/luca/Laputa/procedure.md': `--- type: Type order: 2 --- # Procedure -A **recurring process** tied to a [[type/responsibility|Responsibility]]. Procedures have a cadence (weekly, monthly) and describe how to do something. +A **recurring process** tied to a [[responsibility|Responsibility]]. Procedures have a cadence (weekly, monthly) and describe how to do something. ## Properties - **Status**: Active, Paused @@ -521,7 +521,7 @@ A **recurring process** tied to a [[type/responsibility|Responsibility]]. Proced - **Cadence**: Weekly, Monthly, Quarterly - **Belongs to**: A Responsibility `, - '/Users/luca/Laputa/type/experiment.md': `--- + '/Users/luca/Laputa/experiment.md': `--- type: Type order: 3 --- @@ -534,7 +534,7 @@ A **hypothesis-driven investigation** with a clear test and measurable outcome. - **Status**: Active, Done, Dropped - **Owner**: The person running the experiment `, - '/Users/luca/Laputa/type/person.md': `--- + '/Users/luca/Laputa/person.md': `--- type: Type order: 4 --- @@ -546,7 +546,7 @@ A **person** you interact with — team members, collaborators, contacts. People ## Properties - **Aliases**: Alternative names for wikilink resolution `, - '/Users/luca/Laputa/type/event.md': `--- + '/Users/luca/Laputa/event.md': `--- type: Type order: 5 --- @@ -558,7 +558,7 @@ A **point-in-time occurrence** — meetings, launches, milestones. Events are li ## Properties - **Related to**: Entities this event is about `, - '/Users/luca/Laputa/type/topic.md': `--- + '/Users/luca/Laputa/topic.md': `--- type: Type order: 6 --- @@ -570,7 +570,7 @@ A **subject area** for categorization. Topics group related notes, projects, and ## Properties - **Aliases**: Alternative names `, - '/Users/luca/Laputa/type/essay.md': `--- + '/Users/luca/Laputa/essay.md': `--- type: Type order: 7 --- @@ -582,7 +582,7 @@ A **published piece of writing** — newsletter essays, blog posts, articles. Es ## Properties - **Belongs to**: Usually a Responsibility `, - '/Users/luca/Laputa/type/note.md': `--- + '/Users/luca/Laputa/note.md': `--- type: Type order: 8 --- @@ -594,7 +594,7 @@ A **general-purpose document** — research notes, meeting notes, strategy docs. ## Properties - **Belongs to**: A Project, Responsibility, or other parent `, - '/Users/luca/Laputa/type/recipe.md': `--- + '/Users/luca/Laputa/recipe.md': `--- type: Type icon: cooking-pot color: orange @@ -609,7 +609,7 @@ A **recipe** for cooking or baking. Recipes have ingredients, steps, and serving - **Prep Time**: Time to prepare - **Cook Time**: Time to cook `, - '/Users/luca/Laputa/type/book.md': `--- + '/Users/luca/Laputa/book.md': `--- type: Type icon: book-open color: green diff --git a/src/mock-tauri/mock-entries.ts b/src/mock-tauri/mock-entries.ts index ff15a0d9..4137cc85 100644 --- a/src/mock-tauri/mock-entries.ts +++ b/src/mock-tauri/mock-entries.ts @@ -30,7 +30,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Belongs to': ['[[q1-2026]]'], 'Related to': ['[[software-development]]'], - 'Type': ['[[type/project]]'], + 'Type': ['[[project]]'], }, icon: null, color: null, @@ -67,7 +67,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ ], 'Topics': ['[[growth]]', '[[writing]]'], 'Related to': ['[[growth]]'], - 'Type': ['[[type/responsibility]]'], + 'Type': ['[[responsibility]]'], }, icon: null, color: null, @@ -98,7 +98,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 180, relationships: { 'Owner': ['[[matteo-cellini|Matteo Cellini]]'], - 'Type': ['[[type/responsibility]]'], + 'Type': ['[[responsibility]]'], }, icon: null, color: null, @@ -129,7 +129,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 95, relationships: { 'Belongs to': ['[[grow-newsletter]]'], - 'Type': ['[[type/procedure]]'], + 'Type': ['[[procedure]]'], }, icon: null, color: null, @@ -160,7 +160,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 128, relationships: { 'Belongs to': ['[[manage-sponsorships]]'], - 'Type': ['[[type/procedure]]'], + 'Type': ['[[procedure]]'], }, icon: null, color: null, @@ -192,7 +192,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Related to': ['[[trading]]', '[[algorithmic-trading]]'], 'Has Data': ['[[ema200-backtest-results]]'], - 'Type': ['[[type/experiment]]'], + 'Type': ['[[experiment]]'], }, icon: null, color: null, @@ -224,7 +224,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Belongs to': ['[[26q1-laputa-app]]'], 'Related to': ['[[growth]]', '[[ads]]'], - 'Type': ['[[type/note]]'], + 'Type': ['[[note]]'], }, icon: null, color: null, @@ -255,7 +255,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 150, relationships: { 'Belongs to': ['[[26q1-laputa-app]]'], - 'Type': ['[[type/note]]'], + 'Type': ['[[note]]'], }, icon: null, color: null, @@ -285,7 +285,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Sponsorship manager — handles all sponsor relationships, proposals, and reporting.', wordCount: 88, relationships: { - 'Type': ['[[type/person]]'], + 'Type': ['[[person]]'], }, icon: null, color: null, @@ -315,7 +315,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Product designer — leads UX research and design sprints for the app.', wordCount: 120, relationships: { - 'Type': ['[[type/person]]'], + 'Type': ['[[person]]'], }, icon: null, color: null, @@ -345,7 +345,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Frontend engineer — focuses on React performance and accessibility.', wordCount: 95, relationships: { - 'Type': ['[[type/person]]'], + 'Type': ['[[person]]'], }, icon: null, color: null, @@ -375,7 +375,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Content strategist — plans newsletter topics and manages the editorial calendar.', wordCount: 75, relationships: { - 'Type': ['[[type/person]]'], + 'Type': ['[[person]]'], }, icon: null, color: null, @@ -406,7 +406,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 310, relationships: { 'Related to': ['[[26q1-laputa-app]]', '[[matteo-cellini]]'], - 'Type': ['[[type/event]]'], + 'Type': ['[[event]]'], }, icon: null, color: null, @@ -437,7 +437,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 45, relationships: { 'Notes': ['[[facebook-ads-strategy]]', '[[budget-allocation]]'], - 'Type': ['[[type/topic]]'], + 'Type': ['[[topic]]'], }, icon: null, color: null, @@ -468,7 +468,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 60, relationships: { 'Notes': ['[[stock-screener]]'], - 'Type': ['[[type/topic]]'], + 'Type': ['[[topic]]'], }, icon: null, color: null, @@ -499,7 +499,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 180, relationships: { 'Belongs to': ['[[grow-newsletter]]'], - 'Type': ['[[type/essay]]'], + 'Type': ['[[essay]]'], }, icon: null, color: null, @@ -531,7 +531,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Belongs to': ['[[grow-newsletter]]'], 'Related to': ['[[software-development]]'], - 'Type': ['[[type/essay]]'], + 'Type': ['[[essay]]'], }, icon: null, color: null, @@ -562,7 +562,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 410, relationships: { 'Belongs to': ['[[grow-newsletter]]'], - 'Type': ['[[type/essay]]'], + 'Type': ['[[essay]]'], }, icon: null, color: null, @@ -574,7 +574,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ }, // --- Type documents --- { - path: '/Users/luca/Laputa/type/project.md', + path: '/Users/luca/Laputa/project.md', filename: 'project.md', title: 'Project', isA: 'Type', @@ -602,7 +602,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/responsibility.md', + path: '/Users/luca/Laputa/responsibility.md', filename: 'responsibility.md', title: 'Responsibility', isA: 'Type', @@ -630,7 +630,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/procedure.md', + path: '/Users/luca/Laputa/procedure.md', filename: 'procedure.md', title: 'Procedure', isA: 'Type', @@ -658,7 +658,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/experiment.md', + path: '/Users/luca/Laputa/experiment.md', filename: 'experiment.md', title: 'Experiment', isA: 'Type', @@ -686,7 +686,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/person.md', + path: '/Users/luca/Laputa/person.md', filename: 'person.md', title: 'Person', isA: 'Type', @@ -714,7 +714,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/event.md', + path: '/Users/luca/Laputa/event.md', filename: 'event.md', title: 'Event', isA: 'Type', @@ -742,7 +742,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/topic.md', + path: '/Users/luca/Laputa/topic.md', filename: 'topic.md', title: 'Topic', isA: 'Type', @@ -770,7 +770,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/essay.md', + path: '/Users/luca/Laputa/essay.md', filename: 'essay.md', title: 'Essay', isA: 'Type', @@ -798,7 +798,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/note.md', + path: '/Users/luca/Laputa/note.md', filename: 'note.md', title: 'Note', isA: 'Type', @@ -827,7 +827,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ }, // --- Custom type documents --- { - path: '/Users/luca/Laputa/type/config.md', + path: '/Users/luca/Laputa/config.md', filename: 'config.md', title: 'Config', isA: 'Type', @@ -855,7 +855,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/recipe.md', + path: '/Users/luca/Laputa/recipe.md', filename: 'recipe.md', title: 'Recipe', isA: 'Type', @@ -883,7 +883,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ properties: {}, }, { - path: '/Users/luca/Laputa/type/book.md', + path: '/Users/luca/Laputa/book.md', filename: 'book.md', title: 'Book', isA: 'Type', @@ -931,7 +931,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Vault instructions for AI agents. Defines how tools and integrations interact with this vault.', wordCount: 200, relationships: { - 'Type': ['[[type/config]]'], + 'Type': ['[[config]]'], }, icon: null, color: null, @@ -961,7 +961,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Classic Roman pasta dish with eggs, pecorino, guanciale, and black pepper.', wordCount: 310, relationships: { - 'Type': ['[[type/recipe]]'], + 'Type': ['[[recipe]]'], }, icon: null, color: null, @@ -991,7 +991,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Essential reading for anyone building distributed systems. Covers replication, partitioning, transactions.', wordCount: 100, relationships: { - 'Type': ['[[type/book]]'], + 'Type': ['[[book]]'], }, icon: null, color: null, @@ -1023,7 +1023,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 100, relationships: { 'Belongs to': ['[[26q1-laputa-app]]'], - 'Type': ['[[type/note]]'], + 'Type': ['[[note]]'], }, icon: null, color: null, @@ -1053,7 +1053,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ snippet: 'Old API documentation for the v1 endpoint. Replaced by v2 docs.', wordCount: 85, relationships: { - 'Type': ['[[type/note]]'], + 'Type': ['[[note]]'], }, icon: null, color: null, @@ -1084,7 +1084,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 120, relationships: { 'Related to': ['[[grow-newsletter]]'], - 'Type': ['[[type/experiment]]'], + 'Type': ['[[experiment]]'], }, icon: null, color: null, @@ -1123,7 +1123,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 342, relationships: { 'Belongs to': ['[[q3-2025]]'], - 'Type': ['[[type/project]]'], + 'Type': ['[[project]]'], }, }, { @@ -1154,7 +1154,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ wordCount: 215, relationships: { 'Related to': ['[[grow-newsletter]]'], - 'Type': ['[[type/experiment]]'], + 'Type': ['[[experiment]]'], }, }, // --- Refactoring entries for exact-match search testing --- diff --git a/src/utils/noteListHelpers.test.ts b/src/utils/noteListHelpers.test.ts index ee2c07db..ba0a94d4 100644 --- a/src/utils/noteListHelpers.test.ts +++ b/src/utils/noteListHelpers.test.ts @@ -239,7 +239,7 @@ describe('buildRelationshipGroups', () => { it('excludes Type key from relationship groups', () => { const entity = makeEntry({ path: '/Laputa/project/alpha.md', filename: 'alpha.md', title: 'Alpha', - relationships: { Type: ['[[type/project]]'] }, + relationships: { Type: ['[[project]]'] }, }) const groups = buildRelationshipGroups(entity, [entity]) const labels = groups.map((g) => g.label) @@ -272,7 +272,7 @@ describe('buildRelationshipGroups', () => { const instance1 = makeEntry({ path: '/Laputa/project/a.md', filename: 'a.md', title: 'Project A', isA: 'Project', modifiedAt: 1700000000 }) const instance2 = makeEntry({ path: '/Laputa/project/b.md', filename: 'b.md', title: 'Project B', isA: 'Project', modifiedAt: 1700000000 }) const typeEntity = makeEntry({ - path: '/Laputa/type/project.md', filename: 'project.md', title: 'Project', + path: '/Laputa/project.md', filename: 'project.md', title: 'Project', isA: 'Type', relationships: {}, }) const groups = buildRelationshipGroups(typeEntity, [typeEntity, instance1, instance2]) diff --git a/src/utils/wikilinkColors.test.ts b/src/utils/wikilinkColors.test.ts index 63d4c565..1fc0a337 100644 --- a/src/utils/wikilinkColors.test.ts +++ b/src/utils/wikilinkColors.test.ts @@ -32,12 +32,12 @@ function makeEntry(overrides: Partial): VaultEntry { } } -const typeProject = makeEntry({ path: '/vault/type/project.md', filename: 'project.md', title: 'Project', isA: 'Type', color: 'red' }) -const typePerson = makeEntry({ path: '/vault/type/person.md', filename: 'person.md', title: 'Person', isA: 'Type', color: 'yellow' }) -const typeEvent = makeEntry({ path: '/vault/type/event.md', filename: 'event.md', title: 'Event', isA: 'Type', color: 'yellow' }) -const typeTopic = makeEntry({ path: '/vault/type/topic.md', filename: 'topic.md', title: 'Topic', isA: 'Type', color: 'green' }) -const typeRecipe = makeEntry({ path: '/vault/type/recipe.md', filename: 'recipe.md', title: 'Recipe', isA: 'Type', color: 'orange', icon: 'cooking-pot' }) -const typeNote = makeEntry({ path: '/vault/type/note.md', filename: 'note.md', title: 'Note', isA: 'Type', color: 'blue' }) +const typeProject = makeEntry({ path: '/vault/project.md', filename: 'project.md', title: 'Project', isA: 'Type', color: 'red' }) +const typePerson = makeEntry({ path: '/vault/person.md', filename: 'person.md', title: 'Person', isA: 'Type', color: 'yellow' }) +const typeEvent = makeEntry({ path: '/vault/event.md', filename: 'event.md', title: 'Event', isA: 'Type', color: 'yellow' }) +const typeTopic = makeEntry({ path: '/vault/topic.md', filename: 'topic.md', title: 'Topic', isA: 'Type', color: 'green' }) +const typeRecipe = makeEntry({ path: '/vault/recipe.md', filename: 'recipe.md', title: 'Recipe', isA: 'Type', color: 'orange', icon: 'cooking-pot' }) +const typeNote = makeEntry({ path: '/vault/note.md', filename: 'note.md', title: 'Note', isA: 'Type', color: 'blue' }) const projectEntry = makeEntry({ path: '/vault/project/app.md', filename: 'app.md', title: 'Build App', isA: 'Project' }) const personEntry = makeEntry({ path: '/vault/person/alice.md', filename: 'alice.md', title: 'Alice', isA: 'Person', aliases: ['Alice Smith'] })