diff --git a/src-tauri/src/vault/entry.rs b/src-tauri/src/vault/entry.rs index e464dbe8..bc9c34f5 100644 --- a/src-tauri/src/vault/entry.rs +++ b/src-tauri/src/vault/entry.rs @@ -59,6 +59,8 @@ pub struct VaultEntry { pub view: Option, /// Whether this Type is visible in the sidebar. Defaults to true when absent. pub visible: Option, + /// Whether this note has been explicitly organized (removed from Inbox). + pub organized: bool, /// Whether this note is a user favorite (shown in FAVORITES sidebar section). pub favorite: bool, /// Display order within the FAVORITES section (lower = higher). diff --git a/src-tauri/src/vault/frontmatter.rs b/src-tauri/src/vault/frontmatter.rs index 3dcb8044..3551ecb9 100644 --- a/src-tauri/src/vault/frontmatter.rs +++ b/src-tauri/src/vault/frontmatter.rs @@ -47,6 +47,12 @@ pub(crate) struct Frontmatter { pub view: Option, #[serde(default)] pub visible: Option, + #[serde( + rename = "_organized", + default, + deserialize_with = "deserialize_bool_or_string" + )] + pub organized: Option, #[serde( rename = "_favorite", default, @@ -169,6 +175,7 @@ fn parse_frontmatter(data: &HashMap) -> Frontmatter { "notion_id", "Status", "status", + "_organized", "_favorite", "_favorite_index", "_list_properties_display", @@ -206,6 +213,7 @@ const SKIP_KEYS: &[&str] = &[ "view", "visible", "status", + "_organized", "_favorite", "_favorite_index", "_list_properties_display", diff --git a/src-tauri/src/vault/mod.rs b/src-tauri/src/vault/mod.rs index 99d3315f..23e78599 100644 --- a/src-tauri/src/vault/mod.rs +++ b/src-tauri/src/vault/mod.rs @@ -115,6 +115,7 @@ pub fn parse_md_file(path: &Path, git_dates: Option<(u64, u64)>) -> Result> = { _trashed: { trashed: false }, trashed: { trashed: false }, order: { order: null }, template: { template: null }, sort: { sort: null }, visible: { visible: null }, + _organized: { organized: false }, _favorite: { favorite: false }, _favorite_index: { favoriteIndex: null }, _list_properties_display: { listPropertiesDisplay: [] }, } @@ -63,6 +64,7 @@ export function frontmatterToEntryPatch( sort: { sort: str }, view: { view: str }, visible: { visible: value === false ? false : null }, + _organized: { organized: Boolean(value) }, _favorite: { favorite: Boolean(value) }, _favorite_index: { favoriteIndex: typeof value === 'number' ? value : null }, _list_properties_display: { listPropertiesDisplay: Array.isArray(value) ? value.map(String) : [] }, diff --git a/src/hooks/useNoteCreation.ts b/src/hooks/useNoteCreation.ts index 9b7a29bb..9c4a4c6f 100644 --- a/src/hooks/useNoteCreation.ts +++ b/src/hooks/useNoteCreation.ts @@ -20,7 +20,7 @@ export function buildNewEntry({ path, slug, title, type, status }: NewEntryParam aliases: [], belongsTo: [], relatedTo: [], status, archived: false, trashed: false, trashedAt: null, modifiedAt: now, createdAt: now, fileSize: 0, - snippet: '', wordCount: 0, relationships: {}, icon: null, color: null, order: null, outgoingLinks: [], sidebarLabel: null, template: null, sort: null, view: null, visible: null, properties: {}, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + snippet: '', wordCount: 0, relationships: {}, icon: null, color: null, order: null, outgoingLinks: [], sidebarLabel: null, template: null, sort: null, view: null, visible: null, properties: {}, organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], } } diff --git a/src/mock-tauri/mock-entries.ts b/src/mock-tauri/mock-entries.ts index 2d792b59..c9471ad7 100644 --- a/src/mock-tauri/mock-entries.ts +++ b/src/mock-tauri/mock-entries.ts @@ -37,7 +37,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['q1-2026', 'software-development', 'matteo-cellini', 'maria-bianchi', 'marco-verdi'], properties: { Priority: 'High', 'Due date': '2026-06-15', Owner: 'Luca Rossi' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/grow-newsletter.md', @@ -73,7 +73,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['on-writing-well', 'engineering-leadership-101', 'ai-agents-primer', 'growth', 'writing'], properties: { Priority: 'High', Rating: 5, Cadence: 'Weekly', Owner: 'Luca Rossi' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/manage-sponsorships.md', @@ -103,7 +103,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['matteo-cellini'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/write-weekly-essays.md', @@ -133,7 +133,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['grow-newsletter'], properties: { Owner: 'Luca Rossi', Cadence: 'Weekly' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/run-sponsorships.md', @@ -163,7 +163,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['manage-sponsorships'], properties: { Owner: 'Matteo Cellini', Cadence: 'Weekly' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/stock-screener.md', @@ -194,7 +194,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['trading', 'algorithmic-trading', 'ema200-backtest-results'], properties: { Priority: 'Low', 'Due date': '2026-03-01', Owner: 'Luca Rossi' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/facebook-ads-strategy.md', @@ -225,7 +225,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['26q1-laputa-app', 'growth', 'ads'], properties: { Priority: 'Medium', Rating: 4 }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/budget-allocation.md', @@ -255,7 +255,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['26q1-laputa-app'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/matteo-cellini.md', @@ -284,7 +284,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: { Company: 'Acme Corp', Role: 'Engineering Lead' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/maria-bianchi.md', @@ -313,7 +313,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: { Company: 'TechStart', Role: 'Product Manager' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/marco-verdi.md', @@ -342,7 +342,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/elena-russo.md', @@ -371,7 +371,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/2026-02-14-laputa-app-kickoff.md', @@ -401,7 +401,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['26q1-laputa-app', 'matteo-cellini'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/software-development.md', @@ -431,7 +431,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/trading.md', @@ -461,7 +461,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/on-writing-well.md', @@ -491,7 +491,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['grow-newsletter'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/engineering-leadership-101.md', @@ -522,7 +522,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['grow-newsletter', 'software-development'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/ai-agents-primer.md', @@ -552,7 +552,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: ['grow-newsletter'], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Type documents --- { @@ -580,7 +580,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/responsibility.md', @@ -607,7 +607,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/procedure.md', @@ -634,7 +634,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/experiment.md', @@ -661,7 +661,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: false, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/person.md', @@ -688,7 +688,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/event.md', @@ -715,7 +715,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/topic.md', @@ -742,7 +742,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/essay.md', @@ -769,7 +769,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/note.md', @@ -796,7 +796,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Custom type documents --- { @@ -824,7 +824,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/recipe.md', @@ -851,7 +851,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/book.md', @@ -878,7 +878,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Instances of custom types --- { @@ -908,7 +908,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/pasta-carbonara.md', @@ -937,7 +937,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: { Difficulty: 'Easy', 'Prep time': '30 min', Servings: 4 }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/designing-data-intensive-applications.md', @@ -966,7 +966,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: { Author: 'Martin Kleppmann', Rating: 5, 'Year published': 2017 }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Trashed entries --- { @@ -997,7 +997,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/deprecated-api-notes.md', @@ -1026,7 +1026,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/failed-seo-experiment.md', @@ -1056,7 +1056,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: { Owner: 'Luca Rossi' }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Archived entries --- { @@ -1087,7 +1087,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[q3-2025]]'], 'Type': ['[[project]]'], }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/twitter-thread-experiment.md', @@ -1117,7 +1117,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[grow-newsletter]]'], 'Type': ['[[experiment]]'], }, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, // --- Refactoring entries for exact-match search testing --- { @@ -1145,7 +1145,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/refactoring-ideas.md', @@ -1172,7 +1172,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/refactoring-key-ideas.md', @@ -1199,7 +1199,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, { path: '/Users/luca/Laputa/refactoring-patterns.md', @@ -1226,7 +1226,7 @@ export const MOCK_ENTRIES: VaultEntry[] = [ template: null, sort: null, view: null, visible: null, outgoingLinks: [], properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }, ] @@ -1279,7 +1279,7 @@ function generateBulkEntries(count: number): VaultEntry[] { sidebarLabel: null, template: null, sort: null, view: null, visible: null, properties: {}, - favorite: false, favoriteIndex: null, listPropertiesDisplay: [], + organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], }) } return entries diff --git a/src/types.ts b/src/types.ts index 1b80b8a5..c6406032 100644 --- a/src/types.ts +++ b/src/types.ts @@ -35,6 +35,8 @@ export interface VaultEntry { view: string | null /** Whether this Type is visible in the sidebar. Defaults to true when absent. */ visible: boolean | null + /** Whether this note has been explicitly organized (removed from Inbox). */ + organized: boolean /** Whether this note is a user favorite (shown in FAVORITES sidebar section). */ favorite: boolean /** Display order within the FAVORITES section (lower = higher). */ diff --git a/src/utils/noteListHelpers.test.ts b/src/utils/noteListHelpers.test.ts index 0d2168b5..a0d53c02 100644 --- a/src/utils/noteListHelpers.test.ts +++ b/src/utils/noteListHelpers.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, vi, afterEach } from 'vitest' -import { formatSubtitle, formatSearchSubtitle, relativeDate, buildRelationshipGroups, getSortComparator, extractSortableProperties, getSortOptionLabel, getDefaultDirection, parseSortConfig, serializeSortConfig, buildValidLinkTargets, isInboxEntry, filterInboxEntries, filterEntries } from './noteListHelpers' +import { formatSubtitle, formatSearchSubtitle, relativeDate, buildRelationshipGroups, getSortComparator, extractSortableProperties, getSortOptionLabel, getDefaultDirection, parseSortConfig, serializeSortConfig, isInboxEntry, filterInboxEntries, filterEntries } from './noteListHelpers' import type { VaultEntry } from '../types' function makeEntry(overrides: Partial = {}): VaultEntry { return { path: '/vault/note/test.md', filename: 'test.md', title: 'Test', isA: 'Note', aliases: [], belongsTo: [], relatedTo: [], - status: null, archived: false, + status: null, organized: false, archived: false, trashed: false, trashedAt: null, modifiedAt: null, createdAt: null, fileSize: 0, snippet: '', wordCount: 0, relationships: {}, @@ -566,79 +566,35 @@ describe('parseSortConfig', () => { // --- Inbox --- -describe('buildValidLinkTargets', () => { - it('builds a set of titles, filename stems, and path stems', () => { - const entries = [ - makeEntry({ path: '/vault/project/my-project.md', filename: 'my-project.md', title: 'My Project', aliases: ['MP'] }), - makeEntry({ path: '/vault/note/test.md', filename: 'test.md', title: 'Test Note' }), - ] - const targets = buildValidLinkTargets(entries) - expect(targets.has('My Project')).toBe(true) - expect(targets.has('Test Note')).toBe(true) - expect(targets.has('my-project')).toBe(true) - expect(targets.has('test')).toBe(true) - expect(targets.has('MP')).toBe(true) - // path stems (last 2 segments without .md) - expect(targets.has('project/my-project')).toBe(true) - expect(targets.has('note/test')).toBe(true) - }) -}) - describe('isInboxEntry', () => { - const allEntries = [ - makeEntry({ path: '/vault/topic/ai.md', filename: 'ai.md', title: 'AI', aliases: [] }), - makeEntry({ path: '/vault/project/laputa.md', filename: 'laputa.md', title: 'Laputa' }), - ] - const validTargets = buildValidLinkTargets(allEntries) + it('returns true for a note that is not organized', () => { + const note = makeEntry({ organized: false }) + expect(isInboxEntry(note)).toBe(true) + }) - it('returns true for a note with no outgoing links and no relationships', () => { - const note = makeEntry({ outgoingLinks: [], relationships: {}, belongsTo: [], relatedTo: [] }) - expect(isInboxEntry(note, validTargets)).toBe(true) + it('returns false for an organized note', () => { + const note = makeEntry({ organized: true }) + expect(isInboxEntry(note)).toBe(false) }) it('returns false for a trashed note', () => { - const note = makeEntry({ trashed: true, outgoingLinks: [], relationships: {} }) - expect(isInboxEntry(note, validTargets)).toBe(false) + const note = makeEntry({ trashed: true }) + expect(isInboxEntry(note)).toBe(false) }) it('returns false for an archived note', () => { - const note = makeEntry({ archived: true, outgoingLinks: [], relationships: {} }) - expect(isInboxEntry(note, validTargets)).toBe(false) - }) - - it('returns false for a note with valid outgoing links', () => { - const note = makeEntry({ outgoingLinks: ['AI'] }) - expect(isInboxEntry(note, validTargets)).toBe(false) - }) - - it('returns true for a note with only broken outgoing links (non-existent targets)', () => { - const note = makeEntry({ outgoingLinks: ['NonExistent Page', 'Another Missing'] }) - expect(isInboxEntry(note, validTargets)).toBe(true) - }) - - it('returns false for a note with valid frontmatter relationships', () => { - const note = makeEntry({ outgoingLinks: [], relationships: { 'Related to': ['[[AI]]'] } }) - expect(isInboxEntry(note, validTargets)).toBe(false) - }) - - it('returns false for a note with belongsTo pointing to real note', () => { - const note = makeEntry({ outgoingLinks: [], belongsTo: ['[[Laputa]]'] }) - expect(isInboxEntry(note, validTargets)).toBe(false) - }) - - it('returns false for a note with relatedTo pointing to real note', () => { - const note = makeEntry({ outgoingLinks: [], relatedTo: ['[[AI]]'] }) - expect(isInboxEntry(note, validTargets)).toBe(false) - }) - - it('returns true for a note with only broken relationship refs', () => { - const note = makeEntry({ outgoingLinks: [], relationships: { 'Relates': ['[[Ghost]]'] }, belongsTo: ['[[Missing]]'] }) - expect(isInboxEntry(note, validTargets)).toBe(true) + const note = makeEntry({ archived: true }) + expect(isInboxEntry(note)).toBe(false) }) it('excludes Type entries from inbox', () => { - const note = makeEntry({ isA: 'Type', outgoingLinks: [], relationships: {} }) - expect(isInboxEntry(note, validTargets)).toBe(false) + const note = makeEntry({ isA: 'Type' }) + expect(isInboxEntry(note)).toBe(false) + }) + + it('returns true for a note without _organized field (defaults to false)', () => { + const note = makeEntry({}) + expect(isInboxEntry(note)).toBe(true) }) }) @@ -647,11 +603,11 @@ describe('filterInboxEntries', () => { const DAY = 86400 const allEntries = [ - makeEntry({ path: '/vault/a.md', filename: 'a.md', title: 'A', createdAt: now - 2 * DAY, outgoingLinks: [] }), - makeEntry({ path: '/vault/b.md', filename: 'b.md', title: 'B', createdAt: now - 15 * DAY, outgoingLinks: [] }), - makeEntry({ path: '/vault/c.md', filename: 'c.md', title: 'C', createdAt: now - 60 * DAY, outgoingLinks: [] }), - makeEntry({ path: '/vault/d.md', filename: 'd.md', title: 'D', createdAt: now - 120 * DAY, outgoingLinks: [] }), - makeEntry({ path: '/vault/linked.md', filename: 'linked.md', title: 'Linked', createdAt: now - 1 * DAY, outgoingLinks: ['A'] }), + makeEntry({ path: '/vault/a.md', filename: 'a.md', title: 'A', createdAt: now - 2 * DAY }), + makeEntry({ path: '/vault/b.md', filename: 'b.md', title: 'B', createdAt: now - 15 * DAY }), + makeEntry({ path: '/vault/c.md', filename: 'c.md', title: 'C', createdAt: now - 60 * DAY }), + makeEntry({ path: '/vault/d.md', filename: 'd.md', title: 'D', createdAt: now - 120 * DAY }), + makeEntry({ path: '/vault/org.md', filename: 'org.md', title: 'Organized', createdAt: now - 1 * DAY, organized: true }), ] it('filters by "week" period (last 7 days)', () => { @@ -681,17 +637,17 @@ describe('filterInboxEntries', () => { } }) - it('excludes linked notes', () => { + it('excludes organized notes', () => { const result = filterInboxEntries(allEntries, 'all') - expect(result.find(e => e.title === 'Linked')).toBeUndefined() + expect(result.find(e => e.title === 'Organized')).toBeUndefined() }) - it('returns empty array when all notes have valid outgoing links', () => { - const linked = [ - makeEntry({ path: '/vault/x.md', title: 'X', outgoingLinks: ['Y'] }), - makeEntry({ path: '/vault/y.md', title: 'Y', outgoingLinks: ['X'] }), + it('returns empty array when all notes are organized', () => { + const organized = [ + makeEntry({ path: '/vault/x.md', title: 'X', organized: true }), + makeEntry({ path: '/vault/y.md', title: 'Y', organized: true }), ] - const result = filterInboxEntries(linked, 'all') + const result = filterInboxEntries(organized, 'all') expect(result).toEqual([]) }) }) diff --git a/src/utils/noteListHelpers.ts b/src/utils/noteListHelpers.ts index 8db70535..a7f18e97 100644 --- a/src/utils/noteListHelpers.ts +++ b/src/utils/noteListHelpers.ts @@ -381,82 +381,30 @@ export function countAllByFilter(entries: VaultEntry[]): Record { - const targets = new Set() - for (const e of entries) { - targets.add(e.title) - const fileStem = e.filename.replace(/\.md$/, '') - targets.add(fileStem) - // path stem: everything after vault root, minus .md - // E.g. /Users/luca/Laputa/project/foo.md → project/foo - const parts = e.path.replace(/\.md$/, '').split('/') - // Try from index that gives "folder/name" pattern — skip first segments - if (parts.length >= 2) { - const last2 = parts.slice(-2).join('/') - if (last2 !== fileStem) targets.add(last2) - } - for (const alias of e.aliases) targets.add(alias) - } - return targets -} - -function extractRef(raw: string): string { - return raw.replace(/^\[\[/, '').replace(/\]\]$/, '').split('|')[0] -} - -function hasValidRef(refs: string[], validTargets: Set): boolean { - return refs.some((raw) => { - const inner = extractRef(raw) - return validTargets.has(inner) || validTargets.has(inner.split('/').pop() ?? '') - }) -} - -/** Check if entry has any valid outgoing link (body or frontmatter) that resolves to a real note. */ -export function isInboxEntry(entry: VaultEntry, validTargets: Set): boolean { +/** Check if entry belongs in the Inbox (not organized, not trashed/archived, not a Type). */ +export function isInboxEntry(entry: VaultEntry): boolean { if (entry.trashed || entry.archived) return false if (entry.isA === 'Type') return false - return !hasAnyValidLinks(entry, validTargets) -} - -function hasAnyValidLinks(entry: VaultEntry, validTargets: Set): boolean { - return hasValidBodyLinks(entry.outgoingLinks, validTargets) || hasValidFrontmatterLinks(entry, validTargets) -} - -function hasValidBodyLinks(outgoingLinks: string[], validTargets: Set): boolean { - return outgoingLinks.some((link) => validTargets.has(link) || validTargets.has(link.split('/').pop() ?? '')) -} - -function hasValidFrontmatterLinks(entry: VaultEntry, validTargets: Set): boolean { - if (entry.belongsTo?.length && hasValidRef(entry.belongsTo, validTargets)) return true - if (entry.relatedTo?.length && hasValidRef(entry.relatedTo, validTargets)) return true - - if (entry.relationships) { - return Object.values(entry.relationships).some((refs) => hasValidRef(refs, validTargets)) - } - - return false + return !entry.organized } const INBOX_PERIOD_DAYS: Record = { week: 7, month: 30, quarter: 90, all: Infinity, } -/** Filter entries for the Inbox view: no valid relationships, within the given time period, sorted by createdAt desc. */ +/** Filter entries for the Inbox view: not organized, within the given time period, sorted by createdAt desc. */ export function filterInboxEntries(entries: VaultEntry[], period: InboxPeriod): VaultEntry[] { - const validTargets = buildValidLinkTargets(entries) const now = Math.floor(Date.now() / 1000) const cutoff = period === 'all' ? 0 : now - INBOX_PERIOD_DAYS[period] * 86400 return entries - .filter((e) => isInboxEntry(e, validTargets) && (e.createdAt ?? 0) >= cutoff) + .filter((e) => isInboxEntry(e) && (e.createdAt ?? 0) >= cutoff) .sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0)) } /** Count inbox entries per period. */ export function countInboxByPeriod(entries: VaultEntry[]): Record { - const validTargets = buildValidLinkTargets(entries) - const inbox = entries.filter((e) => isInboxEntry(e, validTargets)) + const inbox = entries.filter((e) => isInboxEntry(e)) const now = Math.floor(Date.now() / 1000) let week = 0, month = 0, quarter = 0