A note-taking app inspired by nb
1import * as frontMatter from "@std/front-matter";
2import { Notebook } from "./notebook.ts";
3
4export interface BaseEntry {
5 notebook: Notebook;
6 folders: string[];
7 filename: string;
8 title: string;
9 lastModified: Date;
10 pinned: boolean;
11}
12
13export interface TodoEntry extends BaseEntry {
14 type: "todo";
15 status: "open" | "closed";
16}
17
18export type GenericEntryType =
19 | "note"
20 | "directory"
21 | "bookmark"
22 | "image"
23 | "file";
24export interface GenericEntry extends BaseEntry {
25 type: GenericEntryType;
26}
27
28export type EntryType = GenericEntryType | "todo";
29export type Entry = TodoEntry | GenericEntry;
30
31export interface NoteContents {
32 // deno-lint-ignore no-explicit-any
33 attrs?: any;
34 body: string;
35}
36
37export function suffixFor(filename: string): string {
38 switch (inferredType(filename)) {
39 case "bookmark":
40 return ".bookmark.md";
41 case "todo":
42 return ".todo.md";
43 default:
44 return ".md";
45 }
46}
47
48export function inferredType(filename: string): EntryType {
49 if (filename.endsWith(".bookmark.md")) {
50 return "bookmark";
51 } else if (filename.endsWith(".todo.md")) {
52 return "todo";
53 } else if (
54 [".jpg", ".jpeg", ".png", ".gif"].some((ext) => filename.endsWith(ext))
55 ) {
56 return "image";
57 } else {
58 return "note";
59 }
60}
61
62export function getRawTitle(content: string): string | null {
63 if (frontMatter.test(content)) {
64 const extract = frontMatter.extractYaml(content);
65 const title = (extract.attrs as { title?: string }).title;
66 if (title != null) return title;
67 }
68 const [firstLine] = content.split("\n", 1);
69 if (firstLine.startsWith("# ")) {
70 return firstLine.slice(2);
71 }
72 return null;
73}
74
75export function getTitleFor(item: string, content: string): string {
76 return getRawTitle(content) ??
77 `${item} · "${content.split("\n", 1)[0]}"`;
78}
79
80export function iconForEntry(entry: Entry): string {
81 let icon = "";
82 if (entry.pinned) {
83 icon = "📌 ";
84 }
85 switch (entry.type) {
86 case "bookmark":
87 icon += "🔖 ";
88 break;
89 case "directory":
90 icon += "📂 ";
91 break;
92 case "todo":
93 icon += entry.status === "open" ? "✔️ " : "✅ ";
94 break;
95 case "note":
96 break;
97 case "image":
98 icon += "🖼️ ";
99 break;
100 case "file":
101 icon += "📄 ";
102 break;
103 default:
104 entry satisfies never;
105 }
106 return icon;
107}