vertex-notes 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,130 +1,130 @@
1
- # ▲ Vertex CLI
2
-
3
- **Your knowledge, structured — from the terminal.**
4
-
5
- Vertex is a keyboard-first knowledge workspace. This CLI gives you full access to your notes, todos, tags, and search from the command line.
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g vertex-notes
11
- ```
12
-
13
- ## Quick Start
14
-
15
- ```bash
16
- vertex login # Log in with GitHub, Google, or Email
17
- vertex notes # List all your notes
18
- vertex today # Open daily note in interactive mode
19
- vertex capture "idea" # Quick capture to Inbox
20
- ```
21
-
22
- ## Commands
23
-
24
- ### Auth
25
- | Command | Description |
26
- |---------|-------------|
27
- | `vertex login` | Log in (GitHub, Google, or Email) |
28
- | `vertex logout` | Log out and clear session |
29
- | `vertex whoami` | Show logged-in user |
30
-
31
- ### Notes
32
- | Command | Description |
33
- |---------|-------------|
34
- | `vertex notes` | List all notes |
35
- | `vertex new [title]` | Create a new note |
36
- | `vertex view <title>` | View note as markdown |
37
- | `vertex edit <title>` | Edit in $EDITOR (vim/nano/code) |
38
- | `vertex daily` | View or create today's daily note |
39
- | `vertex daily --last` | View the most recent daily note |
40
-
41
- ### Interactive Mode
42
-
43
- Open any note in a terminal UI with todo toggling:
44
-
45
- ```bash
46
- vertex today # Latest daily note
47
- vertex interactive <title> # Any note
48
- ```
49
-
50
- **Controls:**
51
-
52
- | Key | Action |
53
- |-----|--------|
54
- | `↑ ↓` | Move between items |
55
- | `Enter` | Toggle todo done/undone |
56
- | `t` | Add new todo |
57
- | `n` | Add new text line |
58
- | `d` | Delete item at cursor |
59
- | `s` | Save changes |
60
- | `q` | Save and quit |
61
-
62
- ### Capture & Search
63
-
64
- ```bash
65
- vertex capture "buy groceries" # Quick capture to Inbox
66
- vertex search "keyword" # Full-text search
67
- vertex search "#tag" # Search by tag
68
- vertex search "type:todo status:open" # Open todos
69
- vertex tags # List all tags
70
- ```
71
-
72
- ### Export
73
-
74
- ```bash
75
- vertex export "Meeting Notes" # Print as markdown
76
- vertex export "Meeting Notes" --json # Print as JSON
77
- vertex export "Meeting Notes" -o out.md # Save to file
78
- ```
79
-
80
- ### Trash
81
-
82
- ```bash
83
- vertex delete <title> # Move to trash
84
- vertex restore <title> # Restore from trash
85
- vertex trash # List trashed notes
86
- vertex trash:empty # Permanently delete all
87
- ```
88
-
89
- ### Info
90
-
91
- ```bash
92
- vertex status # Storage usage, note count
93
- vertex help # Full help with all commands
94
- ```
95
-
96
- ## Editor Syntax
97
-
98
- When using `vertex edit`, you write in markdown:
99
-
100
- ```markdown
101
- # Heading
102
- **bold** *italic* `code` ~~strike~~
103
- - Bullet list
104
- 1. Numbered list
105
- - [x] Done task
106
- - [ ] Open task
107
- > Blockquote
108
- [[Wiki Link]] #tagname
109
- ```
110
-
111
- Code blocks, math blocks (`$$ ... $$`), and standard markdown all work. Rich blocks (mermaid, callouts, file uploads) require the [web app](https://vertex-alu.pages.dev).
112
-
113
- ## Architecture
114
-
115
- The CLI shares the same backend as the Vertex web app:
116
-
117
- - **Database**: Supabase (PostgreSQL with RLS)
118
- - **Auth**: Supabase Auth (GitHub, Google, Email)
119
- - **Storage**: Cloudflare R2
120
-
121
- Notes created in the CLI appear in the web app and vice versa.
122
-
123
- ## Requirements
124
-
125
- - Node.js 18+
126
- - A Vertex account (sign up via `vertex login`)
127
-
128
- ## License
129
-
130
- MIT
1
+ # ▲ Vertex CLI
2
+
3
+ **Your knowledge, structured — from the terminal.**
4
+
5
+ Vertex is a keyboard-first knowledge workspace. This CLI gives you full access to your notes, todos, tags, and search from the command line.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g vertex-notes
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ vertex login # Log in with GitHub, Google, or Email
17
+ vertex notes # List all your notes
18
+ vertex today # Open daily note in interactive mode
19
+ vertex capture "idea" # Quick capture to Inbox
20
+ ```
21
+
22
+ ## Commands
23
+
24
+ ### Auth
25
+ | Command | Description |
26
+ |---------|-------------|
27
+ | `vertex login` | Log in (GitHub, Google, or Email) |
28
+ | `vertex logout` | Log out and clear session |
29
+ | `vertex whoami` | Show logged-in user |
30
+
31
+ ### Notes
32
+ | Command | Description |
33
+ |---------|-------------|
34
+ | `vertex notes` | List all notes |
35
+ | `vertex new [title]` | Create a new note |
36
+ | `vertex view <title>` | View note as markdown |
37
+ | `vertex edit <title>` | Edit in $EDITOR (vim/nano/code) |
38
+ | `vertex daily` | View or create today's daily note |
39
+ | `vertex daily --last` | View the most recent daily note |
40
+
41
+ ### Interactive Mode
42
+
43
+ Open any note in a terminal UI with todo toggling:
44
+
45
+ ```bash
46
+ vertex today # Latest daily note
47
+ vertex interactive <title> # Any note
48
+ ```
49
+
50
+ **Controls:**
51
+
52
+ | Key | Action |
53
+ |-----|--------|
54
+ | `↑ ↓` | Move between items |
55
+ | `Enter` | Toggle todo done/undone |
56
+ | `t` | Add new todo |
57
+ | `n` | Add new text line |
58
+ | `d` | Delete item at cursor |
59
+ | `s` | Save changes |
60
+ | `q` | Save and quit |
61
+
62
+ ### Capture & Search
63
+
64
+ ```bash
65
+ vertex capture "buy groceries" # Quick capture to Inbox
66
+ vertex search "keyword" # Full-text search
67
+ vertex search "#tag" # Search by tag
68
+ vertex search "type:todo status:open" # Open todos
69
+ vertex tags # List all tags
70
+ ```
71
+
72
+ ### Export
73
+
74
+ ```bash
75
+ vertex export "Meeting Notes" # Print as markdown
76
+ vertex export "Meeting Notes" --json # Print as JSON
77
+ vertex export "Meeting Notes" -o out.md # Save to file
78
+ ```
79
+
80
+ ### Trash
81
+
82
+ ```bash
83
+ vertex delete <title> # Move to trash
84
+ vertex restore <title> # Restore from trash
85
+ vertex trash # List trashed notes
86
+ vertex trash:empty # Permanently delete all
87
+ ```
88
+
89
+ ### Info
90
+
91
+ ```bash
92
+ vertex status # Storage usage, note count
93
+ vertex help # Full help with all commands
94
+ ```
95
+
96
+ ## Editor Syntax
97
+
98
+ When using `vertex edit`, you write in markdown:
99
+
100
+ ```markdown
101
+ # Heading
102
+ **bold** *italic* `code` ~~strike~~
103
+ - Bullet list
104
+ 1. Numbered list
105
+ - [x] Done task
106
+ - [ ] Open task
107
+ > Blockquote
108
+ [[Wiki Link]] #tagname
109
+ ```
110
+
111
+ Code blocks, math blocks (`$$ ... $$`), and standard markdown all work. Rich blocks (mermaid, callouts, file uploads) require the [web app](https://vertex-alu.pages.dev).
112
+
113
+ ## Architecture
114
+
115
+ The CLI shares the same backend as the Vertex web app:
116
+
117
+ - **Database**: Supabase (PostgreSQL with RLS)
118
+ - **Auth**: Supabase Auth (GitHub, Google, Email)
119
+ - **Storage**: Cloudflare R2
120
+
121
+ Notes created in the CLI appear in the web app and vice versa.
122
+
123
+ ## Requirements
124
+
125
+ - Node.js 18+
126
+ - A Vertex account (sign up via `vertex login`)
127
+
128
+ ## License
129
+
130
+ MIT
package/bin/run.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
-
3
- import { execute } from "@oclif/core";
4
- await execute({ dir: import.meta.url });
2
+
3
+ import { execute } from "@oclif/core";
4
+ await execute({ dir: import.meta.url });
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-DRIWYEQE.js";
5
5
  import {
6
6
  createSupabaseClient
7
- } from "./chunk-K5SQERJ7.js";
7
+ } from "./chunk-XJVEK2OB.js";
8
8
 
