From 1dfbbb8a9e5cfa6af58fdd2a0d273ae27033b12a Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sat, 21 Feb 2026 17:27:19 +0100 Subject: [PATCH] feat: add order property to VaultEntry and install @dnd-kit - Add `order: number | null` to VaultEntry (TS) and VaultEntry (Rust) - Add order to Frontmatter struct and SKIP_KEYS in vault.rs - Update all mock Type documents with order values (0-10) - Add order: null to all non-Type mock entries and test fixtures - Install @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities Co-Authored-By: Claude Opus 4.6 --- package.json | 3 ++ pnpm-lock.yaml | 56 +++++++++++++++++++++++++++++++ src-tauri/src/vault.rs | 7 +++- src/components/Editor.test.tsx | 1 + src/components/Inspector.test.tsx | 2 ++ src/components/NoteList.test.tsx | 7 ++++ src/components/Sidebar.test.tsx | 12 +++++++ src/hooks/useNoteActions.ts | 4 +-- src/mock-tauri.ts | 39 +++++++++++++++++++++ src/types.ts | 2 ++ 10 files changed, 130 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f03f7a28..60451401 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "@blocknote/core": "^0.46.2", "@blocknote/mantine": "^0.46.2", "@blocknote/react": "^0.46.2", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@mantine/core": "^8.3.14", "@phosphor-icons/react": "^2.1.10", "@radix-ui/react-dialog": "^1.1.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3f43253..949346a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,15 @@ importers: '@blocknote/react': specifier: ^0.46.2 version: 0.46.2(@floating-ui/dom@1.7.5)(@types/hast@3.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(highlight.js@11.11.1)(lowlight@3.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.2.4) '@mantine/core': specifier: ^8.3.14 version: 8.3.14(@mantine/hooks@8.3.14(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -322,6 +331,28 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + '@emoji-mart/data@1.2.1': resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} @@ -3811,6 +3842,31 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@dnd-kit/accessibility@3.1.1(react@19.2.4)': + dependencies: + react: 19.2.4 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) + react: 19.2.4 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@19.2.4)': + dependencies: + react: 19.2.4 + tslib: 2.8.1 + '@emoji-mart/data@1.2.1': {} '@esbuild/aix-ppc64@0.27.3': diff --git a/src-tauri/src/vault.rs b/src-tauri/src/vault.rs index 5005e98f..35bfa838 100644 --- a/src-tauri/src/vault.rs +++ b/src-tauri/src/vault.rs @@ -39,6 +39,8 @@ pub struct VaultEntry { pub icon: Option, /// Accent color key for Type entries: "red", "purple", "blue", "green", "yellow", "orange". pub color: Option, + /// Display order for Type entries in sidebar (lower = higher). None = use default order. + pub order: Option, } /// Intermediate struct to capture YAML frontmatter fields. @@ -68,6 +70,8 @@ struct Frontmatter { icon: Option, #[serde(default)] color: Option, + #[serde(default)] + order: Option, } /// Handles YAML fields that can be either a single string or a list of strings. @@ -214,7 +218,7 @@ fn parse_frontmatter(data: &HashMap) -> Frontmatter { /// Only skip keys that can never contain wikilinks. const SKIP_KEYS: &[&str] = &[ "is a", "aliases", "status", "cadence", "archived", "created at", "created time", - "icon", "color", + "icon", "color", "order", ]; /// Check if a string contains a wikilink pattern `[[...]]`. @@ -367,6 +371,7 @@ pub fn parse_md_file(path: &Path) -> Result { modified_at, created_at, file_size, icon: frontmatter.icon, color: frontmatter.color, + order: frontmatter.order, }) } diff --git a/src/components/Editor.test.tsx b/src/components/Editor.test.tsx index e1b6e392..0848f367 100644 --- a/src/components/Editor.test.tsx +++ b/src/components/Editor.test.tsx @@ -60,6 +60,7 @@ const mockEntry: VaultEntry = { relationships: {}, icon: null, color: null, + order: null, } const mockContent = `--- diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index 8bb351f6..e58f2c31 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -22,6 +22,7 @@ const mockEntry: VaultEntry = { relationships: {}, icon: null, color: null, + order: null, } const mockContent = `--- @@ -61,6 +62,7 @@ const referrerEntry: VaultEntry = { relationships: {}, icon: null, color: null, + order: null, } const allContent: Record = { diff --git a/src/components/NoteList.test.tsx b/src/components/NoteList.test.tsx index 91c2cc4a..439473c0 100644 --- a/src/components/NoteList.test.tsx +++ b/src/components/NoteList.test.tsx @@ -28,6 +28,7 @@ const mockEntries: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/note/facebook-ads-strategy.md', @@ -51,6 +52,7 @@ const mockEntries: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/person/matteo-cellini.md', @@ -71,6 +73,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/event/2026-02-14-kickoff.md', @@ -91,6 +94,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/topic/software-development.md', @@ -111,6 +115,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, ] @@ -268,6 +273,7 @@ describe('getSortComparator', () => { relationships: {}, icon: null, color: null, + order: null, ...overrides, }) @@ -343,6 +349,7 @@ describe('NoteList sort controls', () => { relationships: {}, icon: null, color: null, + order: null, ...overrides, }) diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index dfc6d417..b5b187d5 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -35,6 +35,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/responsibility/grow-newsletter.md', @@ -55,6 +56,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/experiment/stock-screener.md', @@ -75,6 +77,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/procedure/weekly-essays.md', @@ -95,6 +98,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/topic/software-development.md', @@ -115,6 +119,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/topic/trading.md', @@ -135,6 +140,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/person/alice.md', @@ -155,6 +161,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/event/kickoff.md', @@ -175,6 +182,7 @@ const mockEntries: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: null, }, ] @@ -322,6 +330,7 @@ describe('Sidebar', () => { relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/type/book.md', @@ -342,6 +351,7 @@ describe('Sidebar', () => { relationships: {}, icon: null, color: null, + order: null, }, { path: '/vault/recipe/pasta.md', @@ -362,6 +372,7 @@ describe('Sidebar', () => { relationships: {}, icon: null, color: null, + order: null, }, ] @@ -410,6 +421,7 @@ describe('Sidebar', () => { relationships: {}, icon: null, color: null, + order: null, } render( {}} />) // "Projects" should appear once (the built-in section), not twice diff --git a/src/hooks/useNoteActions.ts b/src/hooks/useNoteActions.ts index 30ede554..479d7098 100644 --- a/src/hooks/useNoteActions.ts +++ b/src/hooks/useNoteActions.ts @@ -214,7 +214,7 @@ export function useNoteActions( aliases: [], belongsTo: [], relatedTo: [], status: noStatusTypes.has(type) ? null : 'Active', owner: null, cadence: null, archived: false, modifiedAt: now, createdAt: now, fileSize: 0, - snippet: '', relationships: {}, icon: null, color: null, + snippet: '', relationships: {}, icon: null, color: null, order: null, } const frontmatter = [ @@ -242,7 +242,7 @@ export function useNoteActions( aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, archived: false, modifiedAt: now, createdAt: now, fileSize: 0, - snippet: '', relationships: {}, icon: null, color: null, + snippet: '', relationships: {}, icon: null, color: null, order: null, } const content = `---\nIs A: Type\n---\n\n# ${typeName}\n\n` diff --git a/src/mock-tauri.ts b/src/mock-tauri.ts index c013e6f0..618e871a 100644 --- a/src/mock-tauri.ts +++ b/src/mock-tauri.ts @@ -449,6 +449,7 @@ AI agents are autonomous systems that can plan, execute, and adapt to achieve go // --- Type documents --- '/Users/luca/Laputa/type/project.md': `--- Is A: Type +order: 0 --- # Project @@ -462,6 +463,7 @@ A **time-bound initiative** that advances a [[type/responsibility|Responsibility `, '/Users/luca/Laputa/type/responsibility.md': `--- Is A: Type +order: 1 --- # Responsibility @@ -474,6 +476,7 @@ An **ongoing area of ownership** — something you're accountable for indefinite `, '/Users/luca/Laputa/type/procedure.md': `--- Is A: Type +order: 2 --- # Procedure @@ -488,6 +491,7 @@ A **recurring process** tied to a [[type/responsibility|Responsibility]]. Proced `, '/Users/luca/Laputa/type/experiment.md': `--- Is A: Type +order: 3 --- # Experiment @@ -500,6 +504,7 @@ A **hypothesis-driven investigation** with a clear test and measurable outcome. `, '/Users/luca/Laputa/type/person.md': `--- Is A: Type +order: 4 --- # Person @@ -511,6 +516,7 @@ A **person** you interact with — team members, collaborators, contacts. People `, '/Users/luca/Laputa/type/event.md': `--- Is A: Type +order: 5 --- # Event @@ -522,6 +528,7 @@ A **point-in-time occurrence** — meetings, launches, milestones. Events are li `, '/Users/luca/Laputa/type/topic.md': `--- Is A: Type +order: 6 --- # Topic @@ -533,6 +540,7 @@ A **subject area** for categorization. Topics group related notes, projects, and `, '/Users/luca/Laputa/type/essay.md': `--- Is A: Type +order: 7 --- # Essay @@ -544,6 +552,7 @@ A **published piece of writing** — newsletter essays, blog posts, articles. Es `, '/Users/luca/Laputa/type/note.md': `--- Is A: Type +order: 8 --- # Note @@ -683,6 +692,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/responsibility/grow-newsletter.md', @@ -712,6 +722,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/responsibility/manage-sponsorships.md', @@ -735,6 +746,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/procedure/write-weekly-essays.md', @@ -758,6 +770,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/procedure/run-sponsorships.md', @@ -781,6 +794,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/experiment/stock-screener.md', @@ -805,6 +819,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/note/facebook-ads-strategy.md', @@ -829,6 +844,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/note/budget-allocation.md', @@ -852,6 +868,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/person/matteo-cellini.md', @@ -874,6 +891,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/event/2026-02-14-laputa-app-kickoff.md', @@ -897,6 +915,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/topic/software-development.md', @@ -920,6 +939,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/topic/trading.md', @@ -943,6 +963,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/essay/on-writing-well.md', @@ -966,6 +987,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/essay/engineering-leadership-101.md', @@ -990,6 +1012,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/essay/ai-agents-primer.md', @@ -1013,6 +1036,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, // --- Type documents --- { @@ -1034,6 +1058,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 0, }, { path: '/Users/luca/Laputa/type/responsibility.md', @@ -1054,6 +1079,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 1, }, { path: '/Users/luca/Laputa/type/procedure.md', @@ -1074,6 +1100,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 2, }, { path: '/Users/luca/Laputa/type/experiment.md', @@ -1094,6 +1121,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 3, }, { path: '/Users/luca/Laputa/type/person.md', @@ -1114,6 +1142,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 4, }, { path: '/Users/luca/Laputa/type/event.md', @@ -1134,6 +1163,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 5, }, { path: '/Users/luca/Laputa/type/topic.md', @@ -1154,6 +1184,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 6, }, { path: '/Users/luca/Laputa/type/essay.md', @@ -1174,6 +1205,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 7, }, { path: '/Users/luca/Laputa/type/note.md', @@ -1194,6 +1226,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: null, color: null, + order: 8, }, // --- Custom type documents (user-created types) --- { @@ -1215,6 +1248,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: 'cooking-pot', color: 'orange', + order: 9, }, { path: '/Users/luca/Laputa/type/book.md', @@ -1235,6 +1269,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: {}, icon: 'book-open', color: 'green', + order: 10, }, // --- Instances of custom types --- { @@ -1258,6 +1293,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, { path: '/Users/luca/Laputa/book/designing-data-intensive-applications.md', @@ -1280,6 +1316,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ }, icon: null, color: null, + order: null, }, // --- Archived entries --- { @@ -1296,6 +1333,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ archived: true, icon: null, color: null, + order: null, modifiedAt: Date.now() / 1000 - 86400 * 120, createdAt: Date.now() / 1000 - 86400 * 200, fileSize: 680, @@ -1319,6 +1357,7 @@ const MOCK_ENTRIES: VaultEntry[] = [ archived: true, icon: null, color: null, + order: null, modifiedAt: Date.now() / 1000 - 86400 * 90, createdAt: Date.now() / 1000 - 86400 * 150, fileSize: 520, diff --git a/src/types.ts b/src/types.ts index 3bf8ec29..a571ea3b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,6 +20,8 @@ export interface VaultEntry { icon: string | null /** Accent color key for Type entries: "red" | "purple" | "blue" | "green" | "yellow" | "orange" */ color: string | null + /** Display order for Type entries in sidebar (lower = higher). null = use default order. */ + order: number | null } export interface GitCommit {