9
9
  // src/lib/client.ts
10
10
  var cachedClient = null;
@@ -1,10 +1,14 @@
1
1
  // ../core/dist/index.js
2
2
  import { createClient } from "@supabase/supabase-js";
3
+ import { WebSocket } from "ws";
3
4
  function createSupabaseClient(url, anonKey) {
4
5
  return createClient(url, anonKey, {
5
6
  auth: {
6
7
  autoRefreshToken: true,
7
8
  persistSession: true
9
+ },
10
+ realtime: {
11
+ transport: WebSocket
8
12
  }
9
13
  });
10
14
  }
@@ -198,11 +202,28 @@ async function getUserTags(client, userId) {
198
202
  if (error) throw error;
199
203
  return data ?? [];
200
204
  }
205
+ async function deleteTag(client, tagId) {
206
+ const { error } = await client.from("tags").delete().eq("id", tagId);
207
+ if (error) throw error;
208
+ }
201
209
  async function addTagEdge(client, userId, parentId, childId) {
202
210
  const { data, error } = await client.from("tag_edges").insert({ parent_id: parentId, child_id: childId, user_id: userId }).select().single();
203
211
  if (error) throw error;
204
212
  return data;
205
213
  }
214
+ async function removeTagEdge(client, parentId, childId) {
215
+ const { error } = await client.from("tag_edges").delete().eq("parent_id", parentId).eq("child_id", childId);
216
+ if (error) throw error;
217
+ }
218
+ async function getTagChildren(client, tagId) {
219
+ const { data, error } = await client.from("tag_edges").select("child_id").eq("parent_id", tagId);
220
+ if (error) throw error;
221
+ if (!data || data.length === 0) return [];
222
+ const childIds = data.map((e) => e.child_id);
223
+ const { data: tags, error: tagErr } = await client.from("tags").select("*").in("id", childIds);
224
+ if (tagErr) throw tagErr;
225
+ return tags ?? [];
226
+ }
206
227
  async function getTagDescendants(client, tagId) {
207
228
  const { data, error } = await client.rpc("get_tag_descendants", {
208
229
  root_tag_id: tagId
@@ -252,6 +273,29 @@ async function getBlocksForNote(client, noteId) {
252
273
  return data ?? [];
253
274
  }
254
275
  var saveLocks = /* @__PURE__ */ new Map();
276
+ var tagPathCache = /* @__PURE__ */ new Map();
277
+ var TAG_PATH_REGEX = /#([a-zA-Z][a-zA-Z0-9_-]*(?:\/[a-zA-Z][a-zA-Z0-9_-]*)*)/g;
278
+ function extractAllTagPaths(blockRows) {
279
+ const paths = /* @__PURE__ */ new Set();
280
+ for (const block of blockRows) {
281
+ let m;
282
+ while ((m = TAG_PATH_REGEX.exec(block.content)) !== null) {
283
+ paths.add(m[1].toLowerCase());
284
+ }
285
+ }
286
+ return paths;
287
+ }
288
+ function setsAreEqual(a, b) {
289
+ if (a.size !== b.size) return false;
290
+ for (const item of a) {
291
+ if (!b.has(item)) return false;
292
+ }
293
+ return true;
294
+ }
295
+ async function getNoteTagPathsFromDb(client, noteId) {
296
+ const { data } = await client.from("note_tags").select("tag_path").eq("note_id", noteId);
297
+ return new Set((data ?? []).map((r) => r.tag_path));
298
+ }
255
299
  async function saveNoteContent(client, noteId, userId, tiptapJson) {
256
300
  const existing = saveLocks.get(noteId);
257
301
  const doSave = async () => {
@@ -279,35 +323,73 @@ async function _saveNoteContentInner(client, noteId, userId, tiptapJson) {
279
323
  metadata: { tiptap: node }
280
324
  }));
281
325
  if (blocksToInsert.length === 0) return;
282
- const { error } = await client.from("blocks").delete().eq("note_id", noteId);
283
- if (error) throw error;
326
+ const { data: existingBlocks } = await client.from("blocks").select("id").eq("note_id", noteId);
327
+ const oldBlockIds = (existingBlocks ?? []).map((b) => b.id);
284
328
  const { error: insertError } = await client.from("blocks").insert(blocksToInsert);
285
329
  if (insertError) throw insertError;
330
+ if (oldBlockIds.length > 0) {
331
+ const { error: deleteError } = await client.from("blocks").delete().in("id", oldBlockIds);
332
+ if (deleteError) throw deleteError;
333
+ }
286
334
  const { data: insertedBlocks } = await client.from("blocks").select("id, content, type").eq("note_id", noteId).order("position");
287
335
  const blockRows = insertedBlocks ?? [];
288
- await client.from("block_tags").delete().in("block_id", blockRows.map((b) => b.id));
289
- await client.from("note_tags").delete().eq("note_id", noteId);
290
- const noteTagIds = /* @__PURE__ */ new Set();
291
- const SKIP_TAG_TYPES = /* @__PURE__ */ new Set(["code", "mermaid", "math", "image", "divider", "chart", "file"]);
292
- for (const block of blockRows) {
293
- if (SKIP_TAG_TYPES.has(block.type)) continue;
294
- const leafTags = extractLeafTags(block.content);
295
- for (const tagName of leafTags) {
296
- const tag = await getOrCreateTag(client, userId, tagName);
297
- await addBlockTag(client, block.id, tag.id).catch(() => {
298
- });
299
- noteTagIds.add(tag.id);
336
+ const currentPaths = extractAllTagPaths(blockRows);
337
+ const prevPaths = tagPathCache.get(noteId);
338
+ if (prevPaths && setsAreEqual(currentPaths, prevPaths)) {
339
+ } else {
340
+ let oldPathsForCleanup = prevPaths;
341
+ if (!oldPathsForCleanup) {
342
+ oldPathsForCleanup = await getNoteTagPathsFromDb(client, noteId);
300
343
  }
301
- const scopedTags = extractScopedTags(block.content);
302
- for (const { parent, child } of scopedTags) {
303
- const parentTag = await getOrCreateTag(client, userId, parent);
304
- const childTag = await getOrCreateTag(client, userId, child);
305
- await addTagEdge(client, userId, parentTag.id, childTag.id).catch(() => {
306
- });
344
+ await client.from("block_tags").delete().in("block_id", blockRows.map((b) => b.id));
345
+ await client.from("note_tags").delete().eq("note_id", noteId);
346
+ const SKIP_TAG_TYPES = /* @__PURE__ */ new Set(["code", "mermaid", "math", "image", "divider", "chart", "file", "summary"]);
347
+ const insertedNoteTags = [];
348
+ for (const block of blockRows) {
349
+ if (SKIP_TAG_TYPES.has(block.type)) continue;
350
+ const tagPaths = extractTagPaths(block.content);
351
+ for (const tagPath of tagPaths) {
352
+ const segments = tagPath.split("/");
353
+ const leafName = segments[segments.length - 1];
354
+ const tag = await getOrCreateTag(client, userId, leafName);
355
+ await addBlockTag(client, block.id, tag.id).catch(() => {
356
+ });
357
+ insertedNoteTags.push({ note_id: noteId, tag_id: tag.id, tag_path: tagPath });
358
+ }
359
+ const scopedTags = extractScopedTags(block.content);
360
+ for (const { parent, child } of scopedTags) {
361
+ const parentTag = await getOrCreateTag(client, userId, parent);
362
+ const childTag = await getOrCreateTag(client, userId, child);
363
+ await addTagEdge(client, userId, parentTag.id, childTag.id).catch(() => {
364
+ });
365
+ }
366
+ }
367
+ const seen = /* @__PURE__ */ new Set();
368
+ for (const nt of insertedNoteTags) {
369
+ if (seen.has(nt.tag_path)) continue;
370
+ seen.add(nt.tag_path);
371
+ await client.from("note_tags").upsert({ note_id: nt.note_id, tag_id: nt.tag_id, tag_path: nt.tag_path }, { onConflict: "note_id,tag_path" });
372
+ }
373
+ tagPathCache.set(noteId, currentPaths);
374
+ if (oldPathsForCleanup) {
375
+ const oldEdges = extractEdgesFromPaths(oldPathsForCleanup);
376
+ const newEdges = extractEdgesFromPaths(currentPaths);
377
+ const edgesToRemove = oldEdges.filter(
378
+ (oe) => !newEdges.some((ne) => ne.parent === oe.parent && ne.child === oe.child)
379
+ );
380
+ for (const { parent: parentName, child: childName } of edgesToRemove) {
381
+ const parentTag = await getOrCreateTag(client, userId, parentName);
382
+ const childTag = await getOrCreateTag(client, userId, childName);
383
+ await removeTagEdge(client, parentTag.id, childTag.id).catch(() => {
384
+ });
385
+ const { count: parentNoteTagCount } = await client.from("note_tags").select("*", { count: "exact", head: true }).eq("tag_id", parentTag.id);
386
+ const children = await getTagChildren(client, parentTag.id);
387
+ if (parentNoteTagCount === 0 && children.length === 0) {
388
+ await deleteTag(client, parentTag.id).catch(() => {
389
+ });
390
+ }
391
+ }
307
392
  }
308
- }
309
- for (const tagId of noteTagIds) {
310
- await client.from("note_tags").upsert({ note_id: noteId, tag_id: tagId }, { onConflict: "note_id,tag_id" });
311
393
  }
312
394
  const fullText = blockRows.map((b) => b.content).join(" ");
313
395
  const wikiLinkTitles = extractWikiLinks(fullText);
@@ -330,6 +412,7 @@ function mapTiptapTypeToBlockType(tiptapType) {
330
412
  mathBlock: "math",
331
413
  calloutBlock: "callout",
332
414
  queryBlock: "query",
415
+ summaryBlock: "summary",
333
416
  horizontalRule: "divider",
334
417
  fileBlock: "file",
335
418
  linkPreview: "text",
@@ -347,21 +430,26 @@ function extractPlainText(node) {
347
430
  if (!content) return "";
348
431
  return content.map(extractPlainText).join("");
349
432
  }
350
- function extractLeafTags(text) {
433
+ function extractTagPaths(text) {
351
434
  const matches = text.match(/#([a-zA-Z][a-zA-Z0-9_-]*(?:\/[a-zA-Z][a-zA-Z0-9_-]*)*)/g);
352
435
  if (!matches) return [];
353
436
  const tags = /* @__PURE__ */ new Set();
354
437
  for (const m of matches) {
355
- const full = m.slice(1).toLowerCase();
356
- if (full.includes("/")) {
357
- const parts = full.split("/");
358
- tags.add(parts[parts.length - 1]);
359
- } else {
360
- tags.add(full);
361
- }
438
+ tags.add(m.slice(1).toLowerCase());
362
439
  }
363
440
  return [...tags];
364
441
  }
442
+ function extractEdgesFromPaths(paths) {
443
+ const edges = [];
444
+ for (const path of paths) {
445
+ if (!path.includes("/")) continue;
446
+ const segments = path.split("/");
447
+ for (let i = 0; i < segments.length - 1; i++) {
448
+ edges.push({ parent: segments[i], child: segments[i + 1] });
449
+ }
450
+ }
451
+ return edges;
452
+ }
365
453
  function extractScopedTags(text) {
366
454
  const matches = text.match(/#([a-zA-Z][a-zA-Z0-9_-]*(?:\/[a-zA-Z][a-zA-Z0-9_-]*)+)/g);
367
455
  if (!matches) return [];
@@ -393,11 +481,23 @@ async function fullTextSearch(client, userId, query, limit = 30) {
393
481
  });
394
482
  }
395
483
  async function searchByTag(client, userId, tagName, includeDescendants = true) {
396
- let tagQuery = client.from("tags").select("id").eq("user_id", userId);
397
- if (tagName) {
398
- tagQuery = tagQuery.ilike("name", `${tagName}%`);
484
+ if (tagName.includes("/")) {
485
+ const matchPattern = includeDescendants ? `${tagName}%` : tagName;
486
+ const { data: noteTagRows } = await client.from("note_tags").select("note_id").ilike("tag_path", matchPattern);
487
+ if (!noteTagRows || noteTagRows.length === 0) return [];
488
+ const noteIds = [...new Set(noteTagRows.map((r) => r.note_id))];
489
+ const { data: blocks } = await client.from("blocks").select("*, notes!inner(id, title)").in("note_id", noteIds).eq("user_id", userId).is("deleted_at", null);
490
+ return (blocks ?? []).map((row) => {
491
+ const notes = row.notes;
492
+ return {
493
+ block: row,
494
+ noteId: notes.id,
495
+ noteTitle: notes.title,
496
+ matchType: "tag"
497
+ };
498
+ });
399
499
  }
400
- const { data: matchedTags } = await tagQuery;
500
+ const { data: matchedTags } = await client.from("tags").select("id").eq("user_id", userId).ilike("name", `${tagName}%`);
401
501
  if (!matchedTags || matchedTags.length === 0) return [];
402
502
  const allTagIds = /* @__PURE__ */ new Set();
403
503
  for (const tag of matchedTags) {
@@ -442,8 +542,7 @@ function parseQuery(input) {
442
542
  const textParts = [];
443
543
  for (const part of parts) {
444
544
  if (part.startsWith("#")) {
445
- const raw = part.slice(1).toLowerCase();
446
- filter.tag = raw.includes("/") ? raw.split("/").pop() : raw;
545
+ filter.tag = part.slice(1).toLowerCase();
447
546
  } else if (part.startsWith("type:")) {
448
547
  filter.type = part.slice(5);
449
548
  } else if (part.startsWith("status:")) {
@@ -481,6 +580,344 @@ async function executeQuery(client, userId, input, limit = 30) {
481
580
  }
482
581
  return results;
483
582
  }
583
+ var HELP_SECTIONS = {
584
+ "shortcuts": `# Keyboard Shortcuts
585
+
586
+ ## Global
587
+ - Cmd+B \u2014 Toggle sidebar
588
+ - Cmd+K \u2014 Command palette
589
+ - Cmd+P \u2014 Global search
590
+ - Cmd+G \u2014 Graph view
591
+ - Cmd+O \u2014 New note
592
+ - Cmd+S \u2014 Save snapshot (version checkpoint)
593
+ - Cmd+Shift+D \u2014 Open/create today's daily note
594
+ - F2 \u2014 Rename current note
595
+ - Cmd+Backspace \u2014 Delete current note (to Trash)
596
+ - Cmd+W \u2014 Close current tab
597
+ - Alt+Right \u2014 Next tab
598
+ - Alt+Left \u2014 Previous tab
599
+ - Cmd+\\ \u2014 Toggle focus mode
600
+ - Alt+Space \u2014 Quick capture to Inbox
601
+ - Cmd+/ \u2014 Open help
602
+ - Cmd+Shift+A \u2014 Toggle AI Chat panel
603
+ - Alt+Up \u2014 Move block up
604
+ - Alt+Down \u2014 Move block down
605
+ - Cmd+Z \u2014 Undo
606
+ - Cmd+Shift+Z \u2014 Redo
607
+
608
+ ## Multi-Pane
609
+ - Cmd+D \u2014 Split right (side by side)
610
+ - Cmd+Shift+E \u2014 Split down (top/bottom)
611
+ - Cmd+J \u2014 Close active pane
612
+ - Cmd+] \u2014 Focus next pane
613
+ - Cmd+[ \u2014 Focus previous pane
614
+
615
+ ## Text Formatting
616
+ - Cmd+B \u2014 Bold
617
+ - Cmd+I \u2014 Italic
618
+ - Cmd+E \u2014 Inline code
619
+ - Cmd+Shift+X \u2014 Strikethrough
620
+ - Cmd+Shift+M \u2014 Insert math block
621
+
622
+ ## Block Navigation
623
+ - Cmd+Enter \u2014 Exit code/math/mermaid block \u2192 preview + new paragraph
624
+ - / \u2014 Open slash command menu
625
+ - # \u2014 Tag autocomplete (type to filter)
626
+ - [[ \u2014 Wiki link autocomplete (type to filter)
627
+
628
+ ## Reminders
629
+ - Click clock icon on a todo \u2014 Set reminder
630
+ - Amber clock means a reminder is already set
631
+ - Sidebar \u2192 Reminders section \u2014 View all active reminders`,
632
+ "editor": `# Editor
633
+
634
+ ## Markdown Shortcuts (type at start of line)
635
+ - # \u2014 Heading 1
636
+ - ## \u2014 Heading 2
637
+ - ### \u2014 Heading 3
638
+ - #### \u2014 Heading 4
639
+ - - \u2014 Bullet list
640
+ - 1. \u2014 Numbered list
641
+ - [ ] \u2014 Task/checkbox
642
+ - > \u2014 Blockquote
643
+ - --- \u2014 Horizontal divider
644
+ - \`\`\` \u2014 Code block
645
+
646
+ ## Inline Syntax
647
+ - **text** \u2014 Bold
648
+ - *text* \u2014 Italic
649
+ - \`code\` \u2014 Inline code
650
+ - #tagname \u2014 Tag (renders as purple pill)
651
+ - #parent/child \u2014 Scoped tag (creates hierarchy)
652
+ - [[Note Name]] \u2014 Wiki link to another note
653
+ - $...$ \u2014 Inline math (LaTeX)
654
+
655
+ ## Note Title
656
+ The first H1 heading in a note automatically becomes the note title. You can also rename via F2, double-click the title in the status bar, or Cmd+K \u2192 Rename Note.`,
657
+ "blocks": `# Block Types
658
+
659
+ ## Slash Menu (type / at start of a line)
660
+
661
+ Available blocks:
662
+ - Text \u2014 Plain paragraph
663
+ - Heading 1\u20133 \u2014 Section headings
664
+ - Bullet List \u2014 Unordered list
665
+ - Numbered List \u2014 Ordered list
666
+ - Todo \u2014 Checkbox task item (supports nesting, has a clock icon for reminders)
667
+ - Code Block \u2014 Syntax-highlighted code with language selector and copy button
668
+ - Quote \u2014 Blockquote
669
+ - Divider \u2014 Horizontal rule
670
+ - Math \u2014 LaTeX equation. Edit the source, then press Cmd+Enter to preview the rendered formula (uses KaTeX).
671
+ - Callout \u2014 Info/Warning/Error/Tip box with a dropdown to switch type. Has colored backgrounds per variant.
672
+ - Mermaid \u2014 Diagram (flowchart, sequence, class, state, Gantt, pie, etc.). Edit the source, then press Cmd+Enter to preview.
673
+ - Table \u2014 Insert a 3x3 table with header row. Use Tab to navigate cells.
674
+ - Image \u2014 Upload, paste, or drag an image. Stored in Supabase Storage. Supports common formats.
675
+ - Excalidraw \u2014 Embedded whiteboard canvas. Opens a full drawing interface inline.
676
+ - File \u2014 PDF or audio file. Upload and view inline. Stored in R2 (Cloudflare).
677
+ - Summary \u2014 Collapsible preview block. Shows a snippet with a "Show more" toggle.
678
+ - Query \u2014 Live search block. Type a query and click Run to see matching results inline within the note.
679
+
680
+ ## Query Block Syntax
681
+ - type:todo \u2014 All todo blocks
682
+ - type:todo status:open \u2014 Open todos only
683
+ - type:todo status:done \u2014 Completed todos
684
+ - type:code \u2014 All code blocks
685
+ - #tagname \u2014 Blocks tagged with that tag
686
+ - keyword \u2014 Full-text search
687
+
688
+ ## Exiting Blocks
689
+ Press Cmd+Enter inside a code block, math block, or mermaid block to exit it, switch to preview mode, and create a new paragraph below.`,
690
+ "search": `# Search
691
+
692
+ ## Global Search (Cmd+P)
693
+ Search across all your notes and blocks. Results show the block content with a link to the source note. Click a result to navigate.
694
+
695
+ ### Filter Syntax
696
+ - keyword \u2014 Full-text search across all blocks
697
+ - #tagname \u2014 Find all blocks tagged with that tag (includes child tags via DAG)
698
+ - #parent/child \u2014 Searches by the leaf tag (e.g. #dsa/dp/tabular searches tabular)
699
+ - type:todo \u2014 All todo/checkbox blocks
700
+ - type:code \u2014 All code blocks
701
+ - type:heading \u2014 All headings
702
+ - status:open \u2014 Unchecked todos
703
+ - status:done \u2014 Checked todos
704
+ - Combine filters: type:todo status:open #work
705
+
706
+ ## Command Palette (Cmd+K)
707
+ Quick access to actions and note switching. Type to fuzzy-search.
708
+
709
+ Available actions:
710
+ - New Note \u2014 Create a blank note
711
+ - Today's Daily Note \u2014 Open or create today's daily note
712
+ - Rename Note \u2014 Rename the current note
713
+ - Delete Note \u2014 Soft-delete to Trash
714
+ - Tag Extend \u2014 Set parent\u2192child relationship between tags
715
+ - Graph View \u2014 Open the visual knowledge graph
716
+ - [note titles] \u2014 Switch to any note by typing its name`,
717
+ "tags": `# Tags
718
+
719
+ Tags are a core organizational feature of Vertex. They work like folders/tags in other tools but are more flexible.
720
+
721
+ ## Basic Usage
722
+ Type #tagname anywhere in your note text. Tags render as purple pills inline.
723
+
724
+ ## Hierarchical / Scoped Tags
725
+ Use #parent/child to create hierarchical tags:
726
+ - #work/projects \u2014 creates a "projects" tag with "work" as its parent
727
+ - #work/projects/alpha \u2014 deeper nesting
728
+ - Multi-parent and multi-level are supported (it's a DAG, not a tree)
729
+
730
+ ## Folder Structure
731
+ Tags create a folder-like hierarchy in the sidebar. Each tag path (e.g. "company/projects") gets its own folder node with correct note counts. This lets you organize notes like a file system \u2014 think of tags as folders:
732
+ - #design/mockups \u2014 all design mockup notes
733
+ - #engineering/frontend \u2014 frontend engineering notes
734
+ - #personal/finances \u2014 personal finance notes
735
+
736
+ ## How Tags Work
737
+ - Tags are stored per-block (not per-note), so different blocks in the same note can have different tags
738
+ - The sidebar shows a "Tags" section listing all tags with color dots and lock toggles
739
+ - Tag autocomplete: type # and start typing to see suggestions from existing tags
740
+ - Tag Extend: Cmd+K \u2192 "Tag Extend" to manually create parent\u2192child edges between existing tags
741
+ - The tag DAG (directed acyclic graph) supports complex hierarchies \u2014 a child can have multiple parents
742
+
743
+ ## Tag Locking
744
+ Lock any tag from the sidebar to prevent AI from reading notes under that tag. Locking walks the entire DAG \u2014 all descendant tags are also locked.
745
+
746
+ ## Tag Colors
747
+ Each tag can have a custom color (shown as a dot in the sidebar).`,
748
+ "wiki-links": `# Wiki Links & Backlinks
749
+
750
+ ## Creating Wiki Links
751
+ Type [[Note Name]] to create a link to another note. An autocomplete dropdown suggests existing notes as you type.
752
+
753
+ ## Navigation
754
+ Click any wiki link to navigate to the linked note.
755
+
756
+ ## Backlinks
757
+ The backlinks panel at the bottom of the editor shows all notes that link to the current note. Each backlink shows the source note title and you can click to navigate.`,
758
+ "version-history": `# Version History (Snapshots)
759
+
760
+ ## Saving Snapshots
761
+ - Manual: Press Cmd+S to save a version checkpoint
762
+ - Auto: Snapshots are automatically created every 5 saves
763
+
764
+ ## Viewing History
765
+ Click "Show version history" below the editor to see all snapshots. Each snapshot shows the timestamp.
766
+
767
+ ## Comparing Versions
768
+ Select any two versions to see a side-by-side diff view (opens as a new tab). Additions and removals are highlighted at the block level.
769
+
770
+ ## Rollback
771
+ Click "Restore" on any snapshot to rollback the note to that version with one click.`,
772
+ "graph-view": `# Graph View
773
+
774
+ Press Cmd+G to open the knowledge graph.
775
+
776
+ ## What You See
777
+ - Gray dots represent notes
778
+ - Purple dots represent tags
779
+ - Lines show wiki link connections (between notes) and tag relationships
780
+
781
+ ## Controls
782
+ - Hover over any dot to enlarge its label
783
+ - Click a note to navigate to it
784
+ - Mouse wheel to zoom
785
+ - Drag to pan
786
+
787
+ The graph uses ForceAtlas2 layout (powered by graphology + sigma.js) for natural-looking node positioning.`,
788
+ "quick-capture": `# Quick Capture
789
+
790
+ Press Alt+Space from anywhere in the app to open a quick capture input dialog.
791
+
792
+ Type anything and press Enter \u2014 the text is saved directly to your Inbox note. You stay on your current note (no navigation). This is useful for quickly jotting down ideas without context switching.`,
793
+ "multi-pane": `# Multi-Pane Layout
794
+
795
+ ## Splitting
796
+ - Cmd+D \u2014 Split the editor side-by-side (horizontal split)
797
+ - Cmd+Shift+E \u2014 Split top/bottom (vertical split)
798
+
799
+ ## Each Pane
800
+ Each pane has its own:
801
+ - Tab bar with open notes
802
+ - Editor with full functionality
803
+ - Status bar
804
+
805
+ ## Controls
806
+ - Drag the purple divider to resize panes
807
+ - Cmd+J \u2014 Close the active pane
808
+ - Cmd+] \u2014 Focus the next pane
809
+ - Cmd+[ \u2014 Focus the previous pane
810
+
811
+ The active pane's editor is shared with the AI Chat panel, so the AI knows which note you're currently editing.`,
812
+ "reminders": `# Reminders & Notifications
813
+
814
+ ## Setting Reminders
815
+ Every todo item (checkbox task) has a clock icon next to it. Click the clock to open the reminder picker. Set:
816
+ - Due date and time
817
+ - Repeat: once, daily, weekly, monthly, yearly, or custom
818
+ - "Custom" lets you pick specific days of the week (Mon, Tue, Wed, etc.) that fire every week, or specific dates of the month (1st\u201331st) that fire every month
819
+ - "Keep notifying until done": re-fires the reminder every N minutes until the task is checked off
820
+
821
+ ## Visual Indicator
822
+ An amber-colored clock icon means a reminder is already set on that todo. Gray means no reminder.
823
+
824
+ ## Delivery
825
+ - Push notifications are delivered via the browser's Web Push API (works on Chrome, Firefox, Edge)
826
+ - A Cloudflare R2 worker checks for due reminders every 5 minutes
827
+ - When a reminder fires, you get a browser notification \u2014 click it to open the note
828
+ - Notification title shows the task text
829
+
830
+ ## Repeating Reminders
831
+ For daily/weekly/monthly/yearly reminders:
832
+ - After firing, the reminder auto-reschedules to the next interval
833
+ - The task is automatically unchecked so you get reminded again
834
+ - For example, a daily reminder at 9 AM will fire every day at 9 AM
835
+
836
+ ## Notify Until Done
837
+ The reminder re-fires every N minutes (configurable delay) until you mark the task as done. Marking it done deletes the reminder and checks the task.
838
+
839
+ ## Viewing Reminders
840
+ - Sidebar \u2192 Reminders section lists all active reminders
841
+ - Each entry shows: task text, note title, and relative due date
842
+ - You can mark a reminder done directly from the sidebar
843
+
844
+ ## Stable Block IDs
845
+ Each task item has a persistent UUID (crypto.randomUUID()) that survives page reloads. This ensures reminders stay matched to their todo even after editing.`,
846
+ "ai-integration": `# AI Integration
847
+
848
+ ## AI Chat Panel
849
+ Press Cmd+Shift+A or click the AI button in the status bar to toggle the AI Chat panel.
850
+
851
+ ### Two Modes
852
+ 1. Ask mode \u2014 Q&A and research. The AI can search your notes, list todos, find tagged blocks, read daily notes, and answer questions based on your data.
853
+ 2. Edit mode \u2014 Structured inline content editing. The AI returns actions (insert, replace, delete, etc.) that appear as green/red suggestions in the editor. Accept or reject each change individually.
854
+
855
+ ### How Ask Mode Works
856
+ The AI has 7 tools it can use to fetch information:
857
+ 1. get_current_note \u2014 Read the currently open note's content
858
+ 2. search_notes \u2014 Full-text search across all notes
859
+ 3. get_note_by_title \u2014 Find a note by its title
860
+ 4. get_note_content \u2014 Get full content of a specific note by its ID
861
+ 5. get_recent_daily_notes \u2014 Get recent daily journal entries
862
+ 6. list_all_notes \u2014 List all note titles and dates
863
+ 7. list_todos \u2014 List tasks from notes, scoped to the current note + latest daily note by default
864
+
865
+ ### How Edit Mode Works
866
+ The AI understands the block structure of your note. It can:
867
+ - Insert new blocks after/before a specific block
868
+ - Replace a block's content
869
+ - Delete a block
870
+ - Use special syntax: {summary}...{/summary}, {callout info}...{/callout}, {query value="..."}{/query}
871
+
872
+ Changes appear as inline suggestions (green for inserts, red strikethrough for deletions) with accept/reject buttons. The editor is locked while changes are pending.
873
+
874
+ ### Providers & Settings
875
+ Supported AI providers: OpenRouter, Groq, OpenAI, Gemini, Cerebras
876
+ Configure API keys via the status bar gear icon \u2192 AI Settings. Keys are encrypted using AES-256-GCM and stored in the database.
877
+
878
+ ## AI Privacy (Tag Locking)
879
+ Lock any tag from the sidebar to prevent AI from reading notes under that tag. The AI will receive "locked: true" with no content and will stop trying to access that note. Locking walks the full tag DAG \u2014 locking a parent locks all descendants.`,
880
+ "share-links": `# Share Links
881
+
882
+ Click the share button in the status bar to generate a public share link. Anyone with the link can view the note without signing in. The link uses an unguessable random token.
883
+
884
+ You can revoke share links at any time from the share dialog. Shared notes appear in a "Shared Notes" section in the sidebar.`,
885
+ "tag-locking": `# Tag Locking (AI Privacy)
886
+
887
+ ## What It Does
888
+ Locking prevents AI from reading any note that has a locked tag. This is useful for sensitive notes that you don't want AI to access.
889
+
890
+ ## How It Works
891
+ - Note-level: Toggle the lock in the status bar \u2014 locks the current note
892
+ - Tag-level: Lock a tag from the sidebar \u2014 ALL notes under that tag are locked
893
+ - The locking walks the entire tag DAG: if you lock #work, every note tagged with #work/projects, #work/meetings, etc. is also locked
894
+ - Locked notes show a lock icon in the tab bar, sidebar, and status bar
895
+ - The AI Chat panel shows a banner for locked notes
896
+ - When AI tries to access a locked note, it receives "NOTE LOCKED" and is instructed to stop immediately`,
897
+ "mobile": `# Mobile
898
+
899
+ On mobile devices, Vertex provides:
900
+ - A bottom toolbar with quick access to AI chat, undo, redo, quick capture, and search
901
+ - Touch-friendly interface with adjusted hit targets
902
+ - Sidebar slides in as a full-width overlay with backdrop
903
+ - Responsive design adapts to smaller screens`,
904
+ "trash": `# Trash
905
+
906
+ Deleted notes go to Trash (soft delete \u2014 they can be recovered).
907
+
908
+ - Open the sidebar \u2192 Trash section to see trashed notes
909
+ - Hover over a trashed note to reveal:
910
+ - \u21BA Restore \u2014 brings the note back
911
+ - \u2297 Permanently delete \u2014 deletes the note and all associated blocks, tags, links, and snapshots
912
+ - "Empty" button deletes all trashed notes permanently
913
+ - Notes in trash still count toward your total note count`,
914
+ "themes": `# Themes
915
+
916
+ Click the \u2600\uFE0F/\u{1F319} button in the top bar to toggle between dark and light mode. Your preference is saved in localStorage and persists across sessions.
917
+
918
+ The app uses CSS variables for theming (--foreground, --background, --accent, --border, etc.) giving a consistent look across all UI elements.`
919
+ };
920
+ var HELP_TOPICS = Object.keys(HELP_SECTIONS).sort();
484
921
  function extractTextFromNode(node) {
485
922
  if (typeof node.text === "string") return node.text;
486
923
  const content = node.content;
@@ -927,7 +1364,7 @@ function parseInline(text) {
927
1364
  }
928
1365
  return nodes.length > 0 ? nodes : [{ type: "text", text: text || " " }];
929
1366
  }
930
- var VERTEX_VERSION = "0.1.0";
1367
+ var VERTEX_VERSION = "0.3.4";
931
1368
 
932
1369
  export {
933
1370
  createSupabaseClient,
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  getBlocksForNote,
8
8
  listNotes,
9
9
  saveNoteContent
10
- } from "../chunk-K5SQERJ7.js";
10
+ } from "../chunk-XJVEK2OB.js";
11
11
 
12
12
  // src/commands/capture.ts
13
13
  import { Command, Args } from "@oclif/core";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  exportNoteAsMarkdown,
8
8
  getOrCreateDailyNote,
9
9
  listNotes
10
- } from "../chunk-K5SQERJ7.js";
10
+ } from "../chunk-XJVEK2OB.js";
11
11
 
12
12
  // src/commands/daily.ts
13
13
  import { Command, Flags } from "@oclif/core";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  listNotes,
8
8
  softDeleteNote
9
- } from "../chunk-K5SQERJ7.js";
9
+ } from "../chunk-XJVEK2OB.js";
10
10
 
11
11
  // src/commands/delete.ts
12
12
  import { Command, Args } from "@oclif/core";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  exportNoteAsMarkdown,
8
8
  listNotes,
9
9
  markdownToTiptap,
10
10
  saveNoteContent
11
- } from "../chunk-K5SQERJ7.js";
11
+ } from "../chunk-XJVEK2OB.js";
12
12
 
13
13
  // src/commands/edit.ts
14
14
  import { Command, Args } from "@oclif/core";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  exportNoteAsJson,
8
8
  exportNoteAsMarkdown,
9
9
  listNotes
10
- } from "../chunk-K5SQERJ7.js";
10
+ } from "../chunk-XJVEK2OB.js";
11
11
 
12
12
  // src/commands/export.ts
13
13
  import { Command, Args, Flags } from "@oclif/core";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERTEX_VERSION
3
- } from "../chunk-K5SQERJ7.js";
3
+ } from "../chunk-XJVEK2OB.js";
4
4
 
5
5
  // src/commands/hello.ts
6
6
  import { Command } from "@oclif/core";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERTEX_VERSION
3
- } from "../chunk-K5SQERJ7.js";
3
+ } from "../chunk-XJVEK2OB.js";
4
4
 
5
5
  // src/commands/help.ts
6
6
  import { Command } from "@oclif/core";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  createNote,
8
8
  markdownToTiptap,
9
9
  saveNoteContent
10
- } from "../chunk-K5SQERJ7.js";
10
+ } from "../chunk-XJVEK2OB.js";
11
11
 
12
12
  // src/commands/import.ts
13
13
  import { Command, Args, Flags } from "@oclif/core";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  exportNoteAsMarkdown,
@@ -9,7 +9,7 @@ import {
9
9
  listNotes,
10
10
  markdownToTiptap,
11
11
  saveNoteContent
12
- } from "../chunk-K5SQERJ7.js";
12
+ } from "../chunk-XJVEK2OB.js";
13
13
 
14
14
  // src/commands/interactive.ts
15
15
  import { Command, Args } from "@oclif/core";
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-DRIWYEQE.js";
5
5
  import {
6
6
  createSupabaseClient
7
- } from "../chunk-K5SQERJ7.js";
7
+ } from "../chunk-XJVEK2OB.js";
8
8
 
9
9
  // src/commands/login.ts
10
10
  import { Command } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  createNote
8
- } from "../chunk-K5SQERJ7.js";
8
+ } from "../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/new.ts
11
11
  import { Command, Args } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  listNotes
8
- } from "../chunk-K5SQERJ7.js";
8
+ } from "../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/notes.ts
11
11
  import { Command } from "@oclif/core";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  listNotes,
8
8
  restoreNote
9
- } from "../chunk-K5SQERJ7.js";
9
+ } from "../chunk-XJVEK2OB.js";
10
10
 
11
11
  // src/commands/restore.ts
12
12
  import { Command, Args } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  executeQuery
8
- } from "../chunk-K5SQERJ7.js";
8
+ } from "../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/search.ts
11
11
  import { Command, Args } from "@oclif/core";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import {
6
6
  loadConfig
7
7
  } from "../chunk-DRIWYEQE.js";
@@ -10,7 +10,7 @@ import {
10
10
  formatFileSize,
11
11
  getStorageInfo,
12
12
  listNotes
13
- } from "../chunk-K5SQERJ7.js";
13
+ } from "../chunk-XJVEK2OB.js";
14
14
 
15
15
  // src/commands/status.ts
16
16
  import { Command } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  getUserTags
8
- } from "../chunk-K5SQERJ7.js";
8
+ } from "../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/tags.ts
11
11
  import { Command } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  listNotes
8
- } from "../chunk-K5SQERJ7.js";
8
+ } from "../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/today.ts
11
11
  import { Command } from "@oclif/core";
@@ -4,11 +4,11 @@ import {
4
4
  import {
5
5
  getClient,
6
6
  getUserId
7
- } from "../../chunk-LXT34CD7.js";
7
+ } from "../../chunk-QQO4JMNC.js";
8
8
  import "../../chunk-DRIWYEQE.js";
9
9
  import {
10
10
  emptyTrash
11
- } from "../../chunk-K5SQERJ7.js";
11
+ } from "../../chunk-XJVEK2OB.js";
12
12
 
13
13
  // src/commands/trash/empty.ts
14
14
  import { Command } from "@oclif/core";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../../chunk-LXT34CD7.js";
4
+ } from "../../chunk-QQO4JMNC.js";
5
5
  import "../../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  listNotes
8
- } from "../../chunk-K5SQERJ7.js";
8
+ } from "../../chunk-XJVEK2OB.js";
9
9
 
10
10
  // src/commands/trash/index.ts
11
11
  import { Command } from "@oclif/core";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
6
  import {
7
7
  exportNoteAsMarkdown,
8
8
  listNotes
9
- } from "../chunk-K5SQERJ7.js";
9
+ } from "../chunk-XJVEK2OB.js";
10
10
 
11
11
  // src/commands/view.ts
12
12
  import { Command, Args } from "@oclif/core";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getClient,
3
3
  getUserId
4
- } from "../chunk-LXT34CD7.js";
4
+ } from "../chunk-QQO4JMNC.js";
5
5
  import "../chunk-DRIWYEQE.js";
6
- import "../chunk-K5SQERJ7.js";
6
+ import "../chunk-XJVEK2OB.js";
7
7
  export {
8
8
  getClient,
9
9
  getUserId
package/package.json CHANGED
@@ -1,54 +1,56 @@
1
- {
2
- "name": "vertex-notes",
3
- "version": "0.3.3",
4
- "description": "Vertex — keyboard-first knowledge workspace CLI",
5
- "type": "module",
6
- "bin": {
7
- "vertex": "bin/run.js"
8
- },
9
- "main": "dist/index.js",
10
- "files": [
11
- "bin",
12
- "dist",
13
- "README.md"
14
- ],
15
- "keywords": [
16
- "vertex",
17
- "notes",
18
- "cli",
19
- "knowledge",
20
- "todo",
21
- "markdown",
22
- "productivity"
23
- ],
24
- "author": "Sahil Kumar Sinha",
25
- "license": "MIT",
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/sahilcodes2002/vertex.git"
29
- },
30
- "engines": {
31
- "node": ">=18"
32
- },
33
- "scripts": {
34
- "build": "tsup",
35
- "typecheck": "tsc --noEmit",
36
- "clean": "rm -rf dist",
37
- "prepublishOnly": "pnpm build"
38
- },
39
- "dependencies": {
40
- "@oclif/core": "^4.2.0",
41
- "@supabase/supabase-js": "^2.49.0"
42
- },
43
- "devDependencies": {
44
- "@vertex/core": "workspace:*",
45
- "tsup": "^8.4.0",
46
- "typescript": "^5.7.0",
47
- "oclif": "^4.17.0"
48
- },
49
- "oclif": {
50
- "bin": "vertex",
51
- "dirname": "vertex",
52
- "commands": "./dist/commands"
53
- }
54
- }
1
+ {
2
+ "name": "vertex-notes",
3
+ "version": "0.3.4",
4
+ "description": "Vertex — keyboard-first knowledge workspace CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "vertex": "bin/run.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "files": [
11
+ "bin",
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "vertex",
17
+ "notes",
18
+ "cli",
19
+ "knowledge",
20
+ "todo",
21
+ "markdown",
22
+ "productivity"
23
+ ],
24
+ "author": "Sahil Kumar Sinha",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/sahilcodes2002/vertex.git"
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "typecheck": "tsc --noEmit",
36
+ "clean": "rm -rf dist",
37
+ "prepublishOnly": "pnpm build"
38
+ },
39
+ "dependencies": {
40
+ "@oclif/core": "^4.11.0",
41
+ "@supabase/supabase-js": "^2.49.0",
42
+ "ws": "^8.18.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/ws": "^8.18.0",
46
+ "@vertex/core": "workspace:*",
47
+ "tsup": "^8.4.0",
48
+ "typescript": "^5.7.0",
49
+ "oclif": "^4.17.0"
50
+ },
51
+ "oclif": {
52
+ "bin": "vertex",
53
+ "dirname": "vertex",
54
+ "commands": "./dist/commands"
55
+ }
56
+ }